Open
Description
I was trying to use BouncyCastle SRP6 on the server side for HomeKit and I found out that I can only generate B and S, but then M1 and M2 are calculated in BouncyCastle like:
M1 = H(A | B | S)
M2 = H(A | M1 | S)
K = H(S)
While they should be calculated as follows according to RFC2945:
K = H(S)
M1 = H(H(N) XOR H(g) | H(I) | s | A | B | K)
M2 = H(A | M1 | K)
In C#, I ended up using the following code:
private SecureRandom _random = new SecureRandom();
private readonly IDigest _digest = new Sha512Digest();
private Srp6Server _server;
private byte[] _N;
private byte[] _g;
private byte[] _s;
private byte[] _I;
private byte[] _B;
private byte[] _calculatedM1;
private byte[] _K;
private byte[] _A;
public byte[] GenerateSalt()
{
byte[] s = new byte[16];
_random.NextBytes(s);
return s;
}
public byte[] GenerateServerCredentials(byte[] bN, byte[] bg, byte[] salt, string userName, string password)
{
_N = bN;
_g = bg;
_s = salt;
_I = Encoding.UTF8.GetBytes(userName);
byte[] P = Encoding.UTF8.GetBytes(password);
BigInteger N = new BigInteger(1, _N);
BigInteger g = new BigInteger(1, _g);
Srp6GroupParameters group = new Srp6GroupParameters(N, g);
Srp6VerifierGenerator gen = new Srp6VerifierGenerator();
gen.Init(group, _digest);
BigInteger v = gen.GenerateVerifier(_s, _I, P);
_server = new Srp6Server();
_server.Init(group, v, _digest, _random);
BigInteger B = _server.GenerateServerCredentials();
_B = B.ToByteArrayUnsigned();
return _B;
}
public bool VerifyClientEvidenceMessage(byte[] A, byte[] clientM1)
{
// BouncyCastle is using simplified calculation of M1 and M2 using only A, B and S.
//_ = _server.CalculateSecret(new BigInteger(1, A));
//return _server.VerifyClientEvidenceMessage(new BigInteger(1, clientM1));
// let's calculate it using the correct formula: M1 = H(H(N) XOR H(g) | H(I) | s | A | B | K)
_A = A;
BigInteger S = _server.CalculateSecret(new BigInteger(1, A));
_K = SHA(S.ToByteArrayUnsigned());
_calculatedM1 = SHA(CONCAT(XOR(SHA(_N), SHA(_g)), CONCAT(SHA(_I), CONCAT(_s, CONCAT(A, CONCAT(_B, _K))))));
bool result = _calculatedM1.SequenceEqual(clientM1);
return result;
}
public byte[] CalculateServerEvidenceMessage()
{
//return _server.CalculateServerEvidenceMessage().ToByteArrayUnsigned();
return SHA(CONCAT(_A, CONCAT(_calculatedM1, _K)));
}
public byte[] CalculateSessionKey()
{
//return _server.CalculateSessionKey().ToByteArrayUnsigned();
return _K;
}
private static byte[] CONCAT(byte[] a, byte[] b)
{
return a.Concat(b).ToArray();
}
private byte[] SHA(byte[] bytes)
{
_digest.Reset();
_digest.BlockUpdate(bytes, 0, bytes.Length);
byte[] rv = new byte[_digest.GetDigestSize()];
_digest.DoFinal(rv, 0);
return rv;
}
private static byte[] XOR(byte[] a, byte[] b)
{
for (int i = 0; i < a.Length; i++)
{
a[i] ^= b[i];
}
return a;
}
This issue is not just related to C#, I can see the same code in Java as well. The current BouncyCastle implementation is working only when BouncyCastle is being used on both sides - client and the server.
Metadata
Metadata
Assignees
Labels
No labels