diff options
author | Parménides GV <parmegv@sdf.org> | 2013-04-09 19:21:16 +0200 |
---|---|---|
committer | Parménides GV <parmegv@sdf.org> | 2013-04-09 19:21:16 +0200 |
commit | 1026d9779ff1cdca200cf60ad0de1292475d16f7 (patch) | |
tree | 2a619ebaff13ad984b4ff1c2764c853a2f32afc7 | |
parent | 47e489f521c1f69c357880234a8aa8b5408595aa (diff) |
Response should be correct, as far as different calculations
individually are. But in reality it's not.
Tried to fix final hash putting a trim in every byte array, but it did
not work.
Next step: check the final hash, looking for padding issues.
-rw-r--r-- | src/se/leap/leapclient/LeapSRPSession.java | 435 |
1 files changed, 219 insertions, 216 deletions
diff --git a/src/se/leap/leapclient/LeapSRPSession.java b/src/se/leap/leapclient/LeapSRPSession.java index e5860dd8..3dacce9c 100644 --- a/src/se/leap/leapclient/LeapSRPSession.java +++ b/src/se/leap/leapclient/LeapSRPSession.java @@ -12,244 +12,247 @@ import org.jboss.security.srp.SRPParameters; import org.jboss.security.srp.SRPPermission; public class LeapSRPSession { - + private SRPParameters params; - private BigInteger N; - private BigInteger g; - private BigInteger x; - private BigInteger v; - private byte[] s; - private BigInteger a; - private BigInteger A; - private byte[] K; - /** The M1 = H(H(N) xor H(g) | H(U) | s | A | B | K) hash */ - private MessageDigest clientHash; - /** The M2 = H(A | M | K) hash */ - private MessageDigest serverHash; - - private static int A_LEN; - - /** Creates a new SRP server session object from the username, password + private BigInteger N; + private BigInteger g; + private BigInteger x; + private BigInteger v; + private byte[] s; + private BigInteger a; + private BigInteger A; + private byte[] K; + /** The M1 = H(H(N) xor H(g) | H(U) | s | A | B | K) hash */ + private MessageDigest clientHash; + /** The M2 = H(A | M | K) hash */ + private MessageDigest serverHash; + + private static int A_LEN; + + /** Creates a new SRP server session object from the username, password verifier, @param username, the user ID @param password, the user clear text password @param params, the SRP parameters for the session - */ - public LeapSRPSession(String username, char[] password, SRPParameters params) - { - this(username, password, params, null); - } + */ + public LeapSRPSession(String username, char[] password, SRPParameters params) + { + this(username, password, params, null); + } - /** Creates a new SRP server session object from the username, password + /** Creates a new SRP server session object from the username, password verifier, @param username, the user ID @param password, the user clear text password @param params, the SRP parameters for the session @param abytes, the random exponent used in the A public key. This must be 8 bytes in length. - */ - public LeapSRPSession(String username, char[] password, SRPParameters params, - byte[] abytes) - { - try - { - // Initialize the secure random number and message digests - Util.init(); - } - catch(NoSuchAlgorithmException e) - { - } - this.params = params; - this.g = new BigInteger(1, params.g); - byte[] N_trimmed = Util.trim(params.N); - this.N = new BigInteger(1, N_trimmed); - if( abytes != null ) { - A_LEN = 8*abytes.length; - /* TODO Why did they put this condition? + */ + public LeapSRPSession(String username, char[] password, SRPParameters params, + byte[] abytes) + { + try + { + // Initialize the secure random number and message digests + Util.init(); + } + catch(NoSuchAlgorithmException e) + { + } + this.params = params; + this.g = new BigInteger(1, params.g); + byte[] N_bytes = Util.trim(params.N); + this.N = new BigInteger(1, N_bytes); + if( abytes != null ) { + A_LEN = 8*abytes.length; + /* TODO Why did they put this condition? if( 8*abytes.length != A_LEN ) throw new IllegalArgumentException("The abytes param must be " +(A_LEN/8)+" in length, abytes.length="+abytes.length); - */ - this.a = new BigInteger(abytes); - } - - // Calculate x = H(s | H(U | ':' | password)) - byte[] xb = calculatePasswordHash(username, password, params.s); - this.x = new BigInteger(1, xb); - - // Calculate v = kg^x mod N - BigInteger k = new BigInteger("bf66c44a428916cad64aa7c679f3fd897ad4c375e9bbb4cbf2f5de241d618ef0", 16); - this.v = k.multiply(g.modPow(x, N)); // g^x % N - - serverHash = newDigest(); - clientHash = newDigest(); - - // H(N) - byte[] hn = newDigest().digest(N_trimmed); - // H(g) - byte[] hg = newDigest().digest(params.g); - // clientHash = H(N) xor H(g) - byte[] hxg = xor(hn, hg, hg.length); - clientHash.update(hxg); - // clientHash = H(N) xor H(g) | H(U) - byte[] username_digest = newDigest().digest(username.getBytes()); - clientHash.update(username_digest); - // clientHash = H(N) xor H(g) | H(U) | s - clientHash.update(params.s); - K = null; - } - - /** - * @returns The exponential residue (parameter A) to be sent to the server. - */ - public byte[] exponential() { - byte[] Abytes = null; - if(A == null) { - /* If the random component of A has not been specified use a random - number */ - if( a == null ) { - BigInteger one = BigInteger.ONE; - do { - a = new BigInteger(A_LEN, Util.getPRNG()); - } while(a.compareTo(one) <= 0); - } - A = g.modPow(a, N); - //Abytes = Util.trim(A.toByteArray()); - Abytes = A.toByteArray(); - // clientHash = H(N) xor H(g) | H(U) | A - clientHash.update(Abytes); - // serverHash = A - serverHash.update(Abytes); - } - return Abytes; - } - - public byte[] response(byte[] Bbytes) throws NoSuchAlgorithmException { - // clientHash = H(N) xor H(g) | H(U) | s | A | B - clientHash.update(Util.trim(Bbytes)); - - /* - var B = new BigInteger(ephemeral, 16); - var Bstr = ephemeral; - // u = H(A,B) - var u = new BigInteger(SHA256(hex2a(Astr + Bstr)), 16); - // x = H(s, H(I:p)) - var x = this.calcX(salt); - //S = (B - kg^x) ^ (a + ux) - var kgx = k.multiply(g.modPow(x, N)); - var aux = a.add(u.multiply(x)); - S = B.subtract(kgx).modPow(aux, N); - K = SHA256(hex2a(S.toString(16))); - */ - byte[] ub = getU(A.toByteArray(), Bbytes); - // Calculate S = (B - kg^x) ^ (a + u * x) % N - BigInteger B = new BigInteger(1, Bbytes); - BigInteger u = new BigInteger(1, ub); - BigInteger B_v = B.subtract(v); - BigInteger a_ux = a.add(u.multiply(x)); - BigInteger S = B_v.modPow(a_ux, N); - // K = SessionHash(S) - MessageDigest sessionDigest = MessageDigest.getInstance(params.hashAlgorithm); - K = sessionDigest.digest(S.toByteArray()); - // clientHash = H(N) xor H(g) | H(U) | A | B | K - clientHash.update(K); - byte[] M1 = clientHash.digest(); - return M1; + */ + this.a = new BigInteger(abytes); } + // Calculate x = H(s | H(U | ':' | password)) + byte[] xb = calculatePasswordHash(username, password, params.s); + //xb = Util.trim(xb); + this.x = new BigInteger(1, xb); + + // Calculate v = kg^x mod N + this.v = calculateV(); - public byte[] getU(byte[] Abytes, byte[] Bbytes) { - MessageDigest u_digest = newDigest(); - u_digest.update(Abytes); - u_digest.update(Bbytes); - return new BigInteger(1, u_digest.digest()).toByteArray(); + serverHash = newDigest(); + clientHash = newDigest(); + + // H(N) + byte[] hn = newDigest().digest(N_bytes); + // H(g) + byte[] hg = newDigest().digest(params.g); + // clientHash = H(N) xor H(g) + byte[] hxg = xor(hn, hg, hg.length); + //hxg = Util.trim(hxg); + clientHash.update(hxg); + // clientHash = H(N) xor H(g) | H(U) + byte[] username_bytes = username.getBytes(); + byte[] username_digest = newDigest().digest(username_bytes); + clientHash.update(username_digest); + // clientHash = H(N) xor H(g) | H(U) | s + byte[] salt_bytes = params.s; + clientHash.update(salt_bytes); + K = null; + } + + private BigInteger calculateV() { + BigInteger k = new BigInteger("bf66c44a428916cad64aa7c679f3fd897ad4c375e9bbb4cbf2f5de241d618ef0", 16); + return k.multiply(g.modPow(x, N)); // g^x % N + } + + /** + * @returns The exponential residue (parameter A) to be sent to the server. + */ + public byte[] exponential() { + byte[] Abytes = null; + if(A == null) { + /* If the random component of A has not been specified use a random + number */ + if( a == null ) { + BigInteger one = BigInteger.ONE; + do { + a = new BigInteger(A_LEN, Util.getPRNG()); + } while(a.compareTo(one) <= 0); + } + A = g.modPow(a, N); + Abytes = A.toByteArray(); + //Abytes = Util.trim(Abytes); + // clientHash = H(N) xor H(g) | H(U) | A + clientHash.update(Abytes); + // serverHash = A + serverHash.update(Abytes); } + return Abytes; + } + + public byte[] response(byte[] Bbytes) throws NoSuchAlgorithmException { + // clientHash = H(N) xor H(g) | H(U) | s | A | B + //Bbytes = Util.trim(Bbytes); + clientHash.update(Bbytes); + // Calculate S = (B - kg^x) ^ (a + u * x) % N + BigInteger S = calculateS(Bbytes); + byte[] S_bytes = S.toByteArray(); + //S_bytes = Util.trim(S_bytes); + // K = SessionHash(S) + String hash_algorithm = params.hashAlgorithm; + MessageDigest sessionDigest = MessageDigest.getInstance(hash_algorithm); + K = sessionDigest.digest(S_bytes); + // clientHash = H(N) xor H(g) | H(U) | A | B | K + clientHash.update(K); + byte[] M1 = clientHash.digest(); + return M1; + } + + + private BigInteger calculateS(byte[] Bbytes) { + byte[] ub = getU(A.toByteArray(), Bbytes); + BigInteger B = new BigInteger(1, Bbytes); + BigInteger u = new BigInteger(1, ub); + BigInteger B_v = B.subtract(v); + BigInteger a_ux = a.add(u.multiply(x)); + BigInteger S = B_v.modPow(a_ux, N); + + return S; + } + + public byte[] getU(byte[] Abytes, byte[] Bbytes) { + MessageDigest u_digest = newDigest(); + u_digest.update(Util.trim(Abytes)); + u_digest.update(Util.trim(Bbytes)); + return new BigInteger(1, u_digest.digest()).toByteArray(); + } /** - * @param M2 The server's response to the client's challenge - * @returns True if and only if the server's response was correct. - */ - public boolean verify(byte[] M2) - { - // M2 = H(A | M1 | K) - byte[] myM2 = serverHash.digest(); - boolean valid = Arrays.equals(M2, myM2); - return valid; - } - - /** Returns the negotiated session K, K = SHA_Interleave(S) + * @param M2 The server's response to the client's challenge + * @returns True if and only if the server's response was correct. + */ + public boolean verify(byte[] M2) + { + // M2 = H(A | M1 | K) + byte[] myM2 = serverHash.digest(); + boolean valid = Arrays.equals(M2, myM2); + return valid; + } + + /** Returns the negotiated session K, K = SHA_Interleave(S) @return the private session K byte[] @throws SecurityException - if the current thread does not have an getSessionKey SRPPermission. - */ - public byte[] getSessionKey() throws SecurityException - { - SecurityManager sm = System.getSecurityManager(); - if( sm != null ) - { - SRPPermission p = new SRPPermission("getSessionKey"); - sm.checkPermission(p); - } - return K; - } - - public MessageDigest newDigest() - { - MessageDigest md = null; - try { - md = MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } - return md; - } - - public byte[] calculatePasswordHash(String username, char[] password, - byte[] salt) - { - // Calculate x = H(s | H(U | ':' | password)) - MessageDigest xd = newDigest(); - // Try to convert the username to a byte[] using UTF-8 - byte[] user = null; - byte[] colon = {}; - try { - user = username.getBytes("UTF-8"); - colon = ":".getBytes("UTF-8"); - } - catch(UnsupportedEncodingException e) { - // Use the default platform encoding - user = username.getBytes(); - colon = ":".getBytes(); - } - byte[] passBytes = new byte[2*password.length]; - int passBytesLength = 0; - for(int p = 0; p < password.length; p ++) { - int c = (password[p] & 0x00FFFF); - // The low byte of the char - byte b0 = (byte) (c & 0x0000FF); - // The high byte of the char - byte b1 = (byte) ((c & 0x00FF00) >> 8); - passBytes[passBytesLength ++] = b0; - // Only encode the high byte if c is a multi-byte char - if( c > 255 ) - passBytes[passBytesLength ++] = b1; - } - - // Build the hash - xd.update(user); - xd.update(colon); - xd.update(passBytes, 0, passBytesLength); - byte[] h = xd.digest(); - xd.reset(); - xd.update(salt); - xd.update(h); - byte[] xb = xd.digest(); - return xb; - } - - public byte[] xor(byte[] b1, byte[] b2, int length) - { - //TODO Check if length matters in the order, when b2 is smaller than b1 or viceversa - return new BigInteger(1, b1).xor(new BigInteger(1, b2)).toByteArray(); - } + */ + public byte[] getSessionKey() throws SecurityException + { + SecurityManager sm = System.getSecurityManager(); + if( sm != null ) + { + SRPPermission p = new SRPPermission("getSessionKey"); + sm.checkPermission(p); + } + return K; + } + + public MessageDigest newDigest() + { + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + return md; + } + + public byte[] calculatePasswordHash(String username, char[] password, byte[] salt) + { + // Calculate x = H(s | H(U | ':' | password)) + MessageDigest xd = newDigest(); + // Try to convert the username to a byte[] using UTF-8 + byte[] user = null; + byte[] colon = {}; + try { + user = username.getBytes("UTF-8"); + colon = ":".getBytes("UTF-8"); + } + catch(UnsupportedEncodingException e) { + // Use the default platform encoding + user = username.getBytes(); + colon = ":".getBytes(); + } + byte[] passBytes = new byte[2*password.length]; + int passBytesLength = 0; + for(int p = 0; p < password.length; p ++) { + int c = (password[p] & 0x00FFFF); + // The low byte of the char + byte b0 = (byte) (c & 0x0000FF); + // The high byte of the char + byte b1 = (byte) ((c & 0x00FF00) >> 8); + passBytes[passBytesLength ++] = b0; + // Only encode the high byte if c is a multi-byte char + if( c > 255 ) + passBytes[passBytesLength ++] = b1; + } + + // Build the hash + xd.update(user); + xd.update(colon); + xd.update(passBytes, 0, passBytesLength); + byte[] h = xd.digest(); + xd.reset(); + xd.update(salt); + xd.update(h); + byte[] xb = xd.digest(); + return xb; + } + + public byte[] xor(byte[] b1, byte[] b2, int length) + { + //TODO Check if length matters in the order, when b2 is smaller than b1 or viceversa + return new BigInteger(1, b1).xor(new BigInteger(1, b2)).toByteArray(); + } } |