Skip to content

Commit 142b4b8

Browse files
committed
Fixed SrpParameters thread safety issue, close #3.
1 parent 6afc478 commit 142b4b8

11 files changed

+79
-27
lines changed

src/srp.tests/SrpAuthenticationTests.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
using System;
2+
using System.Linq;
23
using System.Security.Cryptography;
34
using NUnit.Framework;
45

6+
#if !NET_35
7+
using System.Threading.Tasks;
8+
#endif
9+
510
namespace SecureRemotePassword.Tests
611
{
712
/// <summary>
@@ -123,5 +128,41 @@ private void SrpAuthentication(string username, string password, SrpParameters p
123128
throw;
124129
}
125130
}
131+
132+
#if !NET_35
133+
[Test]
134+
public async Task ParallelAuthenticationTest()
135+
{
136+
var username = "demo";
137+
var password = "insecure";
138+
var parameters = new SrpParameters();
139+
var server = new SrpServer(parameters);
140+
141+
// spawn multiple parallel threads reusing the same SrpParameters instance
142+
var tasks = Enumerable.Range(0, 100).Select(i => Task.Run(() =>
143+
{
144+
var client = new SrpClient(parameters);
145+
146+
// sign up
147+
var salt = client.GenerateSalt();
148+
var privateKey = client.DerivePrivateKey(salt, username, password);
149+
var verifier = client.DeriveVerifier(privateKey);
150+
151+
// authenticate
152+
var clientEphemeral = client.GenerateEphemeral();
153+
var serverEphemeral = server.GenerateEphemeral(verifier);
154+
var clientSession = client.DeriveSession(clientEphemeral.Secret, serverEphemeral.Public, salt, username, privateKey);
155+
156+
var serverSession = server.DeriveSession(serverEphemeral.Secret, clientEphemeral.Public, salt, username, verifier, clientSession.Proof);
157+
client.VerifySession(clientEphemeral.Public, clientSession, serverSession.Proof);
158+
159+
// make sure both the client and the server have the same session key
160+
Assert.AreEqual(clientSession.Key, serverSession.Key);
161+
}));
162+
163+
await Task.WhenAll(tasks);
164+
}
165+
#endif
166+
126167
}
127168
}

src/srp.tests/SrpParametersTests.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ public void SrpParameterDefaultsAreReasonable()
1919
[Test]
2020
public void SrpParametersCanUseCustomHashAlgorithm()
2121
{
22-
var hasher = MD5.Create();
23-
var parameters = new SrpParameters(hasher);
22+
var parameters = new SrpParameters(MD5.Create);
2423
Assert.NotNull(parameters.Prime);
2524
Assert.NotNull(parameters.Generator);
2625
Assert.AreEqual(16, parameters.HashSizeBytes);

src/srp.tests/TestVectorSet.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public SrpParameters CreateParameters()
8989

9090
// convert size in bits to padded length in chars
9191
var paddedLength = Size / 4;
92-
return new SrpParameters(hasher, N, g, paddedLength);
92+
return new SrpParameters(CreateHasher, N, g, paddedLength);
9393
}
9494
}
9595
}

src/srp.tests/srp.tests.csproj

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,19 @@
1818
<ProjectReference Include="..\srp\srp.csproj" />
1919
</ItemGroup>
2020

21+
<PropertyGroup Condition=" '$(TargetFramework)' == 'net35' ">
22+
<DefineConstants>$(DefineConstants);NET_35</DefineConstants>
23+
</PropertyGroup>
24+
2125
<PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp1.0' ">
2226
<DefineConstants>$(DefineConstants);NETCOREAPP_10</DefineConstants>
2327
</PropertyGroup>
2428

2529
<ItemGroup>
26-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.2" />
27-
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
28-
<PackageReference Include="NUnit" Version="3.10.1" />
29-
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
30+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
31+
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
32+
<PackageReference Include="NUnit" Version="3.11.0" />
33+
<PackageReference Include="NUnit3TestAdapter" Version="3.11.2" />
3034
</ItemGroup>
3135

3236
<ItemGroup>

src/srp/SrpClient.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ internal SrpInteger ComputeU(SrpInteger A, SrpInteger B)
125125
/// </summary>
126126
/// <param name="a">Client secret ephemeral value.</param>
127127
/// <param name="B">Server public ephemeral value.</param>
128-
/// <param name="u">The computed value of u</param>
128+
/// <param name="u">The computed value of u.</param>
129129
/// <param name="x">The private key.</param>
130130
internal SrpInteger ComputeS(SrpInteger a, SrpInteger B, SrpInteger u, SrpInteger x)
131131
{
@@ -195,7 +195,7 @@ public SrpSession DeriveSession(string clientSecretEphemeral, string serverPubli
195195
return new SrpSession
196196
{
197197
Key = K.ToHex(),
198-
Proof = M1.ToHex()
198+
Proof = M1.ToHex(),
199199
};
200200
}
201201

src/srp/SrpConstants.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ 94B5C803 D89F7AE4 35DE236D 525F5475 9B65E372 FCD68EF2 0FA7111F
6464
/// Safe prime number, 3072-bit group.
6565
/// </summary>
6666
/// <remarks>
67-
/// This prime is: 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 }
67+
/// This prime is: 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 }.
6868
/// </remarks>
6969
public const string SafePrime3072 = @"
7070
FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08
@@ -91,7 +91,7 @@ BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC
9191
/// Safe prime number, 4096-bit group.
9292
/// </summary>
9393
/// <remarks>
94-
/// This prime is: 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 }
94+
/// This prime is: 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 }.
9595
/// </remarks>
9696
public const string SafePrime4096 = @"
9797
FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08
@@ -123,7 +123,7 @@ D5B05AA9 93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34063199
123123
/// Safe prime number, 6144-bit group.
124124
/// </summary>
125125
/// <remarks>
126-
/// This prime is: 2^6144 - 2^6080 - 1 + 2^64 * { [2^6014 pi] + 929484 }
126+
/// This prime is: 2^6144 - 2^6080 - 1 + 2^64 * { [2^6014 pi] + 929484 }.
127127
/// </remarks>
128128
public const string SafePrime6144 = @"
129129
FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08
@@ -164,7 +164,7 @@ 387FE8D7 6E3C0468 043E8F66 3F4860EE 12BF2D5B 0B7474D6 E694F91E
164164
/// Safe prime number, 8192-bit group.
165165
/// </summary>
166166
/// <remarks>
167-
/// This prime is: 2^8192 - 2^8128 - 1 + 2^64 * { [2^8062 pi] + 4743158 }
167+
/// This prime is: 2^8192 - 2^8128 - 1 + 2^64 * { [2^8062 pi] + 4743158 }.
168168
/// </remarks>
169169
public const string SafePrime8192 = @"
170170
FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08

src/srp/SrpHash.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,17 @@ public class SrpHash : ISrpHash
1414
/// <summary>
1515
/// Initializes a new instance of the <see cref="SrpHash"/> class.
1616
/// </summary>
17-
/// <param name="hasher">The hashing algorithm.</param>
17+
/// <param name="hasherFactory">The hashing algorithm factory method.</param>
1818
/// <param name="algorithmName">The name of the algorithm.</param>
19-
public SrpHash(HashAlgorithm hasher, string algorithmName = null)
19+
public SrpHash(Func<HashAlgorithm> hasherFactory, string algorithmName = null)
2020
{
21-
Hasher = hasher;
21+
HasherFactory = hasherFactory;
2222
AlgorithmName = algorithmName ?? Hasher.GetType().Name;
2323
}
2424

25-
private HashAlgorithm Hasher { get; }
25+
private Func<HashAlgorithm> HasherFactory { get; }
26+
27+
private HashAlgorithm Hasher => HasherFactory();
2628

2729
/// <summary>
2830
/// Computes the hash of the specified <see cref="string"/> or <see cref="SrpInteger"/> values.

src/srp/SrpHashT.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ public class SrpHash<T> : SrpHash
1313
/// Initializes a new instance of the <see cref="SrpHash{T}"/> class.
1414
/// </summary>
1515
public SrpHash()
16-
: base(CreateHasher())
16+
: base(CreateHasher)
1717
{
1818
}
1919

2020
/// <summary>
2121
/// Creates the hasher of the given type <typeparamref name="T"/>.
2222
/// </summary>
23-
public static T CreateHasher() => (T)CreateHasher(typeof(T).Name);
23+
public static HashAlgorithm CreateHasher() => CreateHasher(typeof(T).Name);
2424
}
2525
}

src/srp/SrpParameters.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,16 @@ public class SrpParameters
1818
/// <summary>
1919
/// Initializes a new instance of the <see cref="SrpParameters"/> class.
2020
/// </summary>
21-
/// <param name="hashAlgorithm">The hashing algorithm.</param>
21+
/// <param name="hashAlgorithmFactory">The hashing algorithm factory.</param>
2222
/// <param name="largeSafePrime">Large safe prime number N (hexadecimal).</param>
2323
/// <param name="generator">The generator value modulo N (hexadecimal).</param>
2424
/// <param name="paddedLength">The hexadecimal length of N and g.</param>
25-
public SrpParameters(HashAlgorithm hashAlgorithm = null, string largeSafePrime = null, string generator = null, int? paddedLength = null)
25+
public SrpParameters(Func<HashAlgorithm> hashAlgorithmFactory = null, string largeSafePrime = null, string generator = null, int? paddedLength = null)
2626
{
2727
Prime = SrpInteger.FromHex(largeSafePrime ?? SrpConstants.SafePrime2048);
2828
Generator = SrpInteger.FromHex(generator ?? SrpConstants.Generator2048);
2929
PaddedLength = paddedLength ?? Prime.HexLength.Value;
30-
Hasher = hashAlgorithm != null ? new SrpHash(hashAlgorithm) : new SrpHash<SHA256>();
30+
Hasher = hashAlgorithmFactory != null ? new SrpHash(hashAlgorithmFactory) : new SrpHash<SHA256>();
3131
Pad = i => i.Pad(PaddedLength);
3232
}
3333

@@ -43,7 +43,7 @@ public static SrpParameters Create<T>(string largeSafePrime = null, string gener
4343
{
4444
var result = new SrpParameters
4545
{
46-
Hasher = new SrpHash<T>()
46+
Hasher = new SrpHash<T>(),
4747
};
4848

4949
if (largeSafePrime != null)

src/srp/SrpServer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ internal SrpInteger ComputeB(string verifier, SrpInteger b)
6464
/// </summary>
6565
/// <param name="A">Client public ephemeral value.</param>
6666
/// <param name="b">Server secret ephemeral value.</param>
67-
/// <param name="u">The computed value of u</param>
67+
/// <param name="u">The computed value of u.</param>
6868
/// <param name="v">The verifier.</param>
6969
internal SrpInteger ComputeS(SrpInteger A, SrpInteger b, SrpInteger u, SrpInteger v)
7070
{

src/srp/srp.csproj

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
<Description>SRP-6a protocol implementation for .NET Standard 2.0 and .NET Framework 3.5+</Description>
55
<Copyright>Copyright © 2018 Alexey Yakovlev</Copyright>
66
<AssemblyTitle>srp</AssemblyTitle>
7-
<Version>1.0.2</Version>
7+
<Version>1.0.3</Version>
88
<AssemblyVersion>1.0.0.0</AssemblyVersion>
9-
<FileVersion>1.0.2.0</FileVersion>
9+
<FileVersion>1.0.3.0</FileVersion>
1010
<Authors>Alexey Yakovlev</Authors>
1111
<TargetFrameworks>net35;net40;net45;netstandard1.6;netstandard2.0</TargetFrameworks>
1212
<GenerateDocumentationFile>true</GenerateDocumentationFile>
@@ -24,6 +24,9 @@
2424
<RootNamespace>SecureRemotePassword</RootNamespace>
2525
<PackageReleaseNotes>What's new:
2626

27+
v1.0.3:
28+
- Fixed SrpParameters thread safety issue.
29+
2730
v1.0.2:
2831
- Fixed sha384/sha512 support in .NET Standard 1.6 version.
2932

@@ -38,7 +41,10 @@
3841
</PropertyGroup>
3942

4043
<ItemGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
41-
<PackageReference Include="StyleCop.Analyzers" Version="1.0.2" />
44+
<PackageReference Include="StyleCop.Analyzers" Version="1.1.1-beta.61">
45+
<PrivateAssets>all</PrivateAssets>
46+
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
47+
</PackageReference>
4248
</ItemGroup>
4349

4450
<ItemGroup Condition=" '$(TargetFramework)' != 'net35'">

0 commit comments

Comments
 (0)