Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
CMac.cs
1using System;
3
5{
10 public sealed class CMac : IDisposable
11 {
12 private readonly SymmetricAlgorithm cipher;
13 private readonly byte[] key;
14 private readonly byte[] subKey1;
15 private readonly byte[] subKey2;
16 private readonly int blockSizeBits;
17 private readonly int blockSizeBytes;
18 private bool disposed = false;
19
28 private CMac(SymmetricAlgorithm Cipher, byte[] Key, byte[] K1, byte[] K2)
29 {
30 this.cipher = Cipher;
31 this.key = Key;
32 this.subKey1 = K1;
33 this.subKey2 = K2;
34 this.blockSizeBits = this.cipher.BlockSize;
35 this.blockSizeBytes = this.blockSizeBits >> 3;
36 }
37
43 public static CMac CreateAes128CMac(byte[] Key)
44 {
45 return CreateAesCMac(Key, zero16, 128, 128);
46 }
47
53 public static CMac CreateAes192CMac(byte[] Key)
54 {
55 return CreateAesCMac(Key, zero16, 128, 192);
56 }
57
63 public static CMac CreateAes256CMac(byte[] Key)
64 {
65 return CreateAesCMac(Key, zero16, 128, 256);
66 }
67
68 private static CMac CreateAesCMac(byte[] Key, byte[] Zero, int BlockSize, int KeySize)
69 {
70 if (Zero is null)
71 throw new ArgumentNullException(nameof(Zero));
72
73 int BlockByteSize = BlockSize >> 3;
74
75 if (Zero.Length != BlockByteSize)
76 throw new ArgumentException("Zero block must match block size.", nameof(Zero));
77
78 Aes Aes = Aes.Create();
79 Aes.Mode = CipherMode.CBC;
80 Aes.Padding = PaddingMode.None;
81 Aes.BlockSize = BlockSize;
82 Aes.KeySize = KeySize;
83
84 // Ref §6.1: Subkey Generation
85
86 using ICryptoTransform Encryptor = Aes.CreateEncryptor(Key, Zero);
87
88 byte[] L = Encryptor.TransformFinalBlock(Zero, 0, BlockByteSize);
89 byte[] K1, K2;
90
91 K1 = ShiftLeft(L);
92
93 if ((L[0] & 0x80) != 0)
94 K1[BlockByteSize - 1] ^= 0b10000111; // R128
95
96 K2 = ShiftLeft(K1);
97 if ((K1[0] & 0x80) != 0)
98 K2[BlockByteSize - 1] ^= 0b10000111; // R128
99
100 return new CMac(Aes, Key, K1, K2);
101 }
102
103 private static byte[] ShiftLeft(byte[] Data)
104 {
105 int c = Data.Length;
106 byte[] Result = (byte[])Data.Clone();
107 bool Carry = false;
108
109 while (c-- > 0)
110 {
111 Result[c] <<= 1;
112 if (Carry)
113 Result[c] |= 1;
114
115 Carry = (Data[c] & 0x80) != 0;
116 }
117
118 return Result;
119 }
120
124 public void Dispose()
125 {
126 if (!this.disposed)
127 {
128 this.disposed = true;
129
130 Array.Clear(this.key, 0, this.key.Length);
131 Array.Clear(this.subKey1, 0, this.subKey1.Length);
132 Array.Clear(this.subKey2, 0, this.subKey2.Length);
133
134 this.cipher.Dispose();
135 }
136 }
137
144 public byte[] Sign(byte[] Message, int Len)
145 {
146 if (Len > this.blockSizeBytes)
147 throw new ArgumentException("Requested signature length exceeds block size.", nameof(Len));
148
149 byte[] C = this.Sign(Message);
150 if (Len == this.blockSizeBytes)
151 return C;
152
153 byte[] C2 = new byte[Len];
154 Buffer.BlockCopy(C, 0, C2, 0, Len);
155
156 return C2;
157 }
158
164 public byte[] Sign(byte[] Message)
165 {
166 // See §6.2: MAC Generation
167
168 if (Message is null)
169 throw new ArgumentNullException(nameof(Message));
170
171 using ICryptoTransform Cipher = this.cipher.CreateEncryptor(this.key, zero16);
172
173 byte[] M = new byte[this.blockSizeBytes];
174 byte[] C = new byte[this.blockSizeBytes];
175 int i = 0;
176 int c = Message.Length;
177 int d;
178 int n = (c + this.blockSizeBytes - 1) / this.blockSizeBytes;
179 if (n == 0)
180 n = 1;
181
182 do
183 {
184 d = c - i;
185 if (d >= this.blockSizeBytes)
186 {
187 Buffer.BlockCopy(Message, i, M, 0, this.blockSizeBytes);
188 i += this.blockSizeBytes;
189
190 if (i >= c)
191 XorInto(M, this.subKey1);
192 }
193 else
194 {
195 Buffer.BlockCopy(Message, i, M, 0, d);
196 i += d;
197
198 M[d++] = 0x80;
199 if (d < this.blockSizeBytes)
200 Array.Clear(M, d, this.blockSizeBytes - d);
201
202 XorInto(M, this.subKey2);
203 }
204
205 XorInto(M, C);
206
207 byte[] C2 = Cipher.TransformFinalBlock(M, 0, this.blockSizeBytes);
208
209 Array.Clear(C, 0, this.blockSizeBytes);
210 C = C2;
211 }
212 while (i < c);
213
214 Array.Clear(M, 0, this.blockSizeBytes);
215
216 return C;
217 }
218
219 private static readonly byte[] zero16 = new byte[16];
220
221 private static void XorInto(byte[] Data, byte[] Data2)
222 {
223 int c = Data.Length;
224 if (Data2.Length != c)
225 throw new ArgumentException("Data blocks must be of the same length.", nameof(Data2));
226
227 for (int i = 0; i < c; i++)
228 Data[i] ^= Data2[i];
229 }
230
237 public bool Verify(byte[] Message, byte[] Signature)
238 {
239 int i, c;
240
241 if (Message is null || Signature is null || (c = Signature.Length) > this.blockSizeBytes)
242 return false;
243
244 byte[] Signature2 = this.Sign(Message, c);
245
246 for (i = 0; i < c; i++)
247 {
248 if (Signature[i] != Signature2[i])
249 return false;
250 }
251
252 return true;
253 }
254 }
255}
Implements the CMAC algorithm, as defined in NIST SP 800-38B, revision 2016. Ref: https://nvlpubs....
Definition: CMac.cs:11
bool Verify(byte[] Message, byte[] Signature)
Verifies a CMAC Signature.
Definition: CMac.cs:237
byte[] Sign(byte[] Message)
Signs a message using the current CMAC.
Definition: CMac.cs:164
static CMac CreateAes192CMac(byte[] Key)
Creates an instance of CMAC using AES-192 as the underlying block cipher
Definition: CMac.cs:53
static CMac CreateAes256CMac(byte[] Key)
Creates an instance of CMAC using AES-256 as the underlying block cipher
Definition: CMac.cs:63
byte[] Sign(byte[] Message, int Len)
Signs a message using the current CMAC.
Definition: CMac.cs:144
static CMac CreateAes128CMac(byte[] Key)
Creates an instance of CMAC using AES-128 as the underlying block cipher
Definition: CMac.cs:43
void Dispose()
Disposes of the object.
Definition: CMac.cs:124