diff --git a/SrpClient.cs b/SrpClient.cs index fe72f91..d0308f1 100644 --- a/SrpClient.cs +++ b/SrpClient.cs @@ -110,10 +110,12 @@ public SrpSession DeriveSession(string clientSecretEphemeral, string serverPubli // g — A generator modulo N // k — Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6) // H — One-way hash function + // PAD — Pad the number to have the same number of bytes as N var N = Parameters.N; var g = Parameters.G; var k = Parameters.K; var H = Parameters.H; + var PAD = Parameters.PAD; // a — Secret ephemeral value // B — Public ephemeral value @@ -136,8 +138,8 @@ public SrpSession DeriveSession(string clientSecretEphemeral, string serverPubli throw new SecurityException("The server sent an invalid public ephemeral"); } - // u = H(A, B) - var u = H(A, B); + // u = H(PAD(A), PAD(B)) + var u = H(PAD(A), PAD(B)); // S = (B - kg^x) ^ (a + ux) var S = (B - (k * (g.ModPow(x, N)))).ModPow(a + (u * x), N); diff --git a/SrpInteger.cs b/SrpInteger.cs index 1459dbb..86a5a11 100644 --- a/SrpInteger.cs +++ b/SrpInteger.cs @@ -51,6 +51,9 @@ private static string NormalizeWhitespace(string hexNumber) => /// public static SrpInteger Zero { get; } = new SrpInteger(); + /// + /// Gets or sets the value. + /// private BigInteger Value { get; set; } /// @@ -58,6 +61,16 @@ private static string NormalizeWhitespace(string hexNumber) => /// internal int? HexLength { get; private set; } + /// + /// Pads the value to the specified new hexadecimal length. + /// + /// The new length. + public SrpInteger Pad(int newLength) => new SrpInteger + { + Value = Value, + HexLength = newLength, + }; + /// /// Generates the random integer number. /// diff --git a/SrpParameters.cs b/SrpParameters.cs index 720e5ac..f28a766 100644 --- a/SrpParameters.cs +++ b/SrpParameters.cs @@ -1,4 +1,5 @@ -using System.Security.Cryptography; +using System; +using System.Security.Cryptography; namespace Zyan.Communication.Security.SecureRemotePassword { @@ -14,7 +15,9 @@ public SrpParameters() { N = SrpInteger.FromHex(LargeSafePrime); G = SrpInteger.FromHex("02"); + PaddedLength = N.HexLength.Value; Hasher = new SrpHash(); + PAD = i => i.Pad(PaddedLength); } /// @@ -23,8 +26,8 @@ public SrpParameters() /// /// Large safe prime number N (hexadecimal). /// The generator value modulo N (hexadecimal). - /// If true, pad generator to the same length as N, as required by RFC5054 (incompatible with secure-remote-password npm module). - public static SrpParameters Create(string largeSafePrime = null, string generator = null, bool padGenerator = false) + /// The hexadecimal length of N and g. + public static SrpParameters Create(string largeSafePrime = null, string generator = null, int? paddedLength = null) where T : HashAlgorithm { var result = new SrpParameters @@ -35,6 +38,7 @@ public static SrpParameters Create(string largeSafePrime = null, string gener if (largeSafePrime != null) { result.N = SrpInteger.FromHex(largeSafePrime); + result.PaddedLength = result.N.HexLength.Value; } if (generator != null) @@ -42,9 +46,9 @@ public static SrpParameters Create(string largeSafePrime = null, string gener result.G = SrpInteger.FromHex(generator); } - if (padGenerator) + if (paddedLength.HasValue) { - result.G = new SrpInteger(result.G, result.N.HexLength); + result.PaddedLength = paddedLength.Value; } return result; @@ -63,6 +67,11 @@ 03CE5329 9CCC041C 7BC308D8 2A5698F3 A8D0C382 71AE35F8 E9DBFBB6 94B5C803 D89F7AE4 35DE236D 525F5475 9B65E372 FCD68EF2 0FA7111F 9E4AFF73"; + /// + /// Gets or sets the length of the padded N and g values. + /// + public int PaddedLength { get; set; } + /// /// Gets or sets the large safe prime number (N = 2q+1, where q is prime). /// @@ -83,15 +92,20 @@ 94B5C803 D89F7AE4 35DE236D 525F5475 9B65E372 FCD68EF2 0FA7111F /// public SrpHash H => Hasher.HashFunction; + /// + /// Pads the specified integer value. + /// + public Func PAD { get; } + /// /// Gets the hash size in bytes. /// public int HashSizeBytes => Hasher.HashSizeBytes; /// - /// Gets the multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6). + /// Gets the multiplier parameter: k = H(N, g) in SRP-6a (k = 3 for legacy SRP-6). /// - public SrpInteger K => H(N, G); + public SrpInteger K => H(N, PAD(G)); /// public override string ToString() => $"SrpParameters.Create<{Hasher.AlgorithmName}>(\"{N.ToHex()}\", \"{G.ToHex()}\")"; diff --git a/SrpServer.cs b/SrpServer.cs index 646f4b3..1f825bd 100644 --- a/SrpServer.cs +++ b/SrpServer.cs @@ -64,10 +64,12 @@ public SrpSession DeriveSession(string serverSecretEphemeral, string clientPubli // g — A generator modulo N // k — Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6) // H — One-way hash function + // PAD — Pad the number to have the same number of bytes as N var N = Parameters.N; var g = Parameters.G; var k = Parameters.K; var H = Parameters.H; + var PAD = Parameters.PAD; // b — Secret ephemeral values // A — Public ephemeral values @@ -91,8 +93,8 @@ public SrpSession DeriveSession(string serverSecretEphemeral, string clientPubli throw new SecurityException("The client sent an invalid public ephemeral"); } - // u = H(A, B) - var u = H(A, B); + // u = H(PAD(A), PAD(B)) + var u = H(PAD(A), PAD(B)); // S = (Av^u) ^ b (computes session key) var S = (A * v.ModPow(u, N)).ModPow(b, N);