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);