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 import org.bouncycastle.util.BigIntegers;
9
10 public class SRP6Util
11 {
12 private static BigInteger ZERO = BigInteger.valueOf(0);
13 private static BigInteger ONE = BigInteger.valueOf(1);
14
15 public static BigInteger calculateK(Digest digest, BigInteger N, BigInteger g)
16 {
17 return hashPaddedPair(digest, N, N, g);
18 }
19
20 public static BigInteger calculateU(Digest digest, BigInteger N, BigInteger A, BigInteger B)
21 {
22 return hashPaddedPair(digest, N, A, B);
23 }
24
25 public static BigInteger calculateX(Digest digest, BigInteger N, byte[] salt, byte[] identity, byte[] password)
26 {
27 byte[] output = new byte[digest.getDigestSize()];
28
29 digest.update(identity, 0, identity.length);
30 digest.update((byte)':');
31 digest.update(password, 0, password.length);
32 digest.doFinal(output, 0);
33
34 digest.update(salt, 0, salt.length);
35 digest.update(output, 0, output.length);
36 digest.doFinal(output, 0);
37
38 return new BigInteger(1, output).mod(N);
39 }
40
41 public static BigInteger generatePrivateValue(Digest digest, BigInteger N, BigInteger g, SecureRandom random)
42 {
43 int minBits = Math.min(256, N.bitLength() / 2);
44 BigInteger min = ONE.shiftLeft(minBits - 1);
45 BigInteger max = N.subtract(ONE);
46
47 return BigIntegers.createRandomInRange(min, max, random);
48 }
49
50 public static BigInteger validatePublicValue(BigInteger N, BigInteger val)
51 throws CryptoException
52 {
53 val = val.mod(N);
54
55 // Check that val % N != 0
56 if (val.equals(ZERO))
57 {
58 throw new CryptoException("Invalid public value: 0");
59 }
60
61 return val;
62 }
63
64 private static BigInteger hashPaddedPair(Digest digest, BigInteger N, BigInteger n1, BigInteger n2)
65 {
66 int padLength = (N.bitLength() + 7) / 8;
67
68 byte[] n1_bytes = getPadded(n1, padLength);
69 byte[] n2_bytes = getPadded(n2, padLength);
70
71 digest.update(n1_bytes, 0, n1_bytes.length);
72 digest.update(n2_bytes, 0, n2_bytes.length);
73
74 byte[] output = new byte[digest.getDigestSize()];
75 digest.doFinal(output, 0);
76
77 return new BigInteger(1, output).mod(N);
78 }
79
80 private static byte[] getPadded(BigInteger n, int length)
81 {
82 byte[] bs = BigIntegers.asUnsignedByteArray(n);
83 if (bs.length < length)
84 {
85 byte[] tmp = new byte[length];
86 System.arraycopy(bs, 0, tmp, length - bs.length, bs.length);
87 bs = tmp;
88 }
89 return bs;
90 }
91 }