@@ -89,6 +89,62 @@ internal override void CreateKey(
89
89
keyHandle = SecureMemoryHandle . CreateFrom ( seed ) ;
90
90
}
91
91
92
+ public void EncryptDetached (
93
+ Key key ,
94
+ ReadOnlySpan < byte > nonce ,
95
+ ReadOnlySpan < byte > associatedData ,
96
+ ReadOnlySpan < byte > plaintext ,
97
+ Span < byte > ciphertext ,
98
+ Span < byte > authenticationTag )
99
+ {
100
+ if ( key == null )
101
+ {
102
+ throw Error . ArgumentNull_Key ( nameof ( key ) ) ;
103
+ }
104
+ if ( key . Algorithm != this )
105
+ {
106
+ throw Error . Argument_KeyAlgorithmMismatch ( nameof ( key ) , nameof ( key ) ) ;
107
+ }
108
+ if ( nonce . Length != NonceSize )
109
+ {
110
+ throw Error . Argument_NonceLength ( nameof ( nonce ) , NonceSize ) ;
111
+ }
112
+ if ( ciphertext . Length != plaintext . Length )
113
+ {
114
+ throw new ArgumentException ( ) ;
115
+ }
116
+ if ( ciphertext . Overlaps ( plaintext , out int offset ) && offset != 0 )
117
+ {
118
+ throw Error . Argument_OverlapCiphertext ( nameof ( ciphertext ) ) ;
119
+ }
120
+ if ( authenticationTag . Length != TagSize )
121
+ {
122
+ throw new ArgumentException ( ) ;
123
+ }
124
+
125
+ SecureMemoryHandle keyHandle = key . Handle ;
126
+
127
+ Debug . Assert ( keyHandle . Size == crypto_aead_aes256gcm_KEYBYTES ) ;
128
+ Debug . Assert ( nonce . Length == crypto_aead_aes256gcm_NPUBBYTES ) ;
129
+ Debug . Assert ( ciphertext . Length == plaintext . Length ) ;
130
+ Debug . Assert ( authenticationTag . Length == crypto_aead_aes256gcm_ABYTES ) ;
131
+
132
+ int error = crypto_aead_aes256gcm_encrypt_detached (
133
+ ciphertext ,
134
+ authenticationTag ,
135
+ out ulong maclen ,
136
+ plaintext ,
137
+ ( ulong ) plaintext . Length ,
138
+ associatedData ,
139
+ ( ulong ) associatedData . Length ,
140
+ IntPtr . Zero ,
141
+ nonce ,
142
+ keyHandle ) ;
143
+
144
+ Debug . Assert ( error == 0 ) ;
145
+ Debug . Assert ( ( ulong ) authenticationTag . Length == maclen ) ;
146
+ }
147
+
92
148
private protected override void EncryptCore (
93
149
SecureMemoryHandle keyHandle ,
94
150
ReadOnlySpan < byte > nonce ,
@@ -120,6 +176,58 @@ internal override int GetSeedSize()
120
176
return crypto_aead_aes256gcm_KEYBYTES ;
121
177
}
122
178
179
+ public bool DecryptDetached (
180
+ Key key ,
181
+ ReadOnlySpan < byte > nonce ,
182
+ ReadOnlySpan < byte > associatedData ,
183
+ ReadOnlySpan < byte > ciphertext ,
184
+ ReadOnlySpan < byte > authenticationTag ,
185
+ Span < byte > plaintext )
186
+ {
187
+ if ( key == null )
188
+ {
189
+ throw Error . ArgumentNull_Key ( nameof ( key ) ) ;
190
+ }
191
+ if ( key . Algorithm != this )
192
+ {
193
+ throw Error . Argument_KeyAlgorithmMismatch ( nameof ( key ) , nameof ( key ) ) ;
194
+ }
195
+ if ( nonce . Length != NonceSize || authenticationTag . Length != TagSize )
196
+ {
197
+ return false ;
198
+ }
199
+ if ( plaintext . Length != ciphertext . Length )
200
+ {
201
+ throw new ArgumentException ( ) ;
202
+ }
203
+ if ( plaintext . Overlaps ( ciphertext , out int offset ) && offset != 0 )
204
+ {
205
+ throw Error . Argument_OverlapPlaintext ( nameof ( plaintext ) ) ;
206
+ }
207
+
208
+ SecureMemoryHandle keyHandle = key . Handle ;
209
+
210
+ Debug . Assert ( keyHandle . Size == crypto_aead_aes256gcm_KEYBYTES ) ;
211
+ Debug . Assert ( nonce . Length == crypto_aead_aes256gcm_NPUBBYTES ) ;
212
+ Debug . Assert ( ciphertext . Length == plaintext . Length ) ;
213
+ Debug . Assert ( authenticationTag . Length == crypto_aead_aes256gcm_ABYTES ) ;
214
+
215
+ int error = crypto_aead_aes256gcm_decrypt_detached (
216
+ plaintext ,
217
+ IntPtr . Zero ,
218
+ ciphertext ,
219
+ ( ulong ) ciphertext . Length ,
220
+ authenticationTag ,
221
+ associatedData ,
222
+ ( ulong ) associatedData . Length ,
223
+ nonce ,
224
+ keyHandle ) ;
225
+
226
+ // libsodium clears plaintext if decryption fails
227
+
228
+ return error == 0 ;
229
+ }
230
+
123
231
private protected override bool DecryptCore (
124
232
SecureMemoryHandle keyHandle ,
125
233
ReadOnlySpan < byte > nonce ,
0 commit comments