summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorParménides GV <parmegv@sdf.org>2013-04-09 19:21:16 +0200
committerParménides GV <parmegv@sdf.org>2013-04-09 19:21:16 +0200
commit1026d9779ff1cdca200cf60ad0de1292475d16f7 (patch)
tree2a619ebaff13ad984b4ff1c2764c853a2f32afc7
parent47e489f521c1f69c357880234a8aa8b5408595aa (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.java435
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();
+ }
}