From 3e9a68fcc6c16be69abfa27d5fd3a2cbfc620bb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Parm=C3=A9nides=20GV?= Date: Wed, 3 Apr 2013 19:34:40 +0200 Subject: Fixed bug #2146 => A calculation is now fine. Next step: fix M1 calculation, since right now (using tests) response() method is not doing OK. Added new SRPSession modifying response() method from JBoss SRP implementation. Added hosts-for-android-emulator. Use with the following commands to be able to test on api.lvh.me: adb shell mount -o rw,remount -t yaffs2 /dev/block/mtdblock3 /system adb push ~/workspace/leap_android/hosts-for-android-emulator /system/etc/hosts --- src/se/leap/leapclient/LeapSRPSession.java | 193 +++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 src/se/leap/leapclient/LeapSRPSession.java (limited to 'src/se/leap/leapclient/LeapSRPSession.java') diff --git a/src/se/leap/leapclient/LeapSRPSession.java b/src/se/leap/leapclient/LeapSRPSession.java new file mode 100644 index 0000000..1d1f0c9 --- /dev/null +++ b/src/se/leap/leapclient/LeapSRPSession.java @@ -0,0 +1,193 @@ +package se.leap.leapclient; + +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +import org.jboss.security.Util; +import org.jboss.security.srp.SRPClientSession; +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 + 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); + } + + /** 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); + this.N = new BigInteger(1, params.N); + 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 = Util.calculatePasswordHash(username, password, params.s); + this.x = new BigInteger(1, xb); + this.v = g.modPow(x, N); // g^x % N + + serverHash = Util.newDigest(); + clientHash = Util.newDigest(); + // H(N) + byte[] hn = Util.newDigest().digest(params.N); + // H(g) + byte[] hg = Util.newDigest().digest(params.g); + // clientHash = H(N) xor H(g) + byte[] hxg = Util.xor(hn, hg, 20); + clientHash.update(hxg); + // clientHash = H(N) xor H(g) | H(U) + clientHash.update(Util.newDigest().digest(username.getBytes())); + // 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()); + // 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(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[] hA = Util.newDigest().digest(A.toByteArray()); + MessageDigest u_digest = Util.newDigest(); + u_digest.update(A.toByteArray()); + u_digest.update(Bbytes); + clientHash.update(u_digest.digest()); + byte[] ub = new BigInteger(clientHash.digest()).toByteArray(); + // Calculate S = (B - g^x) ^ (a + u * x) % N + BigInteger B = new BigInteger(1, Bbytes); + if( B.compareTo(v) < 0 ) + B = B.add(N); + 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; + } + + + /** + * @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; + } + +} -- cgit v1.2.3 From bfee299e998143c801b231060fd5fdb5eb7204b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Parm=C3=A9nides=20GV?= Date: Mon, 8 Apr 2013 17:12:23 +0200 Subject: Xor method fixed. I use BigInteger Java one. Next step: understand why SHA-256 digest from NG_1024 is not equals to the one leap_web is calculating. --- src/se/leap/leapclient/LeapSRPSession.java | 126 +++++++++++++++++++++-------- 1 file changed, 93 insertions(+), 33 deletions(-) (limited to 'src/se/leap/leapclient/LeapSRPSession.java') diff --git a/src/se/leap/leapclient/LeapSRPSession.java b/src/se/leap/leapclient/LeapSRPSession.java index 1d1f0c9..f81e163 100644 --- a/src/se/leap/leapclient/LeapSRPSession.java +++ b/src/se/leap/leapclient/LeapSRPSession.java @@ -1,5 +1,6 @@ package se.leap.leapclient; +import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -61,33 +62,36 @@ public class LeapSRPSession { this.params = params; this.g = new BigInteger(1, params.g); this.N = new BigInteger(1, params.N); - if( abytes != null ) - { + 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); + */ + this.a = new BigInteger(abytes); } // Calculate x = H(s | H(U | ':' | password)) - byte[] xb = Util.calculatePasswordHash(username, password, params.s); + byte[] xb = calculatePasswordHash(username, password, params.s); this.x = new BigInteger(1, xb); - this.v = g.modPow(x, N); // g^x % N - serverHash = Util.newDigest(); - clientHash = Util.newDigest(); + // 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 = Util.newDigest().digest(params.N); + byte[] hn = newDigest().digest(params.N); // H(g) - byte[] hg = Util.newDigest().digest(params.g); + byte[] hg = newDigest().digest(params.g); // clientHash = H(N) xor H(g) - byte[] hxg = Util.xor(hn, hg, 20); + byte[] hxg = xor(hn, hg, hg.length); clientHash.update(hxg); // clientHash = H(N) xor H(g) | H(U) - clientHash.update(Util.newDigest().digest(username.getBytes())); + clientHash.update(newDigest().digest(username.getBytes())); // clientHash = H(N) xor H(g) | H(U) | s clientHash.update(params.s); K = null; @@ -96,23 +100,20 @@ public class LeapSRPSession { /** * @returns The exponential residue (parameter A) to be sent to the server. */ - public byte[] exponential() - { + public byte[] exponential() { byte[] Abytes = null; - if(A == null) - { + if(A == null) { /* If the random component of A has not been specified use a random number */ - if( a == null ) - { + if( a == null ) { BigInteger one = BigInteger.ONE; - do - { + do { a = new BigInteger(A_LEN, Util.getPRNG()); } while(a.compareTo(one) <= 0); } A = g.modPow(a, N); - Abytes = Util.trim(A.toByteArray()); + //Abytes = Util.trim(A.toByteArray()); + Abytes = A.toByteArray(); // clientHash = H(N) xor H(g) | H(U) | A clientHash.update(Abytes); // serverHash = A @@ -138,16 +139,9 @@ public class LeapSRPSession { S = B.subtract(kgx).modPow(aux, N); K = SHA256(hex2a(S.toString(16))); */ - byte[] hA = Util.newDigest().digest(A.toByteArray()); - MessageDigest u_digest = Util.newDigest(); - u_digest.update(A.toByteArray()); - u_digest.update(Bbytes); - clientHash.update(u_digest.digest()); - byte[] ub = new BigInteger(clientHash.digest()).toByteArray(); - // Calculate S = (B - g^x) ^ (a + u * x) % N + byte[] ub = getU(A.toByteArray(), Bbytes); + // Calculate S = (B - kg^x) ^ (a + u * x) % N BigInteger B = new BigInteger(1, Bbytes); - if( B.compareTo(v) < 0 ) - B = B.add(N); BigInteger u = new BigInteger(1, ub); BigInteger B_v = B.subtract(v); BigInteger a_ux = a.add(u.multiply(x)); @@ -160,9 +154,16 @@ public class LeapSRPSession { byte[] M1 = clientHash.digest(); return M1; } - - - /** + + + public byte[] getU(byte[] Abytes, byte[] Bbytes) { + MessageDigest u_digest = Util.newDigest(); + u_digest.update(Abytes); + u_digest.update(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. */ @@ -190,4 +191,63 @@ public class LeapSRPSession { return K; } + public MessageDigest newDigest() + { + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA256"); + } 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(b1).xor(new BigInteger(b2)).toByteArray(); + } } -- cgit v1.2.3 From 47e489f521c1f69c357880234a8aa8b5408595aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Parm=C3=A9nides=20GV?= Date: Mon, 8 Apr 2013 20:48:17 +0200 Subject: Done constructor of LeapSRPSession: it's OK. Next step: fix response() calculations. --- src/se/leap/leapclient/LeapSRPSession.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'src/se/leap/leapclient/LeapSRPSession.java') diff --git a/src/se/leap/leapclient/LeapSRPSession.java b/src/se/leap/leapclient/LeapSRPSession.java index f81e163..e5860dd 100644 --- a/src/se/leap/leapclient/LeapSRPSession.java +++ b/src/se/leap/leapclient/LeapSRPSession.java @@ -61,7 +61,8 @@ public class LeapSRPSession { } this.params = params; this.g = new BigInteger(1, params.g); - this.N = new BigInteger(1, params.N); + 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? @@ -84,14 +85,15 @@ public class LeapSRPSession { clientHash = newDigest(); // H(N) - byte[] hn = newDigest().digest(params.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) - clientHash.update(newDigest().digest(username.getBytes())); + 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; @@ -124,7 +126,7 @@ public class LeapSRPSession { public byte[] response(byte[] Bbytes) throws NoSuchAlgorithmException { // clientHash = H(N) xor H(g) | H(U) | s | A | B - clientHash.update(Bbytes); + clientHash.update(Util.trim(Bbytes)); /* var B = new BigInteger(ephemeral, 16); @@ -157,7 +159,7 @@ public class LeapSRPSession { public byte[] getU(byte[] Abytes, byte[] Bbytes) { - MessageDigest u_digest = Util.newDigest(); + MessageDigest u_digest = newDigest(); u_digest.update(Abytes); u_digest.update(Bbytes); return new BigInteger(1, u_digest.digest()).toByteArray(); @@ -195,7 +197,7 @@ public class LeapSRPSession { { MessageDigest md = null; try { - md = MessageDigest.getInstance("SHA256"); + md = MessageDigest.getInstance("SHA-256"); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } @@ -248,6 +250,6 @@ public class LeapSRPSession { 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(b1).xor(new BigInteger(b2)).toByteArray(); + return new BigInteger(1, b1).xor(new BigInteger(1, b2)).toByteArray(); } } -- cgit v1.2.3 From 1026d9779ff1cdca200cf60ad0de1292475d16f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Parm=C3=A9nides=20GV?= Date: Tue, 9 Apr 2013 19:21:16 +0200 Subject: 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. --- src/se/leap/leapclient/LeapSRPSession.java | 435 +++++++++++++++-------------- 1 file changed, 219 insertions(+), 216 deletions(-) (limited to 'src/se/leap/leapclient/LeapSRPSession.java') diff --git a/src/se/leap/leapclient/LeapSRPSession.java b/src/se/leap/leapclient/LeapSRPSession.java index e5860dd..3dacce9 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(); + } } -- cgit v1.2.3 From 90ff9cc39faeb5a85ce56175dd64eaec6e4004c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Parm=C3=A9nides=20GV?= Date: Wed, 10 Apr 2013 18:24:50 +0200 Subject: LeapSRPSession response() method is working for the three different tests I've written for it. Next step: verify() --- src/se/leap/leapclient/LeapSRPSession.java | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) (limited to 'src/se/leap/leapclient/LeapSRPSession.java') diff --git a/src/se/leap/leapclient/LeapSRPSession.java b/src/se/leap/leapclient/LeapSRPSession.java index 3dacce9..c0d2d0f 100644 --- a/src/se/leap/leapclient/LeapSRPSession.java +++ b/src/se/leap/leapclient/LeapSRPSession.java @@ -90,15 +90,18 @@ public class LeapSRPSession { 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); + String hxg_string = new BigInteger(1, hxg).toString(16); + hxg = Util.trim(hxg); + clientHash.update(hxg); // OK // clientHash = H(N) xor H(g) | H(U) byte[] username_bytes = username.getBytes(); byte[] username_digest = newDigest().digest(username_bytes); - clientHash.update(username_digest); + String username_digest_string = new BigInteger(1, username_digest).toString(16); + clientHash.update(username_digest); // OK // clientHash = H(N) xor H(g) | H(U) | s byte[] salt_bytes = params.s; - clientHash.update(salt_bytes); + String salt_string = new BigInteger(1, salt_bytes).toString(16); + clientHash.update(salt_bytes); // OK K = null; } @@ -123,9 +126,10 @@ public class LeapSRPSession { } A = g.modPow(a, N); Abytes = A.toByteArray(); - //Abytes = Util.trim(Abytes); + String Abytes_string = new BigInteger(1, Abytes).toString(16); + Abytes = Util.trim(Abytes); // clientHash = H(N) xor H(g) | H(U) | A - clientHash.update(Abytes); + clientHash.update(Abytes); // Begins with 0: second case // serverHash = A serverHash.update(Abytes); } @@ -134,16 +138,21 @@ public class LeapSRPSession { 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); + Bbytes = Util.trim(Bbytes); // Begins with 0: first case, second case + String Abytes_string = new BigInteger(1, Bbytes).toString(16); + clientHash.update(Bbytes); // OK // Calculate S = (B - kg^x) ^ (a + u * x) % N BigInteger S = calculateS(Bbytes); byte[] S_bytes = S.toByteArray(); - //S_bytes = Util.trim(S_bytes); + S_bytes = Util.trim(S_bytes); // Begins with 0: first case + String S_bytes_string = new BigInteger(1, S_bytes).toString(16); // K = SessionHash(S) String hash_algorithm = params.hashAlgorithm; MessageDigest sessionDigest = MessageDigest.getInstance(hash_algorithm); K = sessionDigest.digest(S_bytes); + //K = Util.trim(K); + String K_bytes_string = new BigInteger(1, K).toString(16); + // clientHash = H(N) xor H(g) | H(U) | A | B | K clientHash.update(K); byte[] M1 = clientHash.digest(); -- cgit v1.2.3 From 3140e730378a0a506224d8cb8e1678647238a425 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Parm=C3=A9nides=20GV?= Date: Wed, 10 Apr 2013 21:43:56 +0200 Subject: Some more trims added. It passes a lot of tests from the test project (not included here, still to decide if push it publicly). Next steps: make code beautiful, Android GUI SRP and real communication server, and add even more tests (in my spare time, just to check with more users). --- src/se/leap/leapclient/LeapSRPSession.java | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) (limited to 'src/se/leap/leapclient/LeapSRPSession.java') diff --git a/src/se/leap/leapclient/LeapSRPSession.java b/src/se/leap/leapclient/LeapSRPSession.java index c0d2d0f..cecceed 100644 --- a/src/se/leap/leapclient/LeapSRPSession.java +++ b/src/se/leap/leapclient/LeapSRPSession.java @@ -63,6 +63,7 @@ public class LeapSRPSession { 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? @@ -74,12 +75,15 @@ public class LeapSRPSession { } // Calculate x = H(s | H(U | ':' | password)) - byte[] xb = calculatePasswordHash(username, password, params.s); - //xb = Util.trim(xb); + byte[] salt_bytes = params.s; + salt_bytes = Util.trim(salt_bytes); + byte[] xb = calculatePasswordHash(username, password, salt_bytes); + xb = Util.trim(xb); this.x = new BigInteger(1, xb); // Calculate v = kg^x mod N this.v = calculateV(); + String v_string = v.toString(16); serverHash = newDigest(); clientHash = newDigest(); @@ -95,11 +99,12 @@ public class LeapSRPSession { clientHash.update(hxg); // OK // clientHash = H(N) xor H(g) | H(U) byte[] username_bytes = username.getBytes(); + username_bytes = Util.trim(username_bytes); byte[] username_digest = newDigest().digest(username_bytes); + username_digest = Util.trim(username_digest); String username_digest_string = new BigInteger(1, username_digest).toString(16); clientHash.update(username_digest); // OK // clientHash = H(N) xor H(g) | H(U) | s - byte[] salt_bytes = params.s; String salt_string = new BigInteger(1, salt_bytes).toString(16); clientHash.update(salt_bytes); // OK K = null; @@ -139,7 +144,7 @@ public class LeapSRPSession { public byte[] response(byte[] Bbytes) throws NoSuchAlgorithmException { // clientHash = H(N) xor H(g) | H(U) | s | A | B Bbytes = Util.trim(Bbytes); // Begins with 0: first case, second case - String Abytes_string = new BigInteger(1, Bbytes).toString(16); + String Bbytes_string = new BigInteger(1, Bbytes).toString(16); clientHash.update(Bbytes); // OK // Calculate S = (B - kg^x) ^ (a + u * x) % N BigInteger S = calculateS(Bbytes); @@ -156,16 +161,24 @@ public class LeapSRPSession { // clientHash = H(N) xor H(g) | H(U) | A | B | K clientHash.update(K); byte[] M1 = clientHash.digest(); + // serverHash = Astr + M + K + serverHash.update(M1); + serverHash.update(K); return M1; } private BigInteger calculateS(byte[] Bbytes) { - byte[] ub = getU(A.toByteArray(), Bbytes); + byte[] Abytes = Util.trim(A.toByteArray()); + byte[] ub = getU(Abytes, Bbytes); + //ub = Util.trim(ub); + BigInteger B = new BigInteger(1, Bbytes); BigInteger u = new BigInteger(1, ub); + String u_string = u.toString(16); BigInteger B_v = B.subtract(v); BigInteger a_ux = a.add(u.multiply(x)); + String a_ux_string = a_ux.toString(16); BigInteger S = B_v.modPow(a_ux, N); return S; @@ -173,8 +186,8 @@ public class LeapSRPSession { public byte[] getU(byte[] Abytes, byte[] Bbytes) { MessageDigest u_digest = newDigest(); - u_digest.update(Util.trim(Abytes)); - u_digest.update(Util.trim(Bbytes)); + u_digest.update(Abytes); + u_digest.update(Bbytes); return new BigInteger(1, u_digest.digest()).toByteArray(); } @@ -233,6 +246,8 @@ public class LeapSRPSession { user = username.getBytes(); colon = ":".getBytes(); } + user = Util.trim(user); + colon = Util.trim(colon); byte[] passBytes = new byte[2*password.length]; int passBytesLength = 0; for(int p = 0; p < password.length; p ++) { @@ -252,6 +267,7 @@ public class LeapSRPSession { xd.update(colon); xd.update(passBytes, 0, passBytesLength); byte[] h = xd.digest(); + //h = Util.trim(h); xd.reset(); xd.update(salt); xd.update(h); -- cgit v1.2.3 From 60e5f181c1a9f4f1851aafd059971ba5d05748f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Parm=C3=A9nides=20GV?= Date: Thu, 11 Apr 2013 18:15:19 +0200 Subject: Made LeapSRPSession more beautiful, put javadoc and commented lines of strings used to check everything's fine manually. --- src/se/leap/leapclient/LeapSRPSession.java | 236 ++++++++++++++++------------- 1 file changed, 134 insertions(+), 102 deletions(-) (limited to 'src/se/leap/leapclient/LeapSRPSession.java') diff --git a/src/se/leap/leapclient/LeapSRPSession.java b/src/se/leap/leapclient/LeapSRPSession.java index cecceed..abdf6b2 100644 --- a/src/se/leap/leapclient/LeapSRPSession.java +++ b/src/se/leap/leapclient/LeapSRPSession.java @@ -18,7 +18,6 @@ public class LeapSRPSession { private BigInteger g; private BigInteger x; private BigInteger v; - private byte[] s; private BigInteger a; private BigInteger A; private byte[] K; @@ -49,16 +48,14 @@ public class LeapSRPSession { 8 bytes in length. */ public LeapSRPSession(String username, char[] password, SRPParameters params, - byte[] abytes) - { - try - { + byte[] abytes) { + try { // Initialize the secure random number and message digests Util.init(); } - catch(NoSuchAlgorithmException e) - { + catch(NoSuchAlgorithmException e) { } + this.params = params; this.g = new BigInteger(1, params.g); byte[] N_bytes = Util.trim(params.N); @@ -75,46 +72,113 @@ public class LeapSRPSession { } // Calculate x = H(s | H(U | ':' | password)) - byte[] salt_bytes = params.s; - salt_bytes = Util.trim(salt_bytes); + byte[] salt_bytes = Util.trim(params.s); byte[] xb = calculatePasswordHash(username, password, salt_bytes); - xb = Util.trim(xb); this.x = new BigInteger(1, xb); // Calculate v = kg^x mod N - this.v = calculateV(); - String v_string = v.toString(16); + String k_string = "bf66c44a428916cad64aa7c679f3fd897ad4c375e9bbb4cbf2f5de241d618ef0"; + this.v = calculateV(k_string); + //String v_string = v.toString(16); serverHash = newDigest(); clientHash = newDigest(); // H(N) - byte[] hn = newDigest().digest(N_bytes); + byte[] digest_of_n = newDigest().digest(N_bytes); + // H(g) - byte[] hg = newDigest().digest(params.g); + byte[] digest_of_g = newDigest().digest(params.g); + // clientHash = H(N) xor H(g) - byte[] hxg = xor(hn, hg, hg.length); - String hxg_string = new BigInteger(1, hxg).toString(16); - hxg = Util.trim(hxg); - clientHash.update(hxg); // OK + byte[] xor_digest = xor(digest_of_n, digest_of_g, digest_of_g.length); + //String hxg_string = new BigInteger(1, xor_digest).toString(16); + clientHash.update(xor_digest); + // clientHash = H(N) xor H(g) | H(U) - byte[] username_bytes = username.getBytes(); - username_bytes = Util.trim(username_bytes); - byte[] username_digest = newDigest().digest(username_bytes); + byte[] username_digest = newDigest().digest(Util.trim(username.getBytes())); username_digest = Util.trim(username_digest); - String username_digest_string = new BigInteger(1, username_digest).toString(16); - clientHash.update(username_digest); // OK + //String username_digest_string = new BigInteger(1, username_digest).toString(16); + clientHash.update(username_digest); + // clientHash = H(N) xor H(g) | H(U) | s - String salt_string = new BigInteger(1, salt_bytes).toString(16); - clientHash.update(salt_bytes); // OK + //String salt_string = new BigInteger(1, salt_bytes).toString(16); + clientHash.update(salt_bytes); + K = null; } - private BigInteger calculateV() { - BigInteger k = new BigInteger("bf66c44a428916cad64aa7c679f3fd897ad4c375e9bbb4cbf2f5de241d618ef0", 16); + /** + * Calculates the parameter x of the SRP-6a algorithm. + * @param username + * @param password + * @param salt the salt of the user + * @return x + */ + public byte[] calculatePasswordHash(String username, char[] password, byte[] salt) + { + // Calculate x = H(s | H(U | ':' | password)) + MessageDigest x_digest = newDigest(); + + // Try to convert the username to a byte[] using UTF-8 + byte[] user = null; + byte[] colon = {}; + try { + user = Util.trim(username.getBytes("UTF-8")); + colon = Util.trim(":".getBytes("UTF-8")); + } + catch(UnsupportedEncodingException e) { + // Use the default platform encoding + user = Util.trim(username.getBytes()); + colon = Util.trim(":".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 + x_digest.update(user); + x_digest.update(colon); + x_digest.update(passBytes, 0, passBytesLength); + byte[] h = x_digest.digest(); + //h = Util.trim(h); + + x_digest.reset(); + x_digest.update(salt); + x_digest.update(h); + byte[] x_digest_bytes = Util.trim(x_digest.digest()); + + return x_digest_bytes; + } + + /** + * Calculates the parameter V of the SRP-6a algorithm. + * @param k_string constant k predefined by the SRP server implementation. + * @return the value of V + */ + private BigInteger calculateV(String k_string) { + BigInteger k = new BigInteger(k_string, 16); return k.multiply(g.modPow(x, N)); // g^x % N } + 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 + byte[] xor_digest = new BigInteger(1, b1).xor(new BigInteger(1, b2)).toByteArray(); + return Util.trim(xor_digest); + } + /** * @returns The exponential residue (parameter A) to be sent to the server. */ @@ -130,60 +194,81 @@ public class LeapSRPSession { } while(a.compareTo(one) <= 0); } A = g.modPow(a, N); - Abytes = A.toByteArray(); - String Abytes_string = new BigInteger(1, Abytes).toString(16); - Abytes = Util.trim(Abytes); + Abytes = Util.trim(A.toByteArray()); + //String Abytes_string = new BigInteger(1, Abytes).toString(16); + // clientHash = H(N) xor H(g) | H(U) | A - clientHash.update(Abytes); // Begins with 0: second case + clientHash.update(Abytes); + // serverHash = A serverHash.update(Abytes); } return Abytes; } + /** + * Calculates the parameter M1, to be sent to the SRP server. + * It also updates hashes of client and server for further calculations in other methods. + * @param Bbytes the parameter received from the server, in bytes + * @return the parameter M1 + * @throws NoSuchAlgorithmException + */ public byte[] response(byte[] Bbytes) throws NoSuchAlgorithmException { // clientHash = H(N) xor H(g) | H(U) | s | A | B - Bbytes = Util.trim(Bbytes); // Begins with 0: first case, second case - String Bbytes_string = new BigInteger(1, Bbytes).toString(16); - clientHash.update(Bbytes); // OK + Bbytes = Util.trim(Bbytes); + //String Bbytes_string = new BigInteger(1, Bbytes).toString(16); + 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); // Begins with 0: first case - String S_bytes_string = new BigInteger(1, S_bytes).toString(16); + byte[] S_bytes = Util.trim(S.toByteArray()); + //String S_bytes_string = new BigInteger(1, S_bytes).toString(16); + // K = SessionHash(S) String hash_algorithm = params.hashAlgorithm; MessageDigest sessionDigest = MessageDigest.getInstance(hash_algorithm); K = sessionDigest.digest(S_bytes); //K = Util.trim(K); - String K_bytes_string = new BigInteger(1, K).toString(16); + //String K_bytes_string = new BigInteger(1, K).toString(16); // clientHash = H(N) xor H(g) | H(U) | A | B | K clientHash.update(K); byte[] M1 = clientHash.digest(); + // serverHash = Astr + M + K serverHash.update(M1); serverHash.update(K); return M1; } - + /** + * It calculates the parameter S used by response() to obtain session hash K. + * @param Bbytes the parameter received from the server, in bytes + * @return the parameter S + */ private BigInteger calculateS(byte[] Bbytes) { byte[] Abytes = Util.trim(A.toByteArray()); - byte[] ub = getU(Abytes, Bbytes); + byte[] u_bytes = getU(Abytes, Bbytes); //ub = Util.trim(ub); BigInteger B = new BigInteger(1, Bbytes); - BigInteger u = new BigInteger(1, ub); - String u_string = u.toString(16); - BigInteger B_v = B.subtract(v); + BigInteger u = new BigInteger(1, u_bytes); + //String u_string = u.toString(16); + + BigInteger B_minus_v = B.subtract(v); BigInteger a_ux = a.add(u.multiply(x)); - String a_ux_string = a_ux.toString(16); - BigInteger S = B_v.modPow(a_ux, N); + //String a_ux_string = a_ux.toString(16); + BigInteger S = B_minus_v.modPow(a_ux, N); return S; } + /** + * It calculates the parameter u used by calculateS to obtain S. + * @param Abytes the exponential residue sent to the server + * @param Bbytes the parameter received from the server, in bytes + * @return + */ public byte[] getU(byte[] Abytes, byte[] Bbytes) { MessageDigest u_digest = newDigest(); u_digest.update(Abytes); @@ -210,15 +295,13 @@ public class LeapSRPSession { */ public byte[] getSessionKey() throws SecurityException { - SecurityManager sm = System.getSecurityManager(); - if( sm != null ) - { - SRPPermission p = new SRPPermission("getSessionKey"); - sm.checkPermission(p); - } return K; } + + /** + * @return a new SHA-256 digest. + */ public MessageDigest newDigest() { MessageDigest md = null; @@ -229,55 +312,4 @@ public class LeapSRPSession { } 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(); - } - user = Util.trim(user); - colon = Util.trim(colon); - 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(); - //h = Util.trim(h); - 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(); - } } -- cgit v1.2.3 From 8e47afc7f4f85b80d59d253378681cb85ec54d5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Parm=C3=A9nides=20GV?= Date: Tue, 16 Apr 2013 20:12:13 +0200 Subject: Made SRP working with ProviderAPI methods more frequently than not in localhost, but I cannot succeed in api.bitmask.net with my personal account. Next step: add tests from api.bitmask.net. --- src/se/leap/leapclient/LeapSRPSession.java | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/se/leap/leapclient/LeapSRPSession.java') diff --git a/src/se/leap/leapclient/LeapSRPSession.java b/src/se/leap/leapclient/LeapSRPSession.java index abdf6b2..6fc8b2b 100644 --- a/src/se/leap/leapclient/LeapSRPSession.java +++ b/src/se/leap/leapclient/LeapSRPSession.java @@ -70,6 +70,8 @@ public class LeapSRPSession { */ this.a = new BigInteger(abytes); } + else + A_LEN = 64; // Calculate x = H(s | H(U | ':' | password)) byte[] salt_bytes = Util.trim(params.s); -- cgit v1.2.3 From f9b9827ec1975cb01e83826f0ad77542e514b21f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Parm=C3=A9nides=20GV?= Date: Wed, 17 Apr 2013 21:17:22 +0200 Subject: This commit contains: - SRP algorithm improved (validate method uses trim, and some other trims have been added). - Refactored calculatePasswordHash, so that it receives a String instead of a char array, and now it is capable of escaping "\" correctly. - A 1000*2 successful logins, with a new test that performs 1000 trials for 2 different username/password/server trios. Next step: think about how the user is going to trigger the log in fragment. --- src/se/leap/leapclient/LeapSRPSession.java | 36 +++++++++++++++++------------- 1 file changed, 20 insertions(+), 16 deletions(-) (limited to 'src/se/leap/leapclient/LeapSRPSession.java') diff --git a/src/se/leap/leapclient/LeapSRPSession.java b/src/se/leap/leapclient/LeapSRPSession.java index 6fc8b2b..d266cd7 100644 --- a/src/se/leap/leapclient/LeapSRPSession.java +++ b/src/se/leap/leapclient/LeapSRPSession.java @@ -34,7 +34,7 @@ public class LeapSRPSession { @param password, the user clear text password @param params, the SRP parameters for the session */ - public LeapSRPSession(String username, char[] password, SRPParameters params) + public LeapSRPSession(String username, String password, SRPParameters params) { this(username, password, params, null); } @@ -47,7 +47,7 @@ public class LeapSRPSession { @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, + public LeapSRPSession(String username, String password, SRPParameters params, byte[] abytes) { try { // Initialize the secure random number and message digests @@ -117,11 +117,11 @@ public class LeapSRPSession { * @param salt the salt of the user * @return x */ - public byte[] calculatePasswordHash(String username, char[] password, byte[] salt) + public byte[] calculatePasswordHash(String username, String password, byte[] salt) { + password = password.replaceAll("\\\\", "\\\\\\\\"); // Calculate x = H(s | H(U | ':' | password)) MessageDigest x_digest = newDigest(); - // Try to convert the username to a byte[] using UTF-8 byte[] user = null; byte[] colon = {}; @@ -135,10 +135,10 @@ public class LeapSRPSession { colon = Util.trim(":".getBytes()); } - byte[] passBytes = new byte[2*password.length]; + byte[] passBytes = new byte[2*password.toCharArray().length]; int passBytesLength = 0; - for(int p = 0; p < password.length; p++) { - int c = (password[p] & 0x00FFFF); + for(int p = 0; p < password.toCharArray().length; p++) { + int c = (password.toCharArray()[p] & 0x00FFFF); // The low byte of the char byte b0 = (byte) (c & 0x0000FF); // The high byte of the char @@ -159,7 +159,7 @@ public class LeapSRPSession { x_digest.reset(); x_digest.update(salt); x_digest.update(h); - byte[] x_digest_bytes = Util.trim(x_digest.digest()); + byte[] x_digest_bytes = x_digest.digest(); return x_digest_bytes; } @@ -171,7 +171,8 @@ public class LeapSRPSession { */ private BigInteger calculateV(String k_string) { BigInteger k = new BigInteger(k_string, 16); - return k.multiply(g.modPow(x, N)); // g^x % N + BigInteger v = k.multiply(g.modPow(x, N)); // g^x % N + return v; } public byte[] xor(byte[] b1, byte[] b2, int length) @@ -229,13 +230,13 @@ public class LeapSRPSession { // K = SessionHash(S) String hash_algorithm = params.hashAlgorithm; MessageDigest sessionDigest = MessageDigest.getInstance(hash_algorithm); - K = sessionDigest.digest(S_bytes); + K = Util.trim(sessionDigest.digest(S_bytes)); //K = Util.trim(K); //String K_bytes_string = new BigInteger(1, K).toString(16); // clientHash = H(N) xor H(g) | H(U) | A | B | K clientHash.update(K); - byte[] M1 = clientHash.digest(); + byte[] M1 = Util.trim(clientHash.digest()); // serverHash = Astr + M + K serverHash.update(M1); @@ -250,10 +251,11 @@ public class LeapSRPSession { */ private BigInteger calculateS(byte[] Bbytes) { byte[] Abytes = Util.trim(A.toByteArray()); + Bbytes = Util.trim(Bbytes); byte[] u_bytes = getU(Abytes, Bbytes); - //ub = Util.trim(ub); BigInteger B = new BigInteger(1, Bbytes); + //String Bstring = B.toString(16); BigInteger u = new BigInteger(1, u_bytes); //String u_string = u.toString(16); @@ -273,9 +275,10 @@ public class LeapSRPSession { */ 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(); + u_digest.update(Util.trim(Abytes)); + u_digest.update(Util.trim(Bbytes)); + byte[] u_digest_bytes = u_digest.digest();//Util.trim(u_digest.digest()); + return Util.trim(new BigInteger(1, u_digest_bytes).toByteArray()); } /** @@ -285,7 +288,8 @@ public class LeapSRPSession { public boolean verify(byte[] M2) { // M2 = H(A | M1 | K) - byte[] myM2 = serverHash.digest(); + M2 = Util.trim(M2); + byte[] myM2 = Util.trim(serverHash.digest()); boolean valid = Arrays.equals(M2, myM2); return valid; } -- cgit v1.2.3 From 09493a9dec5fb235d5c4914eb8f8142e3312e246 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Parm=C3=A9nides=20GV?= Date: Mon, 29 Apr 2013 21:32:10 +0200 Subject: Changed the message shown when checking if the password is valid or not. Refactored LeapSRPSession so that there is no need to send A twice. --- src/se/leap/leapclient/LeapSRPSession.java | 94 ++++++++++++++++-------------- 1 file changed, 51 insertions(+), 43 deletions(-) (limited to 'src/se/leap/leapclient/LeapSRPSession.java') diff --git a/src/se/leap/leapclient/LeapSRPSession.java b/src/se/leap/leapclient/LeapSRPSession.java index d266cd7..f9037de 100644 --- a/src/se/leap/leapclient/LeapSRPSession.java +++ b/src/se/leap/leapclient/LeapSRPSession.java @@ -14,7 +14,10 @@ import org.jboss.security.srp.SRPPermission; public class LeapSRPSession { private SRPParameters params; + private String username; + private String password; private BigInteger N; + private byte[] N_bytes; private BigInteger g; private BigInteger x; private BigInteger v; @@ -58,9 +61,11 @@ public class LeapSRPSession { this.params = params; this.g = new BigInteger(1, params.g); - byte[] N_bytes = Util.trim(params.N); + N_bytes = Util.trim(params.N); this.N = new BigInteger(1, N_bytes); - + this.username = username; + this.password = password; + if( abytes != null ) { A_LEN = 8*abytes.length; /* TODO Why did they put this condition? @@ -73,41 +78,8 @@ public class LeapSRPSession { else A_LEN = 64; - // Calculate x = H(s | H(U | ':' | password)) - byte[] salt_bytes = Util.trim(params.s); - byte[] xb = calculatePasswordHash(username, password, salt_bytes); - this.x = new BigInteger(1, xb); - - // Calculate v = kg^x mod N - String k_string = "bf66c44a428916cad64aa7c679f3fd897ad4c375e9bbb4cbf2f5de241d618ef0"; - this.v = calculateV(k_string); - //String v_string = v.toString(16); - serverHash = newDigest(); clientHash = newDigest(); - - // H(N) - byte[] digest_of_n = newDigest().digest(N_bytes); - - // H(g) - byte[] digest_of_g = newDigest().digest(params.g); - - // clientHash = H(N) xor H(g) - byte[] xor_digest = xor(digest_of_n, digest_of_g, digest_of_g.length); - //String hxg_string = new BigInteger(1, xor_digest).toString(16); - clientHash.update(xor_digest); - - // clientHash = H(N) xor H(g) | H(U) - byte[] username_digest = newDigest().digest(Util.trim(username.getBytes())); - username_digest = Util.trim(username_digest); - //String username_digest_string = new BigInteger(1, username_digest).toString(16); - clientHash.update(username_digest); - - // clientHash = H(N) xor H(g) | H(U) | s - //String salt_string = new BigInteger(1, salt_bytes).toString(16); - clientHash.update(salt_bytes); - - K = null; } /** @@ -198,13 +170,6 @@ public class LeapSRPSession { } A = g.modPow(a, N); Abytes = Util.trim(A.toByteArray()); - //String Abytes_string = new BigInteger(1, Abytes).toString(16); - - // clientHash = H(N) xor H(g) | H(U) | A - clientHash.update(Abytes); - - // serverHash = A - serverHash.update(Abytes); } return Abytes; } @@ -213,10 +178,52 @@ public class LeapSRPSession { * Calculates the parameter M1, to be sent to the SRP server. * It also updates hashes of client and server for further calculations in other methods. * @param Bbytes the parameter received from the server, in bytes + * @param bs * @return the parameter M1 * @throws NoSuchAlgorithmException */ - public byte[] response(byte[] Bbytes) throws NoSuchAlgorithmException { + public byte[] response(byte[] salt_bytes, byte[] Bbytes) throws NoSuchAlgorithmException { + // Calculate x = H(s | H(U | ':' | password)) + byte[] xb = calculatePasswordHash(username, password, salt_bytes); + this.x = new BigInteger(1, xb); + + // Calculate v = kg^x mod N + String k_string = "bf66c44a428916cad64aa7c679f3fd897ad4c375e9bbb4cbf2f5de241d618ef0"; + this.v = calculateV(k_string); + //String v_string = v.toString(16); + + + // H(N) + byte[] digest_of_n = newDigest().digest(N_bytes); + + // H(g) + byte[] digest_of_g = newDigest().digest(params.g); + + // clientHash = H(N) xor H(g) + byte[] xor_digest = xor(digest_of_n, digest_of_g, digest_of_g.length); + //String hxg_string = new BigInteger(1, xor_digest).toString(16); + clientHash.update(xor_digest); + + // clientHash = H(N) xor H(g) | H(U) + byte[] username_digest = newDigest().digest(Util.trim(username.getBytes())); + username_digest = Util.trim(username_digest); + //String username_digest_string = new BigInteger(1, username_digest).toString(16); + clientHash.update(username_digest); + + // clientHash = H(N) xor H(g) | H(U) | s + //String salt_string = new BigInteger(1, salt_bytes).toString(16); + clientHash.update(Util.trim(salt_bytes)); + + K = null; + + // clientHash = H(N) xor H(g) | H(U) | s | A | B + + byte[] Abytes = Util.trim(A.toByteArray()); + //String Abytes_string = new BigInteger(1, Abytes).toString(16); + + // clientHash = H(N) xor H(g) | H(U) | A + clientHash.update(Abytes); + // clientHash = H(N) xor H(g) | H(U) | s | A | B Bbytes = Util.trim(Bbytes); //String Bbytes_string = new BigInteger(1, Bbytes).toString(16); @@ -239,6 +246,7 @@ public class LeapSRPSession { byte[] M1 = Util.trim(clientHash.digest()); // serverHash = Astr + M + K + serverHash.update(Abytes); serverHash.update(M1); serverHash.update(K); return M1; -- cgit v1.2.3 From 80a8106afc8956008beb9d1ed9396f1d695d5b7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Parm=C3=A9nides=20GV?= Date: Mon, 13 May 2013 20:39:34 +0200 Subject: A bit more clean. I've upper cased ConfigHelper constants. I've created a new method in ConfigHelper, to send requests to a server, that it's used when sending A and M1. --- src/se/leap/leapclient/LeapSRPSession.java | 39 ++++++------------------------ 1 file changed, 7 insertions(+), 32 deletions(-) (limited to 'src/se/leap/leapclient/LeapSRPSession.java') diff --git a/src/se/leap/leapclient/LeapSRPSession.java b/src/se/leap/leapclient/LeapSRPSession.java index f9037de..7b32e79 100644 --- a/src/se/leap/leapclient/LeapSRPSession.java +++ b/src/se/leap/leapclient/LeapSRPSession.java @@ -47,8 +47,7 @@ public class LeapSRPSession { @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. + @param abytes, the random exponent used in the A public key */ public LeapSRPSession(String username, String password, SRPParameters params, byte[] abytes) { @@ -177,8 +176,9 @@ public class LeapSRPSession { /** * Calculates the parameter M1, to be sent to the SRP server. * It also updates hashes of client and server for further calculations in other methods. + * It uses a predefined k. + * @param salt_bytes * @param Bbytes the parameter received from the server, in bytes - * @param bs * @return the parameter M1 * @throws NoSuchAlgorithmException */ @@ -190,8 +190,6 @@ public class LeapSRPSession { // Calculate v = kg^x mod N String k_string = "bf66c44a428916cad64aa7c679f3fd897ad4c375e9bbb4cbf2f5de241d618ef0"; this.v = calculateV(k_string); - //String v_string = v.toString(16); - // H(N) byte[] digest_of_n = newDigest().digest(N_bytes); @@ -201,54 +199,45 @@ public class LeapSRPSession { // clientHash = H(N) xor H(g) byte[] xor_digest = xor(digest_of_n, digest_of_g, digest_of_g.length); - //String hxg_string = new BigInteger(1, xor_digest).toString(16); clientHash.update(xor_digest); // clientHash = H(N) xor H(g) | H(U) byte[] username_digest = newDigest().digest(Util.trim(username.getBytes())); username_digest = Util.trim(username_digest); - //String username_digest_string = new BigInteger(1, username_digest).toString(16); clientHash.update(username_digest); // clientHash = H(N) xor H(g) | H(U) | s - //String salt_string = new BigInteger(1, salt_bytes).toString(16); clientHash.update(Util.trim(salt_bytes)); K = null; - - // clientHash = H(N) xor H(g) | H(U) | s | A | B - - byte[] Abytes = Util.trim(A.toByteArray()); - //String Abytes_string = new BigInteger(1, Abytes).toString(16); // clientHash = H(N) xor H(g) | H(U) | A + byte[] Abytes = Util.trim(A.toByteArray()); clientHash.update(Abytes); // clientHash = H(N) xor H(g) | H(U) | s | A | B Bbytes = Util.trim(Bbytes); - //String Bbytes_string = new BigInteger(1, Bbytes).toString(16); clientHash.update(Bbytes); // Calculate S = (B - kg^x) ^ (a + u * x) % N BigInteger S = calculateS(Bbytes); byte[] S_bytes = Util.trim(S.toByteArray()); - //String S_bytes_string = new BigInteger(1, S_bytes).toString(16); // K = SessionHash(S) String hash_algorithm = params.hashAlgorithm; MessageDigest sessionDigest = MessageDigest.getInstance(hash_algorithm); K = Util.trim(sessionDigest.digest(S_bytes)); - //K = Util.trim(K); - //String K_bytes_string = new BigInteger(1, K).toString(16); // clientHash = H(N) xor H(g) | H(U) | A | B | K clientHash.update(K); + byte[] M1 = Util.trim(clientHash.digest()); // serverHash = Astr + M + K serverHash.update(Abytes); serverHash.update(M1); serverHash.update(K); + return M1; } @@ -263,13 +252,10 @@ public class LeapSRPSession { byte[] u_bytes = getU(Abytes, Bbytes); BigInteger B = new BigInteger(1, Bbytes); - //String Bstring = B.toString(16); BigInteger u = new BigInteger(1, u_bytes); - //String u_string = u.toString(16); BigInteger B_minus_v = B.subtract(v); BigInteger a_ux = a.add(u.multiply(x)); - //String a_ux_string = a_ux.toString(16); BigInteger S = B_minus_v.modPow(a_ux, N); return S; @@ -285,7 +271,7 @@ public class LeapSRPSession { MessageDigest u_digest = newDigest(); u_digest.update(Util.trim(Abytes)); u_digest.update(Util.trim(Bbytes)); - byte[] u_digest_bytes = u_digest.digest();//Util.trim(u_digest.digest()); + byte[] u_digest_bytes = u_digest.digest(); return Util.trim(new BigInteger(1, u_digest_bytes).toByteArray()); } @@ -302,17 +288,6 @@ public class LeapSRPSession { 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 - { - return K; - } - - /** * @return a new SHA-256 digest. */ -- cgit v1.2.3 From 7b75fbe9ca3b2d6175b124b26f5d8f527b15d1bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Parm=C3=A9nides=20GV?= Date: Wed, 29 May 2013 19:54:26 +0200 Subject: Fixed passwords with strange characters? Using two test with values from my localhost leap_web deployment, I've achieved to login with passwords containing ! and $ without problems. This should fix bug #2348. --- src/se/leap/leapclient/LeapSRPSession.java | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) (limited to 'src/se/leap/leapclient/LeapSRPSession.java') diff --git a/src/se/leap/leapclient/LeapSRPSession.java b/src/se/leap/leapclient/LeapSRPSession.java index 7b32e79..715e9de 100644 --- a/src/se/leap/leapclient/LeapSRPSession.java +++ b/src/se/leap/leapclient/LeapSRPSession.java @@ -95,35 +95,24 @@ public class LeapSRPSession { MessageDigest x_digest = newDigest(); // Try to convert the username to a byte[] using UTF-8 byte[] user = null; + byte[] password_bytes = null; byte[] colon = {}; try { user = Util.trim(username.getBytes("UTF-8")); colon = Util.trim(":".getBytes("UTF-8")); + password_bytes = Util.trim(password.getBytes("UTF-8")); } catch(UnsupportedEncodingException e) { // Use the default platform encoding user = Util.trim(username.getBytes()); colon = Util.trim(":".getBytes()); + password_bytes = Util.trim(password.getBytes()); } - - byte[] passBytes = new byte[2*password.toCharArray().length]; - int passBytesLength = 0; - for(int p = 0; p < password.toCharArray().length; p++) { - int c = (password.toCharArray()[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 x_digest.update(user); x_digest.update(colon); - x_digest.update(passBytes, 0, passBytesLength); + x_digest.update(password_bytes); byte[] h = x_digest.digest(); //h = Util.trim(h); -- cgit v1.2.3 From ac47aab124d63add14189cb3d03e3a05361a7932 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Parm=C3=A9nides=20GV?= Date: Tue, 4 Jun 2013 16:31:39 +0200 Subject: Fixed 2 important bugs. LeapSRPSession was doing bad SRP calculations when salt byte array started with a 0. Now I trimmed that array before using it. ProviderAPI was not timing out when a server didn't respond. Now, I use a timeout of 1 second to stop waiting for a response. --- src/se/leap/leapclient/LeapSRPSession.java | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) (limited to 'src/se/leap/leapclient/LeapSRPSession.java') diff --git a/src/se/leap/leapclient/LeapSRPSession.java b/src/se/leap/leapclient/LeapSRPSession.java index 715e9de..8d6f77b 100644 --- a/src/se/leap/leapclient/LeapSRPSession.java +++ b/src/se/leap/leapclient/LeapSRPSession.java @@ -109,13 +109,29 @@ public class LeapSRPSession { password_bytes = Util.trim(password.getBytes()); } + /*byte[] passBytes = new byte[2*password.toCharArray().length]; + int passBytesLength = 0; + for(int p = 0; p < password.toCharArray().length; p++) { + int c = (password.toCharArray()[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 x_digest.update(user); x_digest.update(colon); x_digest.update(password_bytes); + //x_digest.update(passBytes, 0, passBytesLength); byte[] h = x_digest.digest(); + String hstr = new BigInteger(1, h).toString(16); //h = Util.trim(h); - + //25c19c2b903ff36dd5acd6e1136b8f3af008ceee45103ef9771334f4246d6226 x_digest.reset(); x_digest.update(salt); x_digest.update(h); @@ -173,8 +189,9 @@ public class LeapSRPSession { */ public byte[] response(byte[] salt_bytes, byte[] Bbytes) throws NoSuchAlgorithmException { // Calculate x = H(s | H(U | ':' | password)) - byte[] xb = calculatePasswordHash(username, password, salt_bytes); + byte[] xb = calculatePasswordHash(username, password, Util.trim(salt_bytes)); this.x = new BigInteger(1, xb); + String xstr = x.toString(16); // Calculate v = kg^x mod N String k_string = "bf66c44a428916cad64aa7c679f3fd897ad4c375e9bbb4cbf2f5de241d618ef0"; @@ -211,6 +228,7 @@ public class LeapSRPSession { // Calculate S = (B - kg^x) ^ (a + u * x) % N BigInteger S = calculateS(Bbytes); byte[] S_bytes = Util.trim(S.toByteArray()); + String Sstr = S.toString(16); // K = SessionHash(S) String hash_algorithm = params.hashAlgorithm; @@ -219,8 +237,10 @@ public class LeapSRPSession { // clientHash = H(N) xor H(g) | H(U) | A | B | K clientHash.update(K); + String Kstr = new BigInteger(1, K).toString(16); byte[] M1 = Util.trim(clientHash.digest()); + String M1str = new BigInteger(1, M1).toString(16); // serverHash = Astr + M + K serverHash.update(Abytes); @@ -244,9 +264,10 @@ public class LeapSRPSession { BigInteger u = new BigInteger(1, u_bytes); BigInteger B_minus_v = B.subtract(v); + String vstr = v.toString(16); BigInteger a_ux = a.add(u.multiply(x)); + String xstr = x.toString(16); BigInteger S = B_minus_v.modPow(a_ux, N); - return S; } -- cgit v1.2.3 From 611bb7869ea6a98fe656b6e86ef98118d232fd45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Parm=C3=A9nides=20GV?= Date: Tue, 4 Jun 2013 20:42:05 +0200 Subject: Fixed SRP strange characters bugs. The problem was the encoding of the bytes when calculating the password hash. I supposed that it was UTF-8 (I already saw that encoding in the html code from leap_web), but not, it was ISO-8859-1 (trial/error). --- src/se/leap/leapclient/LeapSRPSession.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/se/leap/leapclient/LeapSRPSession.java') diff --git a/src/se/leap/leapclient/LeapSRPSession.java b/src/se/leap/leapclient/LeapSRPSession.java index 8d6f77b..77f4394 100644 --- a/src/se/leap/leapclient/LeapSRPSession.java +++ b/src/se/leap/leapclient/LeapSRPSession.java @@ -97,10 +97,11 @@ public class LeapSRPSession { byte[] user = null; byte[] password_bytes = null; byte[] colon = {}; + String encoding = "ISO-8859-1"; try { - user = Util.trim(username.getBytes("UTF-8")); - colon = Util.trim(":".getBytes("UTF-8")); - password_bytes = Util.trim(password.getBytes("UTF-8")); + user = Util.trim(username.getBytes(encoding)); + colon = Util.trim(":".getBytes(encoding)); + password_bytes = Util.trim(password.getBytes(encoding)); } catch(UnsupportedEncodingException e) { // Use the default platform encoding -- cgit v1.2.3 From d74a3ae14a27893572d170c138e18522b541866a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Parm=C3=A9nides=20GV?= Date: Wed, 5 Jun 2013 18:57:55 +0200 Subject: Fixed passwords with \ character. The substitution I was doing let me to pass my tests localhost, but was not valid for real use in Android emulator. This was so because JSONObject getString method understood \/ simply as /, while what I wanted was plain \/. This commit makes #2368 --- src/se/leap/leapclient/LeapSRPSession.java | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) (limited to 'src/se/leap/leapclient/LeapSRPSession.java') diff --git a/src/se/leap/leapclient/LeapSRPSession.java b/src/se/leap/leapclient/LeapSRPSession.java index 77f4394..47dff27 100644 --- a/src/se/leap/leapclient/LeapSRPSession.java +++ b/src/se/leap/leapclient/LeapSRPSession.java @@ -90,10 +90,10 @@ public class LeapSRPSession { */ public byte[] calculatePasswordHash(String username, String password, byte[] salt) { - password = password.replaceAll("\\\\", "\\\\\\\\"); + //password = password.replaceAll("\\\\", "\\\\\\\\"); // Calculate x = H(s | H(U | ':' | password)) MessageDigest x_digest = newDigest(); - // Try to convert the username to a byte[] using UTF-8 + // Try to convert the username to a byte[] using ISO-8859-1 byte[] user = null; byte[] password_bytes = null; byte[] colon = {}; @@ -110,29 +110,12 @@ public class LeapSRPSession { password_bytes = Util.trim(password.getBytes()); } - /*byte[] passBytes = new byte[2*password.toCharArray().length]; - int passBytesLength = 0; - for(int p = 0; p < password.toCharArray().length; p++) { - int c = (password.toCharArray()[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 x_digest.update(user); x_digest.update(colon); x_digest.update(password_bytes); - //x_digest.update(passBytes, 0, passBytesLength); byte[] h = x_digest.digest(); - String hstr = new BigInteger(1, h).toString(16); - //h = Util.trim(h); - //25c19c2b903ff36dd5acd6e1136b8f3af008ceee45103ef9771334f4246d6226 + x_digest.reset(); x_digest.update(salt); x_digest.update(h); -- cgit v1.2.3 From e581c6f2c1844d5dd96b12413df91dbb06102282 Mon Sep 17 00:00:00 2001 From: Sean Leonard Date: Sat, 8 Jun 2013 22:50:07 -0600 Subject: Clean up some unused and some formatting --- src/se/leap/leapclient/LeapSRPSession.java | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/se/leap/leapclient/LeapSRPSession.java') diff --git a/src/se/leap/leapclient/LeapSRPSession.java b/src/se/leap/leapclient/LeapSRPSession.java index 47dff27..8eb28c4 100644 --- a/src/se/leap/leapclient/LeapSRPSession.java +++ b/src/se/leap/leapclient/LeapSRPSession.java @@ -7,9 +7,7 @@ import java.security.NoSuchAlgorithmException; import java.util.Arrays; import org.jboss.security.Util; -import org.jboss.security.srp.SRPClientSession; import org.jboss.security.srp.SRPParameters; -import org.jboss.security.srp.SRPPermission; public class LeapSRPSession { -- cgit v1.2.3 From 1e2470b1e940877f6fe247a65bccd2b62b621eb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Parm=C3=A9nides=20GV?= Date: Mon, 10 Jun 2013 18:56:34 +0200 Subject: Removed unused methods and variables. Variables from LeapSRPSession were there because I used it while testing srp calculations, comparing that strings with the ones from javascript. Unused method from ProviderAPI was there because I foresee I'll have to implement it in the future, but I've removed it since it's already in the history. This fixes #2781. --- src/se/leap/leapclient/LeapSRPSession.java | 6 ------ 1 file changed, 6 deletions(-) (limited to 'src/se/leap/leapclient/LeapSRPSession.java') diff --git a/src/se/leap/leapclient/LeapSRPSession.java b/src/se/leap/leapclient/LeapSRPSession.java index 8eb28c4..0a8e735 100644 --- a/src/se/leap/leapclient/LeapSRPSession.java +++ b/src/se/leap/leapclient/LeapSRPSession.java @@ -173,7 +173,6 @@ public class LeapSRPSession { // Calculate x = H(s | H(U | ':' | password)) byte[] xb = calculatePasswordHash(username, password, Util.trim(salt_bytes)); this.x = new BigInteger(1, xb); - String xstr = x.toString(16); // Calculate v = kg^x mod N String k_string = "bf66c44a428916cad64aa7c679f3fd897ad4c375e9bbb4cbf2f5de241d618ef0"; @@ -210,7 +209,6 @@ public class LeapSRPSession { // Calculate S = (B - kg^x) ^ (a + u * x) % N BigInteger S = calculateS(Bbytes); byte[] S_bytes = Util.trim(S.toByteArray()); - String Sstr = S.toString(16); // K = SessionHash(S) String hash_algorithm = params.hashAlgorithm; @@ -219,10 +217,8 @@ public class LeapSRPSession { // clientHash = H(N) xor H(g) | H(U) | A | B | K clientHash.update(K); - String Kstr = new BigInteger(1, K).toString(16); byte[] M1 = Util.trim(clientHash.digest()); - String M1str = new BigInteger(1, M1).toString(16); // serverHash = Astr + M + K serverHash.update(Abytes); @@ -246,9 +242,7 @@ public class LeapSRPSession { BigInteger u = new BigInteger(1, u_bytes); BigInteger B_minus_v = B.subtract(v); - String vstr = v.toString(16); BigInteger a_ux = a.add(u.multiply(x)); - String xstr = x.toString(16); BigInteger S = B_minus_v.modPow(a_ux, N); return S; } -- cgit v1.2.3 From d475ae617d8dc0994a1294be7c8cca338a68fd9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Parm=C3=A9nides=20GV?= Date: Wed, 19 Jun 2013 19:05:12 +0200 Subject: First round of comments. This resolves the first step from issue #2908. Next step: Put user message strings into an appropiate place. --- src/se/leap/leapclient/LeapSRPSession.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'src/se/leap/leapclient/LeapSRPSession.java') diff --git a/src/se/leap/leapclient/LeapSRPSession.java b/src/se/leap/leapclient/LeapSRPSession.java index 0a8e735..beb286d 100644 --- a/src/se/leap/leapclient/LeapSRPSession.java +++ b/src/se/leap/leapclient/LeapSRPSession.java @@ -9,6 +9,14 @@ import java.util.Arrays; import org.jboss.security.Util; import org.jboss.security.srp.SRPParameters; +/** + * Implements all SRP algorithm logic. + * + * It's derived from JBoss implementation, with adjustments to make it work with LEAP platform. + * + * @author parmegv + * + */ public class LeapSRPSession { private SRPParameters params; @@ -133,7 +141,14 @@ public class LeapSRPSession { return v; } - public byte[] xor(byte[] b1, byte[] b2, int length) + /** + * Calculates the trimmed xor from two BigInteger numbers + * @param b1 the positive source to build first BigInteger + * @param b2 the positive source to build second BigInteger + * @param length + * @return + */ + public byte[] xor(byte[] b1, byte[] b2) { //TODO Check if length matters in the order, when b2 is smaller than b1 or viceversa byte[] xor_digest = new BigInteger(1, b1).xor(new BigInteger(1, b2)).toByteArray(); @@ -185,7 +200,7 @@ public class LeapSRPSession { byte[] digest_of_g = newDigest().digest(params.g); // clientHash = H(N) xor H(g) - byte[] xor_digest = xor(digest_of_n, digest_of_g, digest_of_g.length); + byte[] xor_digest = xor(digest_of_n, digest_of_g); clientHash.update(xor_digest); // clientHash = H(N) xor H(g) | H(U) -- cgit v1.2.3 From 65e2f56a8a10877964fb9fc57963d1bf8104801a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Parm=C3=A9nides=20GV?= Date: Thu, 20 Jun 2013 17:47:07 +0200 Subject: No binary library needed. I've decided not to include any lib, but to copy the SRPParameters class to our codebase and Util.trim method to ConfigHelper. --- src/se/leap/leapclient/LeapSRPSession.java | 69 +++++++++++++++--------------- 1 file changed, 35 insertions(+), 34 deletions(-) (limited to 'src/se/leap/leapclient/LeapSRPSession.java') diff --git a/src/se/leap/leapclient/LeapSRPSession.java b/src/se/leap/leapclient/LeapSRPSession.java index beb286d..d21ccff 100644 --- a/src/se/leap/leapclient/LeapSRPSession.java +++ b/src/se/leap/leapclient/LeapSRPSession.java @@ -4,9 +4,9 @@ import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; import java.util.Arrays; -import org.jboss.security.Util; import org.jboss.security.srp.SRPParameters; /** @@ -30,6 +30,7 @@ public class LeapSRPSession { private BigInteger a; private BigInteger A; private byte[] K; + private SecureRandom pseudoRng; /** 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 */ @@ -57,20 +58,20 @@ public class LeapSRPSession { */ public LeapSRPSession(String username, String 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); - N_bytes = Util.trim(params.N); + N_bytes = ConfigHelper.trim(params.N); this.N = new BigInteger(1, N_bytes); this.username = username; this.password = password; + try { + pseudoRng = SecureRandom.getInstance("SHA1PRNG"); + } catch (NoSuchAlgorithmException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if( abytes != null ) { A_LEN = 8*abytes.length; /* TODO Why did they put this condition? @@ -105,15 +106,15 @@ public class LeapSRPSession { byte[] colon = {}; String encoding = "ISO-8859-1"; try { - user = Util.trim(username.getBytes(encoding)); - colon = Util.trim(":".getBytes(encoding)); - password_bytes = Util.trim(password.getBytes(encoding)); + user = ConfigHelper.trim(username.getBytes(encoding)); + colon = ConfigHelper.trim(":".getBytes(encoding)); + password_bytes = ConfigHelper.trim(password.getBytes(encoding)); } catch(UnsupportedEncodingException e) { // Use the default platform encoding - user = Util.trim(username.getBytes()); - colon = Util.trim(":".getBytes()); - password_bytes = Util.trim(password.getBytes()); + user = ConfigHelper.trim(username.getBytes()); + colon = ConfigHelper.trim(":".getBytes()); + password_bytes = ConfigHelper.trim(password.getBytes()); } // Build the hash @@ -152,7 +153,7 @@ public class LeapSRPSession { { //TODO Check if length matters in the order, when b2 is smaller than b1 or viceversa byte[] xor_digest = new BigInteger(1, b1).xor(new BigInteger(1, b2)).toByteArray(); - return Util.trim(xor_digest); + return ConfigHelper.trim(xor_digest); } /** @@ -166,11 +167,11 @@ public class LeapSRPSession { if( a == null ) { BigInteger one = BigInteger.ONE; do { - a = new BigInteger(A_LEN, Util.getPRNG()); + a = new BigInteger(A_LEN, pseudoRng); } while(a.compareTo(one) <= 0); } A = g.modPow(a, N); - Abytes = Util.trim(A.toByteArray()); + Abytes = ConfigHelper.trim(A.toByteArray()); } return Abytes; } @@ -186,7 +187,7 @@ public class LeapSRPSession { */ public byte[] response(byte[] salt_bytes, byte[] Bbytes) throws NoSuchAlgorithmException { // Calculate x = H(s | H(U | ':' | password)) - byte[] xb = calculatePasswordHash(username, password, Util.trim(salt_bytes)); + byte[] xb = calculatePasswordHash(username, password, ConfigHelper.trim(salt_bytes)); this.x = new BigInteger(1, xb); // Calculate v = kg^x mod N @@ -204,36 +205,36 @@ public class LeapSRPSession { clientHash.update(xor_digest); // clientHash = H(N) xor H(g) | H(U) - byte[] username_digest = newDigest().digest(Util.trim(username.getBytes())); - username_digest = Util.trim(username_digest); + byte[] username_digest = newDigest().digest(ConfigHelper.trim(username.getBytes())); + username_digest = ConfigHelper.trim(username_digest); clientHash.update(username_digest); // clientHash = H(N) xor H(g) | H(U) | s - clientHash.update(Util.trim(salt_bytes)); + clientHash.update(ConfigHelper.trim(salt_bytes)); K = null; // clientHash = H(N) xor H(g) | H(U) | A - byte[] Abytes = Util.trim(A.toByteArray()); + byte[] Abytes = ConfigHelper.trim(A.toByteArray()); clientHash.update(Abytes); // clientHash = H(N) xor H(g) | H(U) | s | A | B - Bbytes = Util.trim(Bbytes); + Bbytes = ConfigHelper.trim(Bbytes); clientHash.update(Bbytes); // Calculate S = (B - kg^x) ^ (a + u * x) % N BigInteger S = calculateS(Bbytes); - byte[] S_bytes = Util.trim(S.toByteArray()); + byte[] S_bytes = ConfigHelper.trim(S.toByteArray()); // K = SessionHash(S) String hash_algorithm = params.hashAlgorithm; MessageDigest sessionDigest = MessageDigest.getInstance(hash_algorithm); - K = Util.trim(sessionDigest.digest(S_bytes)); + K = ConfigHelper.trim(sessionDigest.digest(S_bytes)); // clientHash = H(N) xor H(g) | H(U) | A | B | K clientHash.update(K); - byte[] M1 = Util.trim(clientHash.digest()); + byte[] M1 = ConfigHelper.trim(clientHash.digest()); // serverHash = Astr + M + K serverHash.update(Abytes); @@ -249,8 +250,8 @@ public class LeapSRPSession { * @return the parameter S */ private BigInteger calculateS(byte[] Bbytes) { - byte[] Abytes = Util.trim(A.toByteArray()); - Bbytes = Util.trim(Bbytes); + byte[] Abytes = ConfigHelper.trim(A.toByteArray()); + Bbytes = ConfigHelper.trim(Bbytes); byte[] u_bytes = getU(Abytes, Bbytes); BigInteger B = new BigInteger(1, Bbytes); @@ -270,10 +271,10 @@ public class LeapSRPSession { */ public byte[] getU(byte[] Abytes, byte[] Bbytes) { MessageDigest u_digest = newDigest(); - u_digest.update(Util.trim(Abytes)); - u_digest.update(Util.trim(Bbytes)); + u_digest.update(ConfigHelper.trim(Abytes)); + u_digest.update(ConfigHelper.trim(Bbytes)); byte[] u_digest_bytes = u_digest.digest(); - return Util.trim(new BigInteger(1, u_digest_bytes).toByteArray()); + return ConfigHelper.trim(new BigInteger(1, u_digest_bytes).toByteArray()); } /** @@ -283,8 +284,8 @@ public class LeapSRPSession { public boolean verify(byte[] M2) { // M2 = H(A | M1 | K) - M2 = Util.trim(M2); - byte[] myM2 = Util.trim(serverHash.digest()); + M2 = ConfigHelper.trim(M2); + byte[] myM2 = ConfigHelper.trim(serverHash.digest()); boolean valid = Arrays.equals(M2, myM2); return valid; } -- cgit v1.2.3 From 22e90c26036291de66f26ee148bb4c75b68c7764 Mon Sep 17 00:00:00 2001 From: Sean Leonard Date: Fri, 12 Jul 2013 21:53:57 -0600 Subject: Include GPLv3+ file and header for files in se.leap.leapclient package --- src/se/leap/leapclient/LeapSRPSession.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'src/se/leap/leapclient/LeapSRPSession.java') diff --git a/src/se/leap/leapclient/LeapSRPSession.java b/src/se/leap/leapclient/LeapSRPSession.java index d21ccff..9451c1b 100644 --- a/src/se/leap/leapclient/LeapSRPSession.java +++ b/src/se/leap/leapclient/LeapSRPSession.java @@ -1,4 +1,20 @@ -package se.leap.leapclient; +/** + * Copyright (c) 2013 LEAP Encryption Access Project and contributers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package se.leap.leapclient; import java.io.UnsupportedEncodingException; import java.math.BigInteger; -- cgit v1.2.3