Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
RsaPss.cs
1using System;
2using System.Globalization;
3using System.Numerics;
4using System.Text;
10
12{
19 public class RsaPss : RsaAlgorithm
20 {
21 private static readonly HashFunction defaultHashFunction = new Sha1();
22 private static readonly MGF1 defaultMaskGenerationFunction = new(defaultHashFunction);
23
24 private HashFunction hashFunction = defaultHashFunction;
25 private MaskGenerationFunction maskGenerationFunction = defaultMaskGenerationFunction;
26 private int saltLength = 20;
27 private int trailerField = 1;
28 private bool configured = false;
29
33 public override string Oid => "1.2.840.113549.1.1.10";
34
38 public override bool IsConfigured => this.configured;
39
45 public override bool Configure(Vector SecurityInfo)
46 {
47 if (SecurityInfo.Length < 2 ||
48 SecurityInfo[1] is not Vector RsaSsaPssParameters)
49 {
50 return false;
51 }
52
53 int c = RsaSsaPssParameters.Length;
54
55 if (c >= 1)
56 {
57 if (RsaSsaPssParameters.FirstElement is not Vector HashVector ||
58 HashVector.Length < 1)
59 {
60 return false;
61 }
62
63 if (HashVector.FirstElement is not HashFunction HashFunction)
64 {
65 if (HashVector.FirstElement is Vector v &&
66 v.Length == 1 &&
67 v.FirstElement is HashFunction HashFunction2)
68 {
69 HashFunction = HashFunction2;
70 }
71 else
72 return false;
73 }
74
75 this.hashFunction = HashFunction;
76
77 if (c >= 2)
78 {
79 if (RsaSsaPssParameters[1] is not Vector MaskGenerationFunctionVector ||
80 MaskGenerationFunctionVector.Length < 1 ||
81 MaskGenerationFunctionVector.FirstElement is not MaskGenerationFunction MaskGenerationFunction)
82 {
83 return false;
84 }
85
86 this.maskGenerationFunction = MaskGenerationFunction;
87 }
88
89 if (c >= 3)
90 {
91 if (RsaSsaPssParameters[2] is not Vector SaltLengthVector ||
92 SaltLengthVector.Length < 1 ||
93 SaltLengthVector.FirstElement is not BigInteger SaltLength ||
94 SaltLength < int.MinValue ||
95 SaltLength > int.MaxValue)
96 {
97 return false;
98 }
99
100 this.saltLength = (int)SaltLength;
101
102 if (c >= 4)
103 {
104 if (RsaSsaPssParameters[3] is not Vector TrailerFieldVector ||
105 TrailerFieldVector.Length < 1 ||
106 TrailerFieldVector.FirstElement is not BigInteger TrailerField ||
107 TrailerField < int.MinValue ||
108 TrailerField > int.MaxValue)
109 {
110 return false;
111 }
112
113 this.trailerField = (int)TrailerField;
114
115 if (c > 4)
116 return false;
117 }
118 }
119 }
120
121 this.configured = true;
122
123 return true;
124 }
125
129 public override Waher.Security.HashFunctionArray HashAlgorithm => this.hashFunction.ComputeHash;
130
134 public override string HashAlgorithmOid => this.hashFunction.Oid;
135
144 public override bool VerifySignature(byte[] Data, byte[] Signature, IPublicKey PublicKey,
145 ICommunicationLayer? Client)
146 {
148 {
149 Client?.Error("Unable to get RSA public key from certificate.");
150 return false;
151 }
152
153 int Bits = (int)RsaPublicKey.Modulus.GetBitLength();
154
155 ASN1.ReportAlgorithmUse("RSA-PSS-" + Bits.ToString(CultureInfo.InvariantCulture));
156
157 BigInteger S = EllipticCurve.ToInt(Signature, true);
158
159 return Verify(this.hashFunction, this.maskGenerationFunction,
160 RsaPublicKey.Modulus, RsaPublicKey.Exponent, Data, S, this.saltLength, Client);
161 }
162
175 public static bool Verify(HashFunction H, MaskGenerationFunction Mgf, BigInteger n,
176 BigInteger e, byte[] Message, BigInteger S, int SaltLen, ICommunicationLayer? Client)
177 {
178 // Encoded Message EM = S^e mod n
179
180 ModulusP ModN = new(n); // n not a prime, so not a Field (i.e. has zero-divisors), but addition and multiplication mod n work.
181 BigInteger EM = 1;
182 bool HasSniffer = Client?.HasSniffers ?? false;
183 StringBuilder? Msg = HasSniffer ? new StringBuilder() : null;
184
185 if (HasSniffer)
186 {
187 Msg!.AppendLine("RSS-PSS signature verification parameters:");
188 Msg.Append("n (Modulus, dec): ");
189 Msg.AppendLine(n.ToString(CultureInfo.InvariantCulture));
190 Msg.Append("e (Exponent, dec): ");
191 Msg.AppendLine(e.ToString(CultureInfo.InvariantCulture));
192 Msg.Append("S (Signature, dec): ");
193 Msg.AppendLine(S.ToString(CultureInfo.InvariantCulture));
194 Msg.Append("Salt length (dec): ");
195 Msg.AppendLine(SaltLen.ToString(CultureInfo.InvariantCulture));
196 Msg.Append("Hash function: ");
197 Msg.AppendLine(H.ToString());
198 Msg.Append("MGF function: ");
199 Msg.AppendLine(Mgf.ToString());
200 Msg.Append("Message (hex): ");
201 Msg.AppendLine(Waher.Security.Hashes.BinaryToString(Message));
202 }
203
204 while (!e.IsZero)
205 {
206 if (!e.IsEven)
207 EM = ModN.Multiply(EM, S);
208
209 e >>= 1;
210 S = ModN.Multiply(S, S);
211 }
212
213 byte[] EMBin = EM.ToByteArray(true, true);
214 int EMLen = (int)(n.GetBitLength() + 7) / 8;
215
216 if (EMBin.Length < EMLen)
217 {
218 byte[] EMBin2 = new byte[EMLen];
219 Buffer.BlockCopy(EMBin, 0, EMBin2, EMLen - EMBin.Length, EMBin.Length);
220 EMBin = EMBin2;
221 }
222
223 if (HasSniffer)
224 {
225 Msg!.Append("EM = S^e (hex): ");
226 Msg.AppendLine(Waher.Security.Hashes.BinaryToString(EMBin));
227 }
228
229 if (EMBin[^1] != 0xbc)
230 {
231 if (HasSniffer)
232 {
233 Client!.Information(Msg!.ToString());
234 Client.Error("S^e does not end with BC. Invalid signature.");
235 }
236
237 return false;
238 }
239
240 int HLen = H.HashLength;
241 int MaskedDBLen = EMBin.Length - 1 - HLen;
242 byte[] MaskedDB = new byte[MaskedDBLen];
243 byte[] HashDigest = new byte[HLen];
244
245 Buffer.BlockCopy(EMBin, 0, MaskedDB, 0, MaskedDBLen);
246 Buffer.BlockCopy(EMBin, MaskedDBLen, HashDigest, 0, HLen);
247
248 if (HasSniffer)
249 {
250 Msg!.Append("Masked DB (hex): ");
251 Msg.AppendLine(Waher.Security.Hashes.BinaryToString(MaskedDB));
252 Msg!.Append("Hash Digest 1 (hex): ");
253 Msg.AppendLine(Waher.Security.Hashes.BinaryToString(HashDigest));
254 }
255
256 byte[] DBMask = Mgf.CalculateMask(HashDigest, MaskedDBLen);
257
258 if (HasSniffer)
259 {
260 Msg!.Append("DB Mask (hex): ");
261 Msg.AppendLine(Waher.Security.Hashes.BinaryToString(DBMask));
262 }
263
264 byte[] DB = TravelDocumentsClient.XOR(MaskedDB, DBMask);
265
266 DB[0] &= 0x7f; // MSB can be 1, as EMBits=bitlen(n)-1
267
268 if (HasSniffer)
269 {
270 Msg!.Append("DB (hex): ");
271 Msg.AppendLine(Waher.Security.Hashes.BinaryToString(DB));
272 }
273
274 int i = 0;
275 int c = DB.Length;
276
277 while (i < c && DB[i] == 0)
278 i++;
279
280 if (i >= c ||
281 DB[i++] != 1 ||
282 c - i != SaltLen)
283 {
284 if (HasSniffer)
285 {
286 Client!.Information(Msg!.ToString());
287 Client.Error("DB invalid. Invalid signature.");
288 }
289
290 return false;
291 }
292
293 byte[] Digest = H.ComputeHash(Message);
294 byte[] H2 = new byte[8 + HLen + SaltLen];
295
296 Buffer.BlockCopy(Digest, 0, H2, 8, HLen);
297 Buffer.BlockCopy(DB, i, H2, 8 + HLen, SaltLen);
298
299 Digest = H.ComputeHash(H2);
300
301 if (HasSniffer)
302 {
303 Msg!.Append("Hash Digest 2 (hex): ");
304 Msg.AppendLine(Waher.Security.Hashes.BinaryToString(Digest));
305 }
306
307 for (i = 0; i < HLen; i++)
308 {
309 if (Digest[i] != HashDigest[i])
310 {
311 if (HasSniffer)
312 {
313 Client!.Information(Msg!.ToString());
314 Client.Error("Hash digests do not match. Invalid signature.");
315 }
316
317 return false;
318 }
319 }
320
321 if (HasSniffer)
322 Client!.Information(Msg!.ToString());
323
324 return true;
325 }
326
327 }
328}
Static class for parsing and decoding security objects encoded using Abstract Syntax Notation 1 (ASN....
Definition: ASN1.cs:22
static int ReportAlgorithmUse(string Name)
Records an Elliptic Curve has been used.
Definition: ASN1.cs:420
abstract int HashLength
Number of bytes used for the hash digest.
Definition: HashFunction.cs:49
abstract byte[] ComputeHash(byte[] Data)
Computes a Hash Digest from binary data.
abstract byte[] CalculateMask(byte[] Seed, int Length)
Calcaultes a mask of a specific length, given a seed.
The RSA Public Key object does not have an OID of its own. Instead, it uses the OID of rsaEncryption ...
Definition: RsaPublicKey.cs:12
abstract string Oid
OID identifying the type of object.
RSASSA-PSS algorithm, as defined in RFCs 3447, 4055 and 4056: https://www.rfc-editor....
Definition: RsaPss.cs:20
override bool IsConfigured
If the object has been configured.
Definition: RsaPss.cs:38
override string Oid
OID identifying the type of object.
Definition: RsaPss.cs:33
static bool Verify(HashFunction H, MaskGenerationFunction Mgf, BigInteger n, BigInteger e, byte[] Message, BigInteger S, int SaltLen, ICommunicationLayer? Client)
Verifies an RSA-PSS signature.
Definition: RsaPss.cs:175
override Waher.Security.HashFunctionArray HashAlgorithm
Hash algorithm to use.
Definition: RsaPss.cs:129
override bool VerifySignature(byte[] Data, byte[] Signature, IPublicKey PublicKey, ICommunicationLayer? Client)
Verifies a digital signature.
Definition: RsaPss.cs:144
override string HashAlgorithmOid
OID of Hash algorithm to use.
Definition: RsaPss.cs:134
override bool Configure(Vector SecurityInfo)
If the object can be configured by the security information provided.
Definition: RsaPss.cs:45
Abstract base class for elliptic curves.
static BigInteger ToInt(byte[] Binary, bool BigEndian)
Converts a little-endian binary representation of a big integer to a BigInteger.
Integer arithmetic, modulus a prime.
Definition: ModulusP.cs:10
BigInteger Multiply(BigInteger a, BigInteger b)
Multiplies two numbers, modulus p
Definition: ModulusP.cs:80
Contains methods for simple hash calculations.
Definition: Hashes.cs:57
static string BinaryToString(byte[] Data)
Converts an array of bytes to a string with their hexadecimal representations (in lower case).
Definition: Hashes.cs:63
void Information(string Comment)
Called to inform the viewer of something.
void Error(string Error)
Called to inform the viewer of an error state.
Interface for observable classes implementing communication protocols.
bool HasSniffers
If there are sniffers registered on the object.
Definition: App.xaml.cs:4