1 package org.bouncycastle.crypto.agreement.srp;
2
3 import java.math.BigInteger;
4 import java.security.SecureRandom;
5
6 import org.bouncycastle.crypto.CryptoException;
7 import org.bouncycastle.crypto.Digest;
8
9 /**
10 * Implements the client side SRP-6a protocol. Note that this class is stateful, and therefore NOT threadsafe.
11 * This implementation of SRP is based on the optimized message sequence put forth by Thomas Wu in the paper
12 * "SRP-6: Improvements and Refinements to the Secure Remote Password Protocol, 2002"
13 */
14 public class SRP6Client
15 {
16 protected BigInteger N;
17 protected BigInteger g;
18
19 protected BigInteger a;
20 protected BigInteger A;
21
22 protected BigInteger B;
23
24 protected BigInteger x;
25 protected BigInteger u;
26 protected BigInteger S;
27
28 protected Digest digest;
29 protected SecureRandom random;
30
31 public SRP6Client()
32 {
33 }
34
35 /**
36 * Initialises the client to begin new authentication attempt
37 * @param N The safe prime associated with the client's verifier
38 * @param g The group parameter associated with the client's verifier
39 * @param digest The digest algorithm associated with the client's verifier
40 * @param random For key generation
41 */
42 public void init(BigInteger N, BigInteger g, Digest digest, SecureRandom random)
43 {
44 this.N = N;
45 this.g = g;
46 this.digest = digest;
47 this.random = random;
48 }
49
50 /**
51 * Generates client's credentials given the client's salt, identity and password
52 * @param salt The salt used in the client's verifier.
53 * @param identity The user's identity (eg. username)
54 * @param password The user's password
55 * @return Client's public value to send to server
56 */
57 public BigInteger generateClientCredentials(byte[] salt, byte[] identity, byte[] password)
58 {
59 this.x = SRP6Util.calculateX(digest, N, salt, identity, password);
60 this.a = selectPrivateValue();
61 this.A = g.modPow(a, N);
62
63 return A;
64 }
65
66 /**
67 * Generates client's verification message given the server's credentials
68 * @param serverB The server's credentials
69 * @return Client's verification message for the server
70 * @throws CryptoException If server's credentials are invalid
71 */
72 public BigInteger calculateSecret(BigInteger serverB) throws CryptoException
73 {
74 this.B = SRP6Util.validatePublicValue(N, serverB);
75 this.u = SRP6Util.calculateU(digest, N, A, B);
76 this.S = calculateS();
77
78 return S;
79 }
80
81 protected BigInteger selectPrivateValue()
82 {
83 return SRP6Util.generatePrivateValue(digest, N, g, random);
84 }
85
86 private BigInteger calculateS()
87 {
88 BigInteger k = SRP6Util.calculateK(digest, N, g);
89 BigInteger exp = u.multiply(x).mod(N).add(a).mod(N);
90 BigInteger tmp = g.modPow(x, N).multiply(k).mod(N);
91 return B.subtract(tmp).mod(N).modPow(exp, N);
92 }
93 }