FileDocCategorySizeDatePackage
ObexAuth.javaAPI DocphoneME MR2 API (J2ME)17846Wed May 02 18:00:32 BST 2007com.sun.kvem.jsr082.obex

ObexAuth

public class ObexAuth extends Object
Obex protocol authentication functinality.

Fields Summary
private static final boolean
DEBUG
Debug 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
intaddChallenge(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 booleancheckAuthResponse(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 booleancompare(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.ObexAuthcreateChallenge(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

return
timestamp value

        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 voidmakeDigest(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.ObexAuthparseAuthChallenge(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);
    
intprepareChallenge()
Prepare challenge before adding to packet.

return
length of challenge

        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 voidprint(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("");
        }
    
intreplyAuthChallenge(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;