forked from sdrapkin/SecurityDriven.Inferno
-
Notifications
You must be signed in to change notification settings - Fork 0
/
EtM_CTR.cs
171 lines (138 loc) · 8.47 KB
/
EtM_CTR.cs
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
using System;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Threading;
namespace SecurityDriven.Inferno
{
public static partial class EtM_CTR
{
static readonly Func<Aes> _aesFactory = Cipher.AesFactories.Aes;
static readonly Func<Mac.HMAC2> _hmacFactory = Mac.HMACFactories.HMACSHA384;
static readonly CryptoRandom _cryptoRandom = new CryptoRandom();
internal const int MAC_LENGTH = 128 / 8;
const int MAC_KEY_LENGTH = MAC_LENGTH;
const int ENC_KEY_LENGTH = 256 / 8;
static readonly int HMAC_LENGTH = _hmacFactory().HashSize / 8;
internal const int CONTEXT_TWEAK_LENGTH = ENC_KEY_LENGTH;
internal const int NONCE_LENGTH = Cipher.AesConstants.AES_BLOCK_SIZE / 2;
const int CONTEXT_BUFFER_LENGTH = CONTEXT_TWEAK_LENGTH + NONCE_LENGTH;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static void ClearKeyMaterial(byte[] encKey, byte[] macKey, byte[] sessionKey)
{
for (int i = 0; i < encKey.Length; ++i) encKey[i] = 0; //Array.Clear(encKey, 0, encKey.Length);
for (int j = 0; j < macKey.Length; ++j) macKey[j] = 0; //Array.Clear(macKey, 0, macKey.Length);
Array.Clear(sessionKey, 0, sessionKey.Length);
}// ClearKeyMaterial()
public static void Encrypt(byte[] masterKey, ArraySegment<byte> plaintext, byte[] output, int outputOffset, ArraySegment<byte>? salt = null, uint counter = 1)
{
int ciphertextLength = CONTEXT_BUFFER_LENGTH + MAC_LENGTH + plaintext.Count;
if (output.Length - outputOffset < ciphertextLength) throw new ArgumentOutOfRangeException(nameof(output), $"'{nameof(output)}' array segment is not big enough for the ciphertext");
var counterBuffer = new byte[Cipher.AesConstants.AES_BLOCK_SIZE];
var contextBuffer = new byte[CONTEXT_BUFFER_LENGTH];
var encKey = new byte[ENC_KEY_LENGTH];
var macKey = new byte[MAC_KEY_LENGTH];
var sessionKey = new byte[HMAC_LENGTH];
try
{
_cryptoRandom.NextBytes(contextBuffer, 0, CONTEXT_BUFFER_LENGTH);
Kdf.SP800_108_Ctr.DeriveKey(hmacFactory: _hmacFactory, key: masterKey, label: salt, context: new ArraySegment<byte>(contextBuffer, 0, CONTEXT_TWEAK_LENGTH), derivedOutput: sessionKey.AsArraySegment(), counter: counter);
//Utils.BlockCopy(sessionKey, 0, macKey, 0, MAC_KEY_LENGTH);
for (int i = 0; i < macKey.Length; ++i) macKey[i] = sessionKey[i];
//Utils.BlockCopy(sessionKey, MAC_KEY_LENGTH, encKey, 0, ENC_KEY_LENGTH);
for (int i = 0; i < encKey.Length; ++i) encKey[i] = sessionKey[MAC_KEY_LENGTH + i];
//Utils.BlockCopy(contextBuffer, 0, output, outputOffset, CONTEXT_BUFFER_LENGTH);
for (int i = 0; i < contextBuffer.Length; ++i) output[outputOffset + i] = contextBuffer[i];
//Utils.BlockCopy(contextBuffer, CONTEXT_TWEAK_LENGTH, counterBuffer, 0, NONCE_LENGTH);
for (int i = 0; i < NONCE_LENGTH; ++i) counterBuffer[i] = contextBuffer[CONTEXT_TWEAK_LENGTH + i];
using (var ctrTransform = new Cipher.AesCtrCryptoTransform(key: encKey, counterBufferSegment: counterBuffer.AsArraySegment(), aesFactory: _aesFactory))
{
ctrTransform.TransformBlock(inputBuffer: plaintext.Array, inputOffset: plaintext.Offset, inputCount: plaintext.Count, outputBuffer: output, outputOffset: outputOffset + CONTEXT_BUFFER_LENGTH);
}// using aesEncryptor
using (var hmac = _hmacFactory())
{
hmac.Key = macKey;
hmac.TransformBlock(output, outputOffset + CONTEXT_TWEAK_LENGTH, NONCE_LENGTH + plaintext.Count, null, 0);
hmac.TransformFinalBlock(output, 0, 0);
var fullmac = hmac.HashInner;
//Utils.BlockCopy(fullmac, 0, output, outputOffset + ciphertextLength - MAC_LENGTH, MAC_LENGTH);
for (int i = 0; i < MAC_LENGTH; ++i) output[outputOffset + ciphertextLength - MAC_LENGTH + i] = fullmac[i];
}// using hmac
}
finally { EtM_CTR.ClearKeyMaterial(encKey, macKey, sessionKey); }
}// Encrypt()
public static byte[] Encrypt(byte[] masterKey, ArraySegment<byte> plaintext, ArraySegment<byte>? salt = null, uint counter = 1)
{
byte[] buffer = new byte[CONTEXT_BUFFER_LENGTH + plaintext.Count + MAC_LENGTH];
EtM_CTR.Encrypt(masterKey: masterKey, plaintext: plaintext, output: buffer, outputOffset: 0, salt: salt, counter: counter);
return buffer;
}// Encrypt()
public static void Decrypt(byte[] masterKey, ArraySegment<byte> ciphertext, ref ArraySegment<byte>? outputSegment, ArraySegment<byte>? salt = null, uint counter = 1)
{
int cipherLength = ciphertext.Count - CONTEXT_BUFFER_LENGTH - MAC_LENGTH;
if (cipherLength < 0) { outputSegment = null; return; }
var counterBuffer = new byte[Cipher.AesConstants.AES_BLOCK_SIZE];
var encKey = new byte[ENC_KEY_LENGTH];
var macKey = new byte[MAC_KEY_LENGTH];
var sessionKey = new byte[HMAC_LENGTH];
try
{
var ciphertextArray = ciphertext.Array;
var ciphertextOffset = ciphertext.Offset;
Kdf.SP800_108_Ctr.DeriveKey(hmacFactory: _hmacFactory, key: masterKey, label: salt, context: new ArraySegment<byte>(ciphertextArray, ciphertextOffset, CONTEXT_TWEAK_LENGTH), derivedOutput: sessionKey.AsArraySegment(), counter: counter);
//Utils.BlockCopy(sessionKey, 0, macKey, 0, MAC_KEY_LENGTH);
for (int i = 0; i < macKey.Length; ++i) macKey[i] = sessionKey[i];
using (var hmac = _hmacFactory())
{
hmac.Key = macKey;
hmac.TransformBlock(ciphertextArray, ciphertextOffset + CONTEXT_TWEAK_LENGTH, NONCE_LENGTH + cipherLength, null, 0);
hmac.TransformFinalBlock(ciphertextArray, 0, 0);
var fullmacActual = hmac.HashInner;
if (!Utils.ConstantTimeEqual(fullmacActual, 0, ciphertextArray, ciphertextOffset + ciphertext.Count - MAC_LENGTH, MAC_LENGTH)) { outputSegment = null; return; };
}// using hmac
if (outputSegment == null) outputSegment = (new byte[cipherLength]).AsNullableArraySegment();
//Utils.BlockCopy(ciphertext.Array, ciphertext.Offset + CONTEXT_TWEAK_LENGTH, counterBuffer, 0, NONCE_LENGTH);
for (int i = 0; i < NONCE_LENGTH; ++i) counterBuffer[i] = ciphertextArray[ciphertextOffset + CONTEXT_TWEAK_LENGTH + i];
//Utils.BlockCopy(sessionKey, MAC_KEY_LENGTH, encKey, 0, ENC_KEY_LENGTH);
for (int i = 0; i < encKey.Length; ++i) encKey[i] = sessionKey[MAC_KEY_LENGTH + i];
using (var ctrTransform = new Cipher.AesCtrCryptoTransform(key: encKey, counterBufferSegment: counterBuffer.AsArraySegment(), aesFactory: _aesFactory))
{
ctrTransform.TransformBlock(inputBuffer: ciphertextArray, inputOffset: ciphertextOffset + CONTEXT_BUFFER_LENGTH, inputCount: cipherLength, outputBuffer: outputSegment.GetValueOrDefault().Array, outputOffset: outputSegment.GetValueOrDefault().Offset);
}// using aesDecryptor
}
finally { EtM_CTR.ClearKeyMaterial(encKey, macKey, sessionKey); }
}// Decrypt()
public static byte[] Decrypt(byte[] masterKey, ArraySegment<byte> ciphertext, ArraySegment<byte>? salt = null, uint counter = 1)
{
int cipherLength = ciphertext.Count - CONTEXT_BUFFER_LENGTH - MAC_LENGTH;
if (cipherLength < 0) return null;
var bufferSegment = default(ArraySegment<byte>?);
EtM_CTR.Decrypt(masterKey, ciphertext, ref bufferSegment, salt, counter);
return (bufferSegment != null) ? bufferSegment.GetValueOrDefault().Array : null;
}// Decrypt()
public static bool Authenticate(byte[] masterKey, ArraySegment<byte> ciphertext, ArraySegment<byte>? salt = null, uint counter = 1)
{
int cipherLength = ciphertext.Count - CONTEXT_BUFFER_LENGTH - MAC_LENGTH;
if (cipherLength < 0) return false;
var macKey = new byte[MAC_KEY_LENGTH];
var sessionKey = new byte[HMAC_LENGTH];
try
{
var ciphertextArray = ciphertext.Array;
var ciphertextOffset = ciphertext.Offset;
Kdf.SP800_108_Ctr.DeriveKey(hmacFactory: _hmacFactory, key: masterKey, label: salt, context: new ArraySegment<byte>(ciphertextArray, ciphertextOffset, CONTEXT_TWEAK_LENGTH), derivedOutput: sessionKey.AsArraySegment(), counter: counter);
//Utils.BlockCopy(sessionKey, 0, macKey, 0, MAC_KEY_LENGTH);
for (int i = 0; i < macKey.Length; ++i) macKey[i] = sessionKey[i];
using (var hmac = _hmacFactory())
{
hmac.Key = macKey;
hmac.TransformBlock(ciphertextArray, ciphertextOffset + CONTEXT_TWEAK_LENGTH, NONCE_LENGTH + cipherLength, null, 0);
hmac.TransformFinalBlock(ciphertextArray, 0, 0);
var fullmacActual = hmac.HashInner;
if (!Utils.ConstantTimeEqual(fullmacActual, 0, ciphertextArray, ciphertextOffset + ciphertext.Count - MAC_LENGTH, MAC_LENGTH)) return false;
}// using hmac
return true;
}
finally { EtM_CTR.ClearKeyMaterial(encKey: Array.Empty<byte>(), macKey: macKey, sessionKey: sessionKey); }
}// Authenticate()
}//class EtM_CTR
}//ns