forked from FirebirdSQL/NETProvider
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSrpClientBase.cs
More file actions
185 lines (157 loc) · 6.34 KB
/
SrpClientBase.cs
File metadata and controls
185 lines (157 loc) · 6.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/*
* The contents of this file are subject to the Initial
* Developer's Public License Version 1.0 (the "License");
* you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
* https://github.com/FirebirdSQL/NETProvider/raw/master/license.txt.
*
* Software distributed under the License is distributed on
* an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
* express or implied. See the License for the specific
* language governing rights and limitations under the License.
*
* All Rights Reserved.
*/
//$Authors = Hajime Nakagami (nakagami@gmail.com), Jiri Cincura (jiri@cincura.net)
using System;
using System.Globalization;
using System.Linq;
using System.Numerics;
using System.Security.Cryptography;
using System.Text;
using FirebirdSql.Data.Common;
namespace FirebirdSql.Data.Client.Managed.Srp;
/// <remarks>
/// http://srp.stanford.edu/design.html
/// </remarks>
abstract class SrpClientBase
{
public abstract string Name { get; }
public string SessionKeyName { get; } = "Symmetric";
private const int SRP_KEY_SIZE = 128;
private const int SRP_SALT_SIZE = 32;
private static readonly BigInteger N = BigInteger.Parse("00E67D2E994B2F900C3F41F08F5BB2627ED0D49EE1FE767A52EFCD565CD6E768812C3E1E9CE8F0A8BEA6CB13CD29DDEBF7A96D4A93B55D488DF099A15C89DCB0640738EB2CBDD9A8F7BAB561AB1B0DC1C6CDABF303264A08D1BCA932D1F1EE428B619D970F342ABA9A65793B8B2F041AE5364350C16F735F56ECBCA87BD57B29E7", NumberStyles.HexNumber);
private static readonly BigInteger g = new BigInteger(2);
private static readonly BigInteger k = BigInteger.Parse("1277432915985975349439481660349303019122249719989");
private static readonly byte[] SEPARATOR_BYTES = Encoding.UTF8.GetBytes(":");
public BigInteger PublicKey { get; } // A
public string PublicKeyHex => Pad(PublicKey).ToHexString();
public BigInteger PrivateKey { get; } // a
public byte[] Proof { get; private set; } // M
public byte[] SessionKey { get; private set; } // K
public SrpClientBase()
{
PrivateKey = GetSecret();
PublicKey = BigInteger.ModPow(g, PrivateKey, N);
}
public byte[] ClientProof(string user, string password, byte[] salt, BigInteger serverPublicKey)
{
var K = GetClientSessionKey(user, password, salt, serverPublicKey);
var n1 = BigIntegerFromByteArray(ComputeSHA1Hash(BigIntegerToByteArray(N)));
var n2 = BigIntegerFromByteArray(ComputeSHA1Hash(BigIntegerToByteArray(g)));
n1 = BigInteger.ModPow(n1, n2, N);
n2 = BigIntegerFromByteArray(ComputeSHA1Hash(Encoding.UTF8.GetBytes(user)));
var M = ComputeHash(BigIntegerToByteArray(n1), BigIntegerToByteArray(n2), salt, BigIntegerToByteArray(PublicKey), BigIntegerToByteArray(serverPublicKey), K);
SessionKey = K;
Proof = M;
return Proof;
}
public byte[] ClientProof(string user, string password, byte[] authData)
{
var saltLength = authData[0] + authData[1] * 256;
var salt = new byte[saltLength];
Array.Copy(authData, 2, salt, 0, saltLength);
var serverKeyStart = saltLength + 4;
var serverKeyLength = authData.Length - saltLength - 4;
var hexServerPublicKey = new byte[serverKeyLength];
Array.Copy(authData, serverKeyStart, hexServerPublicKey, 0, serverKeyLength);
var hexServerPublicKeyString = Encoding.UTF8.GetString(hexServerPublicKey);
var serverPublicKey = BigInteger.Parse($"00{hexServerPublicKeyString}", NumberStyles.HexNumber);
return ClientProof(user, password, salt, serverPublicKey);
}
public (BigInteger, BigInteger) ServerSeed(string user, string password, byte[] salt)
{
var v = BigInteger.ModPow(g, GetUserHash(user, password, salt), N);
var b = GetSecret();
var gb = BigInteger.ModPow(g, b, N);
BigInteger.DivRem(k * v, N, out var kv);
BigInteger.DivRem(BigInteger.Add(kv, gb), N, out var B);
return (B, b);
}
public byte[] GetServerSessionKey(string user, string password, byte[] salt, BigInteger A, BigInteger B, BigInteger b)
{
var u = GetScramble(A, B);
var v = BigInteger.ModPow(g, GetUserHash(user, password, salt), N);
var vu = BigInteger.ModPow(v, u, N);
BigInteger.DivRem(A * vu, N, out var Avu);
var sessionSecret = BigInteger.ModPow(Avu, b, N);
return ComputeSHA1Hash(BigIntegerToByteArray(sessionSecret));
}
public byte[] GetSalt()
{
return GetRandomBytes(SRP_SALT_SIZE);
}
private BigInteger GetSecret()
{
return new BigInteger(GetRandomBytes(SRP_KEY_SIZE / 8).Concat(new byte[] { 0 }).ToArray());
}
private byte[] GetClientSessionKey(string user, string password, byte[] salt, BigInteger serverPublicKey)
{
var u = GetScramble(PublicKey, serverPublicKey);
var x = GetUserHash(user, password, salt);
var gx = BigInteger.ModPow(g, x, N);
BigInteger.DivRem(k * gx, N, out var kgx);
var Bkgx = serverPublicKey - kgx;
if (Bkgx < 0)
{
Bkgx = Bkgx + N;
}
BigInteger.DivRem(Bkgx, N, out var diff);
BigInteger.DivRem(u * x, N, out var ux);
BigInteger.DivRem(PrivateKey + ux, N, out var aux);
var sessionSecret = BigInteger.ModPow(diff, aux, N);
return ComputeSHA1Hash(BigIntegerToByteArray(sessionSecret));
}
protected abstract byte[] ComputeHash(params byte[][] ba);
private static BigInteger GetUserHash(string user, string password, byte[] salt)
{
var userBytes = Encoding.UTF8.GetBytes(user);
var passwordBytes = Encoding.UTF8.GetBytes(password);
var hash1 = ComputeSHA1Hash(userBytes, SEPARATOR_BYTES, passwordBytes);
var hash2 = ComputeSHA1Hash(salt, hash1);
return BigIntegerFromByteArray(hash2);
}
private static BigInteger BigIntegerFromByteArray(byte[] b)
{
return new BigInteger(b.AsEnumerable().Reverse().Concat(new byte[] { 0 }).ToArray());
}
private static byte[] BigIntegerToByteArray(BigInteger n)
{
return n.ToByteArray().AsEnumerable().Reverse().SkipWhile((e, i) => i == 0 && e == 0).ToArray();
}
private static byte[] ComputeSHA1Hash(params byte[][] ba)
{
using (var hash = SHA1.Create())
{
return hash.ComputeHash(ba.SelectMany(x => x).ToArray());
}
}
private static byte[] Pad(BigInteger n)
{
var bn = BigIntegerToByteArray(n);
return bn.SkipWhile((_, i) => i < bn.Length - SRP_KEY_SIZE).ToArray();
}
private static BigInteger GetScramble(BigInteger x, BigInteger y)
{
return BigIntegerFromByteArray(ComputeSHA1Hash(Pad(x), Pad(y)));
}
private static byte[] GetRandomBytes(int count)
{
var result = new byte[count];
using (var random = RandomNumberGenerator.Create())
{
random.GetBytes(result);
}
return result;
}
}