ObexAuthpublic class ObexAuth extends Object Obex protocol authentication functinality. |
Fields Summary |
---|
private static final boolean | DEBUGDebug information, should be false for RR. | private static byte[] | column | String | realm | boolean | userID | boolean | access | byte[] | nonce | private static int | counter | private byte[] | realm_array | private int | challengeLength | private static byte[] | privateKey |
Constructors Summary |
---|
private ObexAuth(String realm, byte[] nonce, boolean userID, boolean access)
this.realm = realm;
this.nonce = nonce;
this.userID = userID;
this.access = access;
|
Methods Summary |
---|
int | addChallenge(byte[] packet, int offset)
int len = prepareChallenge();
packet[offset] = (byte)ObexPacketStream.HEADER_AUTH_CHALLENGE;
packet[offset+1] = (byte) (len >> 8);
packet[offset+2] = (byte) (len & 255);
packet[offset+3] = (byte) 0; // nonce tag (0x0)
packet[offset+4] = (byte) 16; // nonce len (16) (1 bytes)
nonce = makeNonce(); // 16 byte of nonce
if (DEBUG) {
print("addChallenge: nonce", nonce);
}
System.arraycopy(nonce, 0, packet, offset + 5, 16);
packet[offset+21] = (byte) 1; // options tag (0x1)
packet[offset+22] = (byte) 1; // options length (1)
packet[offset+23] = (byte) ((userID ? 1 : 0) + (access ? 0 : 2));
if (realm != null) {
int realm_len = realm_array.length;
packet[offset+24] = (byte) 2; // realm tag (0x2)
// realm length including encoding (1 byte)
packet[offset+25] = (byte) (realm_len + 1);
packet[offset+26] = (byte) 0xFF; // realm encoding UNICODE
System.arraycopy(realm_array, 0, packet, offset+27, realm_len);
}
return len;
| static boolean | checkAuthResponse(byte[] buffer, int packetOffset, int length, ObexPacketStream stream, java.util.Vector challenges)
if (DEBUG) {
System.out.println("ObexAuth.parseAuthResponse()");
}
// skiping header type and length
int offset = packetOffset + 3;
length += packetOffset;
byte[] digest = null;
byte[] username = null;
byte[] nonce = null;
// decoding data in buffer
while (offset < length) {
int tag = buffer[offset] & 0xFF;
int len = buffer[offset + 1] & 0xFF;
offset += 2;
switch (tag) {
case 0x0: // digest
if (DEBUG) {
System.out.println("got digest");
}
if (len != 16 || digest != null) {
throw new IOException("protocol error (1)");
}
digest = new byte[16];
System.arraycopy(buffer, offset, digest, 0, 16);
if (DEBUG) {
print("got response: digest", digest);
}
break;
case 0x1: // username
if (DEBUG) {
System.out.println("got username");
}
if (len > 20 || len == 0 || username != null) {
throw new IOException("protocol error (2)");
}
username = new byte[len];
System.arraycopy(buffer, offset, username, 0, len);
break;
case 0x2: // nonce
if (DEBUG) {
System.out.println("got nonce");
}
if (len != 16 || nonce != null) {
throw new IOException("protocol error (3)");
}
nonce = new byte[16];
System.arraycopy(buffer, offset, nonce, 0, 16);
if (DEBUG) {
print("got response: nonce", nonce);
}
break;
default:
if (DEBUG) {
System.out.println("unknown tag = " + tag);
}
}
offset += len;
}
if (offset != length) {
throw new IOException("protocol error (4)");
}
// check nonce and select auth object
ObexAuth auth = null;
if (nonce == null) {
if (DEBUG) {
System.out.println("no nonce received, using first auth");
}
if (challenges.size() == 0) {
return false;
}
auth = (ObexAuth) challenges.elementAt(0);
nonce = auth.nonce;
challenges.removeElementAt(0);
} else {
if (DEBUG) {
System.out.println("nonce provided, searching for auth");
System.out.println("challenges = " + challenges.size());
}
for (int i = 0; i < challenges.size(); i++) {
ObexAuth a = (ObexAuth) challenges.elementAt(i);
if (compare(nonce, a.nonce)) {
if (DEBUG) {
System.out.println("nonce is in " + i + " challenge");
}
auth = a;
challenges.removeElementAt(i);
break;
}
}
if (DEBUG) {
System.out.println("auth = " + auth);
}
if (auth == null)
return false;
}
// check username existance
if (auth.userID && username == null) {
if (DEBUG) {
System.out.println("need username!");
}
// NOTE: may be too strict
stream.onAuthenticationFailure(username);
return false;
}
// ask password from authenticator and check digest
try {
if (DEBUG) {
System.out.println("running onAuthenticationResponse()...");
}
byte[] password =
stream.authenticator.onAuthenticationResponse(username);
byte[] localDigest = new byte[16];
makeDigest(localDigest, 0, nonce, password);
if (DEBUG) {
System.out.println("digest created");
}
boolean res = compare(localDigest, digest);
if (res == false) {
if (DEBUG) {
System.out.println("Calling onAuthenticationFailure()..");
}
stream.onAuthenticationFailure(username);
}
return res;
} catch (Throwable t) {
// catch localDigest = null, crypto and user code exception
if (DEBUG) {
System.out.println("exception");
}
stream.onAuthenticationFailure(username);
return false;
}
| private static boolean | compare(byte[] src1, byte[] src2)
for (int i = 0; i < 16; i++) {
if (src1[i] != src2[i])
return false;
}
return true;
| static com.sun.kvem.jsr082.obex.ObexAuth | createChallenge(java.lang.String realm, boolean userID, boolean access)
return new ObexAuth(realm, null, userID, access);
| private static byte[] | createTimestamp()Creates timestamp.
No strict specification for timestamp generation in OBEX 1.2
long time = System.currentTimeMillis();
byte[] timestamp = new byte[9];
timestamp[0] = (byte)(time >> 56);
timestamp[1] = (byte)(time >> 48);
timestamp[2] = (byte)(time >> 40);
timestamp[3] = (byte)(time >> 32);
timestamp[4] = (byte)(time >> 24);
timestamp[5] = (byte)(time >> 16);
timestamp[6] = (byte)(time >> 8);
timestamp[7] = (byte)(time);
synchronized (ObexAuth.class) {
timestamp[8] = (byte)(counter++);
}
return timestamp;
| private static synchronized byte[] | getPrivateKey()Create and return private key.
Weak security scheme. Should be rewritten for more secure
implementation if used outside emulator.
if (privateKey != null) {
return privateKey;
}
SSLWrapper md5 = new SSLWrapper();
byte[] keyData = null;
try {
keyData = "timestamp = ".getBytes("ISO-8859-1");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
md5.update(keyData, 0, keyData.length);
byte[] timestamp = createTimestamp();
privateKey = new byte[16];
md5.doFinal(timestamp, 0, timestamp.length, privateKey, 0);
return privateKey;
| static void | makeDigest(byte[] buffer, int offset, byte[] nonce, byte[] password)
SSLWrapper md5 = new SSLWrapper();
md5.update(nonce, 0, 16);
md5.update(column, 0, 1);
md5.doFinal(password, 0, password.length, buffer, offset);
| private static byte[] | makeNonce()
SSLWrapper md5 = new SSLWrapper();
byte[] timestamp = createTimestamp();
md5.update(timestamp, 0, timestamp.length);
md5.update(column, 0, 1);
byte[] privateKey = getPrivateKey();
byte[] nonce = new byte[16];
md5.doFinal(privateKey, 0, privateKey.length, nonce, 0);
return nonce;
| static com.sun.kvem.jsr082.obex.ObexAuth | parseAuthChallenge(byte[] buffer, int packetOffset, int length)
if (DEBUG) {
System.out.println("ObexAuth.parseAuthChallenge()");
}
// default values
boolean readonly = false;
boolean needUserid = false;
byte[] nonce = null;
String realm = null;
// skiping header type and length
int offset = packetOffset + 3;
length += packetOffset;
// decoding data in buffer
while (offset < length) {
int tag = buffer[offset] & 0xFF;
int len = buffer[offset + 1] & 0xFF;
offset += 2;
switch (tag) {
case 0x0: // nonce
if (len != 16 || nonce != null) {
throw new IOException("protocol error");
}
nonce = new byte[16];
System.arraycopy(buffer, offset, nonce, 0, 16);
if (DEBUG) {
print("got challenge: nonce", nonce);
}
break;
case 0x1: // options
if (len != 1) {
throw new IOException("protocol error");
}
int options = buffer[offset];
readonly = ((options & 2) != 0);
needUserid = ((options & 1) != 0);
break;
case 0x2: // realm
try {
int encodingID = buffer[offset] & 0xFF;
String encoding = null;
if (encodingID == 255) encoding = "UTF-16BE";
else if (encodingID == 0) encoding = "US-ASCII";
else if (encodingID < 10) encoding = "ISO-8859-"
+ encoding;
else throw new UnsupportedEncodingException();
realm = new String(buffer, offset + 1,
len - 1, encoding);
} catch (UnsupportedEncodingException e) {
// already: realm = null;
}
}
offset += len;
}
if (offset != length) {
throw new IOException("protocol error");
}
return new ObexAuth(realm, nonce, needUserid, !readonly);
| int | prepareChallenge()Prepare challenge before adding to packet.
if (challengeLength != 0) {
return challengeLength;
}
try {
int len = 24;
realm_array = null;
if (realm != null) {
realm_array = realm.getBytes("UTF-16BE");
len += 3 + realm_array.length;
}
challengeLength = len;
} catch (UnsupportedEncodingException e) {
if (DEBUG) {
System.out.println("prepareChallenge(): ERROR, no encoding");
}
return 0;
}
return challengeLength;
| private static void | print(java.lang.String msg, byte[] array)
if (DEBUG) {
System.out.println(msg);
if (array == null) {
System.out.println("[0] = NULL");
return;
}
System.out.print("[" + array.length + "]");
for (int i = 0; i < array.length; i++) {
System.out.print(" " + Integer.toHexString(array[i] & 0xFF));
}
System.out.println("");
}
| int | replyAuthChallenge(byte[] buffer, int packetOffset, javax.obex.Authenticator authenticator)
if (DEBUG) {
System.out.println("ObexAuth.replyAuthChallenge()");
}
if (realm == null) {
realm = "";
}
byte[] password = null;
byte[] username = null;
try {
PasswordAuthentication pass =
authenticator.onAuthenticationChallenge(realm, userID, access);
password = pass.getPassword();
int uidLen = 0;
// userid subheader length with subheader <tag> and <len>
username = pass.getUserName();
if (userID || username != null) {
// username is required but not provided
if (userID && username.length == 0) {
if (DEBUG) {
System.out.println("ObexAuth.replyAuthChallenge():"
+ " required username not provided");
}
throw new Exception();
}
uidLen = 2 + username.length;
// maximum supported username length = 20
if (uidLen > 22) uidLen = 22;
}
int len = 39 + uidLen;
// byte[] response = new byte[len];
buffer[packetOffset + 0] = (byte)ObexPacketStream
.HEADER_AUTH_RESPONSE;
buffer[packetOffset + 1] = (byte) (len >> 8);
buffer[packetOffset + 2] = (byte) (len & 255);
buffer[packetOffset + 3] = 0x0; // tag (Request-Digest)
buffer[packetOffset + 4] = 16; // digest len (16)
makeDigest(buffer, packetOffset + 5, nonce, password);
buffer[packetOffset + 21] = 0x02; // tag nonce
buffer[packetOffset + 22] = 16; // nonce len (16)
System.arraycopy(nonce, 0, buffer, packetOffset + 23, 16);
if (DEBUG) {
print("send response: nonce", nonce);
}
if (uidLen > 2) {
buffer[packetOffset + 39] = 0x01; // tag userid
buffer[packetOffset + 40] = (byte) (uidLen - 2); // userid len
System.arraycopy(username, 0, buffer, packetOffset + 41,
uidLen - 2);
}
if (DEBUG) {
System.out.println("ObexAuth.replyAuthChallenge():"
+ " response generated");
}
return len;
// need to create authentication response
// we should resend previous packet with the authentication response
} catch (Throwable t) {
if (DEBUG) {
System.out.println("ObexAuth.replyAuthChallenge(): exception");
t.printStackTrace();
}
// will caught NullPointerException if authenticator
// was not provided
// will caught exceptions in handler
// will caught NullPointerException exception
// if username == null and needUserid
//
// wrong response from client application,
// ignoring authentication challenge
// client will receive UNAUTHORIZED
}
return 0;
|
|