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+ }
0 commit comments