Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/quite/humla-spongycastle.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/main/java/org/spongycastle/crypto/agreement/srp')
-rw-r--r--core/src/main/java/org/spongycastle/crypto/agreement/srp/SRP6Client.java150
-rw-r--r--core/src/main/java/org/spongycastle/crypto/agreement/srp/SRP6Server.java148
-rw-r--r--core/src/main/java/org/spongycastle/crypto/agreement/srp/SRP6Util.java154
-rw-r--r--core/src/main/java/org/spongycastle/crypto/agreement/srp/SRP6VerifierGenerator.java47
4 files changed, 499 insertions, 0 deletions
diff --git a/core/src/main/java/org/spongycastle/crypto/agreement/srp/SRP6Client.java b/core/src/main/java/org/spongycastle/crypto/agreement/srp/SRP6Client.java
new file mode 100644
index 00000000..c6ee4dbe
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/agreement/srp/SRP6Client.java
@@ -0,0 +1,150 @@
+package org.spongycastle.crypto.agreement.srp;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CryptoException;
+import org.spongycastle.crypto.Digest;
+
+/**
+ * Implements the client side SRP-6a protocol. Note that this class is stateful, and therefore NOT threadsafe.
+ * This implementation of SRP is based on the optimized message sequence put forth by Thomas Wu in the paper
+ * "SRP-6: Improvements and Refinements to the Secure Remote Password Protocol, 2002"
+ */
+public class SRP6Client
+{
+ protected BigInteger N;
+ protected BigInteger g;
+
+ protected BigInteger a;
+ protected BigInteger A;
+
+ protected BigInteger B;
+
+ protected BigInteger x;
+ protected BigInteger u;
+ protected BigInteger S;
+
+ protected BigInteger M1;
+ protected BigInteger M2;
+ protected BigInteger Key;
+
+ protected Digest digest;
+ protected SecureRandom random;
+
+ public SRP6Client()
+ {
+ }
+
+ /**
+ * Initialises the client to begin new authentication attempt
+ * @param N The safe prime associated with the client's verifier
+ * @param g The group parameter associated with the client's verifier
+ * @param digest The digest algorithm associated with the client's verifier
+ * @param random For key generation
+ */
+ public void init(BigInteger N, BigInteger g, Digest digest, SecureRandom random)
+ {
+ this.N = N;
+ this.g = g;
+ this.digest = digest;
+ this.random = random;
+ }
+
+ /**
+ * Generates client's credentials given the client's salt, identity and password
+ * @param salt The salt used in the client's verifier.
+ * @param identity The user's identity (eg. username)
+ * @param password The user's password
+ * @return Client's public value to send to server
+ */
+ public BigInteger generateClientCredentials(byte[] salt, byte[] identity, byte[] password)
+ {
+ this.x = SRP6Util.calculateX(digest, N, salt, identity, password);
+ this.a = selectPrivateValue();
+ this.A = g.modPow(a, N);
+
+ return A;
+ }
+
+ /**
+ * Generates the secret S given the server's credentials
+ * @param serverB The server's credentials
+ * @return Client's verification message for the server
+ * @throws CryptoException If server's credentials are invalid
+ */
+ public BigInteger calculateSecret(BigInteger serverB) throws CryptoException
+ {
+ this.B = SRP6Util.validatePublicValue(N, serverB);
+ this.u = SRP6Util.calculateU(digest, N, A, B);
+ this.S = calculateS();
+
+ return S;
+ }
+
+ protected BigInteger selectPrivateValue()
+ {
+ return SRP6Util.generatePrivateValue(digest, N, g, random);
+ }
+
+ private BigInteger calculateS()
+ {
+ BigInteger k = SRP6Util.calculateK(digest, N, g);
+ BigInteger exp = u.multiply(x).add(a);
+ BigInteger tmp = g.modPow(x, N).multiply(k).mod(N);
+ return B.subtract(tmp).mod(N).modPow(exp, N);
+ }
+
+ /**
+ * Computes the client evidence message M1 using the previously received values.
+ * To be called after calculating the secret S.
+ * @return M1: the client side generated evidence message
+ * @throws CryptoException
+ */
+ public BigInteger calculateClientEvidenceMessage() throws CryptoException{
+ // verify pre-requirements
+ if ((this.A==null)||(this.B==null)||(this.S==null)){
+ throw new CryptoException("Impossible to compute M1: " +
+ "some data are missing from the previous operations (A,B,S)");
+ }
+ // compute the client evidence message 'M1'
+ this.M1 = SRP6Util.calculateM1(digest, N, A, B, S);
+ return M1;
+ }
+
+ /** Authenticates the server evidence message M2 received and saves it only if correct.
+ * @param M2: the server side generated evidence message
+ * @return A boolean indicating if the server message M2 was the expected one.
+ * @throws CryptoException
+ */
+ public boolean verifyServerEvidenceMessage(BigInteger serverM2) throws CryptoException{
+ //verify pre-requirements
+ if ((this.A==null)||(this.M1==null)||(this.S==null)){
+ throw new CryptoException("Impossible to compute and verify M2: " +
+ "some data are missing from the previous operations (A,M1,S)");
+ }
+ // Compute the own server evidence message 'M2'
+ BigInteger computedM2 = SRP6Util.calculateM2(digest, N, A, M1, S);
+ if (computedM2.equals(serverM2)){
+ this.M2 = serverM2;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Computes the final session key as a result of the SRP successful mutual authentication
+ * To be called after verifying the server evidence message M2.
+ * @return Key: the mutually authenticated symmetric session key
+ * @throws CryptoException
+ */
+ public BigInteger calculateSessionKey() throws CryptoException{
+ //verify pre-requirements (here we enforce a previous calculation of M1 and M2)
+ if ((this.S==null)||(this.M1==null)||(this.M2==null)){
+ throw new CryptoException("Impossible to compute Key: " +
+ "some data are missing from the previous operations (S,M1,M2)");
+ }
+ this.Key = SRP6Util.calculateKey(digest, N, S);
+ return Key;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/agreement/srp/SRP6Server.java b/core/src/main/java/org/spongycastle/crypto/agreement/srp/SRP6Server.java
new file mode 100644
index 00000000..efbdd0ab
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/agreement/srp/SRP6Server.java
@@ -0,0 +1,148 @@
+package org.spongycastle.crypto.agreement.srp;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CryptoException;
+import org.spongycastle.crypto.Digest;
+
+/**
+ * Implements the server side SRP-6a protocol. Note that this class is stateful, and therefore NOT threadsafe.
+ * This implementation of SRP is based on the optimized message sequence put forth by Thomas Wu in the paper
+ * "SRP-6: Improvements and Refinements to the Secure Remote Password Protocol, 2002"
+ */
+public class SRP6Server
+{
+ protected BigInteger N;
+ protected BigInteger g;
+ protected BigInteger v;
+
+ protected SecureRandom random;
+ protected Digest digest;
+
+ protected BigInteger A;
+
+ protected BigInteger b;
+ protected BigInteger B;
+
+ protected BigInteger u;
+ protected BigInteger S;
+ protected BigInteger M1;
+ protected BigInteger M2;
+ protected BigInteger Key;
+
+ public SRP6Server()
+ {
+ }
+
+ /**
+ * Initialises the server to accept a new client authentication attempt
+ * @param N The safe prime associated with the client's verifier
+ * @param g The group parameter associated with the client's verifier
+ * @param v The client's verifier
+ * @param digest The digest algorithm associated with the client's verifier
+ * @param random For key generation
+ */
+ public void init(BigInteger N, BigInteger g, BigInteger v, Digest digest, SecureRandom random)
+ {
+ this.N = N;
+ this.g = g;
+ this.v = v;
+
+ this.random = random;
+ this.digest = digest;
+ }
+
+ /**
+ * Generates the server's credentials that are to be sent to the client.
+ * @return The server's public value to the client
+ */
+ public BigInteger generateServerCredentials()
+ {
+ BigInteger k = SRP6Util.calculateK(digest, N, g);
+ this.b = selectPrivateValue();
+ this.B = k.multiply(v).mod(N).add(g.modPow(b, N)).mod(N);
+
+ return B;
+ }
+
+ /**
+ * Processes the client's credentials. If valid the shared secret is generated and returned.
+ * @param clientA The client's credentials
+ * @return A shared secret BigInteger
+ * @throws CryptoException If client's credentials are invalid
+ */
+ public BigInteger calculateSecret(BigInteger clientA) throws CryptoException
+ {
+ this.A = SRP6Util.validatePublicValue(N, clientA);
+ this.u = SRP6Util.calculateU(digest, N, A, B);
+ this.S = calculateS();
+
+ return S;
+ }
+
+ protected BigInteger selectPrivateValue()
+ {
+ return SRP6Util.generatePrivateValue(digest, N, g, random);
+ }
+
+ private BigInteger calculateS()
+ {
+ return v.modPow(u, N).multiply(A).mod(N).modPow(b, N);
+ }
+
+ /**
+ * Authenticates the received client evidence message M1 and saves it only if correct.
+ * To be called after calculating the secret S.
+ * @param M1: the client side generated evidence message
+ * @return A boolean indicating if the client message M1 was the expected one.
+ * @throws CryptoException
+ */
+ public boolean verifyClientEvidenceMessage(BigInteger clientM1) throws CryptoException{
+ //verify pre-requirements
+ if ((this.A==null)||(this.B==null)||(this.S==null)){
+ throw new CryptoException("Impossible to compute and verify M1: " +
+ "some data are missing from the previous operations (A,B,S)");
+ }
+ // Compute the own client evidence message 'M1'
+ BigInteger computedM1 = SRP6Util.calculateM1(digest, N, A, B, S);
+ if (computedM1.equals(clientM1)){
+ this.M1 = clientM1;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Computes the server evidence message M2 using the previously verified values.
+ * To be called after successfully verifying the client evidence message M1.
+ * @return M2: the server side generated evidence message
+ * @throws CryptoException
+ */
+ public BigInteger calculateServerEvidenceMessage() throws CryptoException{
+ //verify pre-requirements
+ if ((this.A==null)||(this.M1==null)||(this.S==null)){
+ throw new CryptoException("Impossible to compute M2: " +
+ "some data are missing from the previous operations (A,M1,S)");
+ }
+ // Compute the server evidence message 'M2'
+ this.M2 = SRP6Util.calculateM2(digest, N, A, M1, S);
+ return M2;
+ }
+
+ /**
+ * Computes the final session key as a result of the SRP successful mutual authentication
+ * To be called after calculating the server evidence message M2.
+ * @return Key: the mutual authenticated symmetric session key
+ * @throws CryptoException
+ */
+ public BigInteger calculateSessionKey() throws CryptoException{
+ //verify pre-requirements
+ if ((this.S==null)||(this.M1==null)||(this.M2==null)){
+ throw new CryptoException("Impossible to compute Key: " +
+ "some data are missing from the previous operations (S,M1,M2)");
+ }
+ this.Key = SRP6Util.calculateKey(digest, N, S);
+ return Key;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/agreement/srp/SRP6Util.java b/core/src/main/java/org/spongycastle/crypto/agreement/srp/SRP6Util.java
new file mode 100644
index 00000000..25d5b9a7
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/agreement/srp/SRP6Util.java
@@ -0,0 +1,154 @@
+package org.spongycastle.crypto.agreement.srp;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.CryptoException;
+import org.spongycastle.crypto.Digest;
+import org.spongycastle.util.BigIntegers;
+
+public class SRP6Util
+{
+ private static BigInteger ZERO = BigInteger.valueOf(0);
+ private static BigInteger ONE = BigInteger.valueOf(1);
+
+ public static BigInteger calculateK(Digest digest, BigInteger N, BigInteger g)
+ {
+ return hashPaddedPair(digest, N, N, g);
+ }
+
+ public static BigInteger calculateU(Digest digest, BigInteger N, BigInteger A, BigInteger B)
+ {
+ return hashPaddedPair(digest, N, A, B);
+ }
+
+ public static BigInteger calculateX(Digest digest, BigInteger N, byte[] salt, byte[] identity, byte[] password)
+ {
+ byte[] output = new byte[digest.getDigestSize()];
+
+ digest.update(identity, 0, identity.length);
+ digest.update((byte)':');
+ digest.update(password, 0, password.length);
+ digest.doFinal(output, 0);
+
+ digest.update(salt, 0, salt.length);
+ digest.update(output, 0, output.length);
+ digest.doFinal(output, 0);
+
+ return new BigInteger(1, output);
+ }
+
+ public static BigInteger generatePrivateValue(Digest digest, BigInteger N, BigInteger g, SecureRandom random)
+ {
+ int minBits = Math.min(256, N.bitLength() / 2);
+ BigInteger min = ONE.shiftLeft(minBits - 1);
+ BigInteger max = N.subtract(ONE);
+
+ return BigIntegers.createRandomInRange(min, max, random);
+ }
+
+ public static BigInteger validatePublicValue(BigInteger N, BigInteger val)
+ throws CryptoException
+ {
+ val = val.mod(N);
+
+ // Check that val % N != 0
+ if (val.equals(ZERO))
+ {
+ throw new CryptoException("Invalid public value: 0");
+ }
+
+ return val;
+ }
+ /**
+ * Computes the client evidence message (M1) according to the standard routine:
+ * M1 = H( A | B | S )
+ * @param digest The Digest used as the hashing function H
+ * @param N Modulus used to get the pad length
+ * @param A The public client value
+ * @param B The public server value
+ * @param S The secret calculated by both sides
+ * @return M1 The calculated client evidence message
+ */
+ public static BigInteger calculateM1(Digest digest, BigInteger N, BigInteger A, BigInteger B, BigInteger S) {
+ BigInteger M1 = hashPaddedTriplet(digest,N,A,B,S);
+ return M1;
+ }
+
+ /**
+ * Computes the server evidence message (M2) according to the standard routine:
+ * M2 = H( A | M1 | S )
+ * @param digest The Digest used as the hashing function H
+ * @param N Modulus used to get the pad length
+ * @param A The public client value
+ * @param M1 The client evidence message
+ * @param S The secret calculated by both sides
+ * @return M2 The calculated server evidence message
+ */
+ public static BigInteger calculateM2(Digest digest, BigInteger N, BigInteger A, BigInteger M1, BigInteger S){
+ BigInteger M2 = hashPaddedTriplet(digest,N,A,M1,S);
+ return M2;
+ }
+
+ /**
+ * Computes the final Key according to the standard routine: Key = H(S)
+ * @param digest The Digest used as the hashing function H
+ * @param N Modulus used to get the pad length
+ * @param S The secret calculated by both sides
+ * @return
+ */
+ public static BigInteger calculateKey(Digest digest, BigInteger N, BigInteger S) {
+ int padLength = (N.bitLength() + 7) / 8;
+ byte[] _S = getPadded(S,padLength);
+ digest.update(_S, 0, _S.length);
+
+ byte[] output = new byte[digest.getDigestSize()];
+ digest.doFinal(output, 0);
+ return new BigInteger(1, output);
+ }
+
+ private static BigInteger hashPaddedTriplet(Digest digest, BigInteger N, BigInteger n1, BigInteger n2, BigInteger n3){
+ int padLength = (N.bitLength() + 7) / 8;
+
+ byte[] n1_bytes = getPadded(n1, padLength);
+ byte[] n2_bytes = getPadded(n2, padLength);
+ byte[] n3_bytes = getPadded(n3, padLength);
+
+ digest.update(n1_bytes, 0, n1_bytes.length);
+ digest.update(n2_bytes, 0, n2_bytes.length);
+ digest.update(n3_bytes, 0, n3_bytes.length);
+
+ byte[] output = new byte[digest.getDigestSize()];
+ digest.doFinal(output, 0);
+
+ return new BigInteger(1, output);
+ }
+
+ private static BigInteger hashPaddedPair(Digest digest, BigInteger N, BigInteger n1, BigInteger n2)
+ {
+ int padLength = (N.bitLength() + 7) / 8;
+
+ byte[] n1_bytes = getPadded(n1, padLength);
+ byte[] n2_bytes = getPadded(n2, padLength);
+
+ digest.update(n1_bytes, 0, n1_bytes.length);
+ digest.update(n2_bytes, 0, n2_bytes.length);
+
+ byte[] output = new byte[digest.getDigestSize()];
+ digest.doFinal(output, 0);
+
+ return new BigInteger(1, output);
+ }
+
+ private static byte[] getPadded(BigInteger n, int length)
+ {
+ byte[] bs = BigIntegers.asUnsignedByteArray(n);
+ if (bs.length < length)
+ {
+ byte[] tmp = new byte[length];
+ System.arraycopy(bs, 0, tmp, length - bs.length, bs.length);
+ bs = tmp;
+ }
+ return bs;
+ }
+}
diff --git a/core/src/main/java/org/spongycastle/crypto/agreement/srp/SRP6VerifierGenerator.java b/core/src/main/java/org/spongycastle/crypto/agreement/srp/SRP6VerifierGenerator.java
new file mode 100644
index 00000000..0324af18
--- /dev/null
+++ b/core/src/main/java/org/spongycastle/crypto/agreement/srp/SRP6VerifierGenerator.java
@@ -0,0 +1,47 @@
+package org.spongycastle.crypto.agreement.srp;
+
+import java.math.BigInteger;
+
+import org.spongycastle.crypto.Digest;
+
+/**
+ * Generates new SRP verifier for user
+ */
+public class SRP6VerifierGenerator
+{
+ protected BigInteger N;
+ protected BigInteger g;
+ protected Digest digest;
+
+ public SRP6VerifierGenerator()
+ {
+ }
+
+ /**
+ * Initialises generator to create new verifiers
+ * @param N The safe prime to use (see DHParametersGenerator)
+ * @param g The group parameter to use (see DHParametersGenerator)
+ * @param digest The digest to use. The same digest type will need to be used later for the actual authentication
+ * attempt. Also note that the final session key size is dependent on the chosen digest.
+ */
+ public void init(BigInteger N, BigInteger g, Digest digest)
+ {
+ this.N = N;
+ this.g = g;
+ this.digest = digest;
+ }
+
+ /**
+ * Creates a new SRP verifier
+ * @param salt The salt to use, generally should be large and random
+ * @param identity The user's identifying information (eg. username)
+ * @param password The user's password
+ * @return A new verifier for use in future SRP authentication
+ */
+ public BigInteger generateVerifier(byte[] salt, byte[] identity, byte[] password)
+ {
+ BigInteger x = SRP6Util.calculateX(digest, N, salt, identity, password);
+
+ return g.modPow(x, N);
+ }
+}