Skip to content

Commit 1ff4ac0

Browse files
committed
[Cryptography] Add algorithm stubs
1 parent ca0baa5 commit 1ff4ac0

3 files changed

Lines changed: 285 additions & 0 deletions

File tree

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
using System.Buffers.Binary;
2+
using System.Security.Cryptography;
3+
4+
namespace Amazon.Cryptography.Algorithms;
5+
6+
internal sealed class AES256_GCM_HKDF_SHA512_COMMIT_KEY : AlgorithmSuite
7+
{
8+
public static readonly AES256_GCM_HKDF_SHA512_COMMIT_KEY Default = new();
9+
10+
public override AlgorithmSuiteId AlgorithmId => AlgorithmSuiteId.AES_256_GCM_HKDF_SHA512_COMMIT_KEY;
11+
12+
public override byte MessageFormatVersion => 0x02;
13+
14+
public override KeySize DataKeyLength => KeySize.FromBitCount(256);
15+
16+
public override int AlgorithmSuiteDataLengthBytes => 32;
17+
18+
public override KeyDerivationAlgorithmType? KeyDerivationAlgorithm => KeyDerivationAlgorithmType.HKDF_SHA512;
19+
20+
public EncryptedMessage Encrypt(DataKey key, EncryptMessageRequest request)
21+
{
22+
if (key.RawKey.Length != 32)
23+
{
24+
throw new ArgumentException("Key must be 32 bytes (256 bits)", nameof(key));
25+
}
26+
27+
var messageId = RandomNumberGenerator.GetBytes(32); // 256 bits
28+
29+
// Derive a key using HKDF
30+
Span<byte> derivedKey = stackalloc byte[32];
31+
32+
DeriveKey(key.RawKey, messageId, derivedKey);
33+
34+
using var aes = new AesGcm(derivedKey, 16);
35+
36+
Span<byte> iv = stackalloc byte[12]; // 12-byte IV for AES-GCM
37+
38+
Span<byte> tag = stackalloc byte[16]; // 16-byte authentication tag
39+
40+
tag.Clear();
41+
42+
byte[] associatedData = request.EncryptionContext.Serialize();
43+
44+
(long chuckCount, long r) = Math.DivRem(request.Plaintext.Length, request.FrameLength);
45+
46+
if (r > 0) chuckCount++;
47+
48+
var frames = new List<EncryptedMessageFrame>((int)chuckCount);
49+
uint sequenceNumber = 1;
50+
51+
int offset = 0;
52+
int frameLength = (int)request.FrameLength;
53+
ReadOnlySpan<byte> plaintext = request.Plaintext;
54+
55+
while (offset < plaintext.Length)
56+
{
57+
var chunk = plaintext.Slice(offset, Math.Min(frameLength, plaintext.Length - offset));
58+
59+
SetIV(sequenceNumber, iv);
60+
61+
byte[] ciphertext = new byte[chunk.Length];
62+
aes.Encrypt(iv, chunk, ciphertext, tag, associatedData);
63+
64+
var frame = new EncryptedMessageFrame {
65+
IV = iv.ToArray(),
66+
EncryptedContent = ciphertext,
67+
SequenceNumber = sequenceNumber,
68+
AuthenticationTag = tag.ToArray(),
69+
IsFinal = sequenceNumber == chuckCount,
70+
};
71+
72+
sequenceNumber++;
73+
offset += chunk.Length;
74+
75+
frames.Add(frame);
76+
}
77+
78+
return new EncryptedMessage {
79+
Header = new EncryptedMessageHeader {
80+
AlgorithmId = AlgorithmId,
81+
AlgorithmSuiteData = messageId,
82+
MessageId = messageId,
83+
ContentType = 2, // framed
84+
EncryptionContext = request.EncryptionContext,
85+
AuthenticationTag = "a"u8.ToArray(),
86+
EncryptedDataKeys = [
87+
new EncryptedDataKey(key.ProviderId, key.ProviderContext, [])
88+
],
89+
FrameLength = request.FrameLength,
90+
Version = 2
91+
},
92+
Frames = frames
93+
};
94+
}
95+
96+
public override void DeriveKey(ReadOnlySpan<byte> dataKey, ReadOnlySpan<byte> messageId, Span<byte> derivedKey)
97+
{
98+
if (messageId.Length != 32)
99+
{
100+
throw new ArgumentException("Must be 32 bytes (256 bits)", nameof(dataKey));
101+
}
102+
103+
if (messageId.Length != 32)
104+
{
105+
throw new CryptographicException("Must be 32 bytes (256 bits)");
106+
}
107+
108+
if (derivedKey.Length != 32)
109+
{
110+
throw new ArgumentException("Must be 32 bytes (256 bits)", nameof(derivedKey));
111+
}
112+
113+
Span<byte> inputInfo = stackalloc byte[
114+
2 + // algorithmId
115+
9 // key label
116+
];
117+
118+
BinaryPrimitives.WriteUInt16BigEndian(inputInfo, (ushort)AlgorithmId);
119+
"DERIVEKEY"u8.CopyTo(inputInfo[2..]);
120+
121+
HKDF.DeriveKey(HashAlgorithmName.SHA512, dataKey, derivedKey, salt: messageId, inputInfo);
122+
}
123+
124+
public void CalculateCommitmentKey(ReadOnlySpan<byte> dataKey, ReadOnlySpan<byte> messageId, Span<byte> commitmentKey)
125+
{
126+
HKDF.DeriveKey(HashAlgorithmName.SHA512, dataKey, commitmentKey, salt: messageId, info: "COMMITKEY"u8);
127+
}
128+
129+
private static void SetIV(uint sequenceNumber, Span<byte> iv)
130+
{
131+
iv.Clear();
132+
133+
BinaryPrimitives.WriteUInt32BigEndian(iv[8..], sequenceNumber);
134+
}
135+
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
using System.Buffers.Binary;
2+
using System.Security.Cryptography;
3+
4+
namespace Amazon.Cryptography.Algorithms;
5+
6+
internal sealed class AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384 : AlgorithmSuite
7+
{
8+
public static readonly AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384 Default = new();
9+
10+
public override AlgorithmSuiteId AlgorithmId => AlgorithmSuiteId.AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384;
11+
12+
public override byte MessageFormatVersion => 0x02;
13+
14+
public override KeySize DataKeyLength => KeySize.FromBitCount(256);
15+
16+
public override int AlgorithmSuiteDataLengthBytes => 32;
17+
18+
public override KeyDerivationAlgorithmType? KeyDerivationAlgorithm => KeyDerivationAlgorithmType.HKDF_SHA512;
19+
20+
public EncryptedMessage Encrypt(DataKey key, EncryptMessageRequest request)
21+
{
22+
if (key.RawKey.Length != 32)
23+
{
24+
throw new ArgumentException("Key must be 32 bytes (256 bits)", nameof(key));
25+
}
26+
27+
var messageId = RandomNumberGenerator.GetBytes(32); // 256 bits
28+
29+
// Derive a key using HKDF
30+
Span<byte> derivedKey = stackalloc byte[32];
31+
32+
DeriveKey(key.RawKey, messageId, derivedKey);
33+
34+
using var aes = new AesGcm(derivedKey, 16);
35+
36+
Span<byte> iv = stackalloc byte[12]; // 12-byte IV for AES-GCM
37+
38+
Span<byte> tag = stackalloc byte[16]; // 16-byte authentication tag
39+
40+
tag.Clear();
41+
42+
byte[] associatedData = request.EncryptionContext.Serialize();
43+
44+
(long chuckCount, long r) = Math.DivRem(request.Plaintext.Length, request.FrameLength);
45+
46+
if (r > 0) chuckCount++;
47+
48+
var frames = new List<EncryptedMessageFrame>((int)chuckCount);
49+
uint sequenceNumber = 1;
50+
51+
int offset = 0;
52+
int frameLength = (int)request.FrameLength;
53+
ReadOnlySpan<byte> plaintext = request.Plaintext;
54+
55+
while (offset < plaintext.Length)
56+
{
57+
var chunk = plaintext.Slice(offset, Math.Min(frameLength, plaintext.Length - offset));
58+
59+
SetIV(sequenceNumber, iv);
60+
61+
byte[] ciphertext = new byte[chunk.Length];
62+
aes.Encrypt(iv, chunk, ciphertext, tag, associatedData);
63+
64+
var frame = new EncryptedMessageFrame {
65+
IV = iv.ToArray(),
66+
EncryptedContent = ciphertext,
67+
SequenceNumber = sequenceNumber,
68+
AuthenticationTag = tag.ToArray(),
69+
IsFinal = sequenceNumber == chuckCount,
70+
};
71+
72+
sequenceNumber++;
73+
offset += chunk.Length;
74+
75+
frames.Add(frame);
76+
}
77+
78+
return new EncryptedMessage {
79+
Header = new EncryptedMessageHeader {
80+
AlgorithmId = AlgorithmId,
81+
AlgorithmSuiteData = messageId,
82+
MessageId = messageId,
83+
ContentType = 2, // framed
84+
EncryptionContext = request.EncryptionContext,
85+
AuthenticationTag = "a"u8.ToArray(),
86+
EncryptedDataKeys = [
87+
new EncryptedDataKey(key.ProviderId, key.ProviderContext, [])
88+
],
89+
FrameLength = request.FrameLength,
90+
Version = 2
91+
},
92+
Frames = frames
93+
};
94+
}
95+
96+
public override void DeriveKey(ReadOnlySpan<byte> dataKey, ReadOnlySpan<byte> messageId, Span<byte> derivedKey)
97+
{
98+
if (messageId.Length != 32)
99+
{
100+
throw new ArgumentException("Must be 32 bytes (256 bits)", nameof(dataKey));
101+
}
102+
103+
if (messageId.Length != 32)
104+
{
105+
throw new CryptographicException("Must be 32 bytes (256 bits)");
106+
}
107+
108+
if (derivedKey.Length != 32)
109+
{
110+
throw new ArgumentException("Must be 32 bytes (256 bits)", nameof(derivedKey));
111+
}
112+
113+
Span<byte> inputInfo = stackalloc byte[
114+
2 + // algorithmId
115+
9 // key label
116+
];
117+
118+
BinaryPrimitives.WriteUInt16BigEndian(inputInfo, (ushort)AlgorithmId);
119+
"DERIVEKEY"u8.CopyTo(inputInfo[2..]);
120+
121+
HKDF.DeriveKey(HashAlgorithmName.SHA512, dataKey, derivedKey, salt: messageId, inputInfo);
122+
}
123+
124+
public void CalculateCommitmentKey(ReadOnlySpan<byte> dataKey, ReadOnlySpan<byte> messageId, Span<byte> commitmentKey)
125+
{
126+
HKDF.DeriveKey(HashAlgorithmName.SHA512, dataKey, commitmentKey, salt: messageId, info: "COMMITKEY"u8);
127+
}
128+
129+
private static void SetIV(uint sequenceNumber, Span<byte> iv)
130+
{
131+
iv.Clear();
132+
133+
BinaryPrimitives.WriteUInt32BigEndian(iv[8..], sequenceNumber);
134+
}
135+
136+
// TODO: Implement signature logic
137+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
namespace Amazon.Cryptography.Algorithms;
2+
3+
public partial class AlgorithmSuite
4+
{
5+
public static AlgorithmSuite Get(AlgorithmSuiteId id)
6+
{
7+
return id switch {
8+
AlgorithmSuiteId.AES_256_GCM_HKDF_SHA512_COMMIT_KEY => AES256_GCM_HKDF_SHA512_COMMIT_KEY.Default,
9+
AlgorithmSuiteId.AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384 => AES_256_GCM_HKDF_SHA512_COMMIT_KEY_ECDSA_P384.Default,
10+
_ => throw new NotImplementedException()
11+
};
12+
}
13+
}

0 commit comments

Comments
 (0)