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.

(Omit source code)

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;