diff --git a/src/srp.tests/SrpAuthenticationTests.cs b/src/srp.tests/SrpAuthenticationTests.cs index 0ecc854..cbad834 100644 --- a/src/srp.tests/SrpAuthenticationTests.cs +++ b/src/srp.tests/SrpAuthenticationTests.cs @@ -158,6 +158,17 @@ public async Task ParallelAuthenticationTest() // make sure both the client and the server have the same session key Assert.AreEqual(clientSession.Key, serverSession.Key); + + // verify padded length of all parameters + Assert.AreEqual(verifier.Length, parameters.PaddedLength); + Assert.AreEqual(clientEphemeral.Public.Length, parameters.PaddedLength); + Assert.AreEqual(clientEphemeral.Secret.Length, parameters.HashSizeBytes * 2); + Assert.AreEqual(serverEphemeral.Public.Length, parameters.PaddedLength); + Assert.AreEqual(serverEphemeral.Secret.Length, parameters.HashSizeBytes * 2); + Assert.AreEqual(serverSession.Key.Length, parameters.HashSizeBytes * 2); + Assert.AreEqual(clientSession.Key.Length, parameters.HashSizeBytes * 2); + Assert.AreEqual(serverSession.Proof.Length, parameters.HashSizeBytes * 2); + Assert.AreEqual(clientSession.Proof.Length, parameters.HashSizeBytes * 2); })); await Task.WhenAll(tasks); diff --git a/src/srp.tests/SrpIntegerTests.cs b/src/srp.tests/SrpIntegerTests.cs index 087d9c6..63c43f5 100644 --- a/src/srp.tests/SrpIntegerTests.cs +++ b/src/srp.tests/SrpIntegerTests.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using System.Security.Cryptography; using NUnit.Framework; @@ -82,7 +83,7 @@ public void SrpIntegerSubtract() public void SrpIntegerMultiply() { var result = SrpInteger.FromHex("CAFE") * SrpInteger.FromHex("babe"); - Assert.AreEqual("94133484", result.ToHex()); + Assert.AreEqual("94133484", result.ToHex(8)); } [Test] @@ -95,7 +96,7 @@ public void SrpIntegerDivide() [Test] public void SrpIntegerModulo() { - var result = SrpInteger.FromHex("10") % SrpInteger.FromHex("9"); + var result = SrpInteger.FromHex("10") % SrpInteger.FromHex("09"); Assert.AreEqual("07", result.ToHex()); } @@ -253,5 +254,42 @@ public void RandomIntegerReturnsAnIntegerOfTheGivenSize() Assert.AreEqual(16, rnd.ToHex().Length); Assert.AreNotEqual("0000000000000000", rnd.ToHex()); } + + [Test] + public void ArithmeticOperationsKeepTheLargestHexSizeOfOperands() + { + var left = SrpInteger.FromHex("12345"); + var right = SrpInteger.FromHex("1234567890"); + var operations = new Func[] + { + (a, b) => a + b, + (a, b) => a - b, + (a, b) => a / b, + (a, b) => a ^ b, + }; + + Assert.Multiple(() => + { + foreach (var op in operations) + { + Assert.AreEqual(10, op(left, right).HexLength); + Assert.AreEqual(10, op(right, left).HexLength); + } + }); + + // multiplication loses hex widths + Assert.IsNull((left * right).HexLength); + Assert.IsNull((right * left).HexLength); + + // mod gets the width of the modulus + Assert.AreEqual(10, (left % right).HexLength); + Assert.AreEqual(5, (right % left).HexLength); + + // modPow gets the width of the modulus + Assert.AreEqual(5, left.ModPow(right, left).HexLength); + Assert.AreEqual(5, right.ModPow(left, left).HexLength); + Assert.AreEqual(10, left.ModPow(right, right).HexLength); + Assert.AreEqual(10, right.ModPow(left, right).HexLength); + } } } diff --git a/src/srp.tests/SrpServerTests.cs b/src/srp.tests/SrpServerTests.cs index 702545f..05c14a3 100644 --- a/src/srp.tests/SrpServerTests.cs +++ b/src/srp.tests/SrpServerTests.cs @@ -24,6 +24,16 @@ public void SrpServerGeneratesEphemeralValue() Assert.IsTrue(ephemeral.Secret.Length < ephemeral.Public.Length); } + [Test] + public void SrpServerEphemeralValueWidthEqualsToPaddedLength() + { + var server = new SrpServer(); + var verifier = "a113f1b3ed0aed0bf3ec043b80eb8a5a983b201237b2d14bf59b6eaa09b3bc8c9b8371368974395bef93b2beba1ca95fd0eec768915fcb7725fba212850e285d92e3d89995dad4fbc430bed66f1049a7a95aea09a3a3e7a797152135d6e881ac8f703b886e6ba9f68fc8a304b1670b2b8adb109932fa9c642810598ef45d60a73b5e0fcc11c1fcc3989d2254a2407d7ffea3c4dc7c3511baa2137f4b2e9226bfd082e1acfe03d3a944810c41cbf633220cadb107fef709075b46796a1e632dd9aa075e40e4619998ef26e1c09238839883cdf3eee3804a82ca4bc3cd31bdf9c8cd2535511dcbc00491e5fe93e155321c7b686bda115b92a2d7bb5fb01a51355e"; + var secret = "ddfddf4060e11ef3f5389b8be0ff4f5133f8958aa0dafb0e4ad65949cb5c723d"; + var ephemeral = server.ComputeB(verifier, SrpInteger.FromHex(secret)).ToHex(); + Assert.IsTrue(ephemeral.Length == server.Parameters.PaddedLength); + } + [Test] public void SrpServerDeriveSession() { diff --git a/src/srp/SrpInteger.cs b/src/srp/SrpInteger.cs index dd488b7..93f174a 100644 --- a/src/srp/SrpInteger.cs +++ b/src/srp/SrpInteger.cs @@ -71,6 +71,8 @@ private static string NormalizeWhitespace(string hexNumber) => HexLength = newLength, }; + internal static int Max(params int?[] values) => values.Max(v => v ?? 0); + /// /// Generates the random integer number. /// @@ -119,9 +121,11 @@ public SrpInteger ModPow(SrpInteger exponent, SrpInteger modulus) /// /// Returns the fixed-length hexadecimal representation of the instance. /// - public string ToHex() + /// Custom hexadecimal length (optional) + public string ToHex(int? hexLength = null) { - if (!HexLength.HasValue) + hexLength = HexLength ?? hexLength; + if (!hexLength.HasValue) { throw new InvalidOperationException("Hexadecimal length is not specified"); } @@ -135,7 +139,7 @@ public string ToHex() } // ToString may add extra leading zeros to the positive BigIntegers, so we trim them first - return sign + value.ToString("x").TrimStart('0').PadLeft(HexLength.Value, '0'); + return sign + value.ToString("x").TrimStart('0').PadLeft(hexLength.Value, '0'); } /// @@ -215,7 +219,7 @@ public byte[] ToByteArray() return new SrpInteger { Value = left.Value - right.Value, - HexLength = left.HexLength, + HexLength = Max(left.HexLength, right.HexLength), }; } @@ -229,7 +233,7 @@ public byte[] ToByteArray() return new SrpInteger { Value = left.Value + right.Value, - HexLength = left.HexLength, + HexLength = Max(left.HexLength, right.HexLength), }; } @@ -243,7 +247,7 @@ public byte[] ToByteArray() return new SrpInteger { Value = dividend.Value / divisor.Value, - HexLength = dividend.HexLength, + HexLength = Max(dividend.HexLength, divisor.HexLength), }; } @@ -251,13 +255,13 @@ public byte[] ToByteArray() /// Implements the operator %. /// /// The dividend. - /// The divisor. - public static SrpInteger operator %(SrpInteger dividend, SrpInteger divisor) + /// The modulus. + public static SrpInteger operator %(SrpInteger dividend, SrpInteger modulus) { return new SrpInteger { - Value = dividend.Value % divisor.Value, - HexLength = dividend.HexLength, + Value = dividend.Value % modulus.Value, + HexLength = modulus.HexLength, }; } @@ -271,7 +275,7 @@ public byte[] ToByteArray() return new SrpInteger { Value = left.Value * right.Value, - HexLength = left.HexLength, + HexLength = null, // the padding is lost }; } @@ -285,7 +289,7 @@ public byte[] ToByteArray() return new SrpInteger { Value = left.Value ^ right.Value, - HexLength = left.HexLength, + HexLength = Max(left.HexLength, right.HexLength), }; } diff --git a/src/srp/srp.csproj b/src/srp/srp.csproj index 6c21737..7188464 100644 --- a/src/srp/srp.csproj +++ b/src/srp/srp.csproj @@ -25,16 +25,16 @@ What's new: v1.0.3: - - Fixed SrpParameters thread safety issue. + — Fixed SrpParameters thread safety issue. v1.0.2: - - Fixed sha384/sha512 support in .NET Standard 1.6 version. + — Fixed sha384/sha512 support in .NET Standard 1.6 version. v1.0.1: - - Enabled .NET Standard 1.6 support. + — Enabled .NET Standard 1.6 support. v1.0.0: - - Initial release. + — Initial release. ..\srp.ruleset ..\..\bin\