15
15
part of webcrypto;
16
16
17
17
/// AES secret key for symmetric encryption and decryption using AES in
18
- /// _Cipher Block Chaining mode_ (CBC-mode), as described in [NIST SP800-38A] .
18
+ /// _Cipher Block Chaining mode_ (CBC-mode), as described in
19
+ /// [NIST SP800-38A][1] .
19
20
///
20
21
/// Encrypted messages are always padded in PKCS#7 mode, as described in
21
- /// [RFC 2315 Section 10.3 step 2][rfc-2315-10.3 ] . This padding is stripped when
22
+ /// [RFC 2315 Section 10.3 step 2][2 ] . This padding is stripped when
22
23
/// the message is decrypted.
23
24
///
24
- /// Instances of [AesCbcSecretKey] can be imported using
25
- /// [AesCbcSecretKey.importRawKey] and [AesCbcSecretKey.importJsonWebKey ] , or
26
- /// generated using [AesCbcSecretKey.generateKey ] .
25
+ /// An [AesCbcSecretKey] can be imported from:
26
+ /// * Raw bytes using [AesCbcSecretKey.importRawKey ] , and,
27
+ /// * [JWK] format using [AesCbcSecretKey.importJsonWebKey ] .
27
28
///
28
- /// [NIST SP800-38A] : https://csrc.nist.gov/publications/detail/sp/800-38a/final
29
- /// [rfc-2315-10.3] : https://tools.ietf.org/html/rfc2315#section-10.3
29
+ /// A random [AesCbcSecretKey] can generated using
30
+ /// [AesCbcSecretKey.generateKey] .
31
+ ///
32
+ /// {@macro AesCbcSecretKey-encryptBytes/decryptBytes:example}
33
+ ///
34
+ /// [1] : https://csrc.nist.gov/publications/detail/sp/800-38a/final
35
+ /// [2] : https://tools.ietf.org/html/rfc2315#section-10.3
36
+ /// [3] : https://tools.ietf.org/html/rfc7517
30
37
@sealed
31
38
abstract class AesCbcSecretKey {
32
39
AesCbcSecretKey ._(); // keep the constructor private.
@@ -37,11 +44,15 @@ abstract class AesCbcSecretKey {
37
44
/// * 16 bytes (128 bit) for AES-128, or,
38
45
/// * 32 bytes (256 bit) for AES-256.
39
46
///
40
- /// Support for AES-192 (24 byte keys) is [intentionally omitted by Chrome][1] ,
41
- /// and will therefore not be supported by `package:webcrypto` .
47
+ /// {@template AES:no-support-for-AES-192}
48
+ /// Support for AES-192 (24 byte keys) is intentionally omitted, in line with
49
+ /// the [decision not support AES-192 in Chrome] (https://crbug.com/533699).
50
+ /// {@endtemplate}
42
51
///
43
52
/// **Example**
44
53
/// ```dart
54
+ /// import 'dart:convert' show utf8;
55
+ /// import 'dart:typed_data' show Uint8List;
45
56
/// import 'package:webcrypto/webcrypto.dart';
46
57
///
47
58
/// final rawKey = Uint8List(16);
@@ -58,29 +69,232 @@ abstract class AesCbcSecretKey {
58
69
///
59
70
/// print(utf8.decode(await k.decryptBytes(c, iv))); // hello world
60
71
/// ```
61
- ///
62
- /// [1] : https://crbug.com/533699
63
72
static Future <AesCbcSecretKey > importRawKey (List <int > keyData) {
64
73
return impl.aesCbc_importRawKey (keyData);
65
74
}
66
75
76
+ /// Import an [AesCbcSecretKey] from [JSON Web Key][1] .
77
+ ///
78
+ /// JSON Web Keys imported using [AesCbcSecretKey.importJsonWebKey]
79
+ /// must have `"kty": "oct"` , and the `"alg"` property of the imported [jwk]
80
+ /// must be either:
81
+ /// * `"alg": "A128CBC"` for AES-128, or
82
+ /// * `"alg": "A256CBC"` for AES-256.
83
+ ///
84
+ /// {@macro AES:no-support-for-AES-192}
85
+ ///
86
+ /// If specified the `"use"` property of the imported [jwk] must be
87
+ /// `"use": "sig"` .
88
+ ///
89
+ /// {@macro importJsonWebKey:throws-FormatException-if-jwk}
90
+ ///
91
+ /// **Example**
92
+ /// ```dart
93
+ /// import 'dart:convert' show jsonEncode, jsonDecode;
94
+ /// import 'package:webcrypto/webcrypto.dart';
95
+ ///
96
+ /// // JSON Web Key as a string containing JSON.
97
+ /// final jwk = '{"kty": "oct", "alg": "A256CBC", "k": ...}';
98
+ ///
99
+ /// // Import secret key from decoded JSON.
100
+ /// final key = await AesCbcSecretKey.importJsonWebKey(jsonDecode(jwk));
101
+ ///
102
+ /// // Export the key (print it in same format as it was given).
103
+ /// Map<String, dynamic> keyData = await key.exportJsonWebKey();
104
+ /// print(jsonEncode(keyData));
105
+ /// ```
106
+ ///
107
+ /// [1] : https://tools.ietf.org/html/rfc7517
108
+ // TODO: Decide if we want restrictions on "use" property" (we probably have it on web, if we don't strip it)
109
+ // TODO: Decide if we want place restrictions on key_ops
67
110
static Future <AesCbcSecretKey > importJsonWebKey (Map <String , dynamic > jwk) {
68
111
return impl.aesCbc_importJsonWebKey (jwk);
69
112
}
70
113
114
+ /// Generate random [AesCbcSecretKey] .
115
+ ///
116
+ /// The [length] is given in bits, and implies the AES variant to be used.
117
+ /// The [length] can be either:
118
+ /// * 128 for AES-128, or,
119
+ /// * 256 for AES-256.
120
+ ///
121
+ /// {@macro AES:no-support-for-AES-192}
122
+ ///
123
+ /// **Example**
124
+ /// ```dart
125
+ /// import 'package:webcrypto/webcrypto.dart';
126
+ ///
127
+ /// // Generate a new random HMAC secret key for AES-256.
128
+ /// final key = await AesCbcSecretKey.generate(256);
129
+ /// ```
71
130
static Future <AesCbcSecretKey > generateKey (int length) {
72
131
return impl.aesCbc_generateKey (length);
73
132
}
74
133
134
+ /// Encrypt [data] with this [AesCbcSecretKey] using AES in _Cipher Block
135
+ /// Chaining_ mode, as specified in [NIST SP800-38A][1] .
136
+ ///
137
+ /// {@template AesCbcSecretKey-encrypt:iv}
138
+ /// The operation requires a 16 bytes _initalization vector_ [iv] . The [iv]
139
+ /// needs not be secret, but it must be unpredictable. In particular, for a
140
+ /// given plaintext it must not be possible to predict the [iv] that will be
141
+ /// used to encrypt the plaintext.
142
+ /// For detailed discussion of the initialization vector requirements for
143
+ /// AES-CBC, see [Appendix C of NIST SP800-38A] (https://csrc.nist.gov/publications/detail/sp/800-38a/final).
144
+ /// {@endtemplate}
145
+ ///
146
+ /// {@template AesCbcSecretKey-encrypt:padding}
147
+ /// Encrypted output is always padded in PKCS#7 mode, as described in
148
+ /// [RFC 2315 Section 10.3 step 2] (https://tools.ietf.org/html/rfc2315#section-10.3).
149
+ /// This padding is stripped when the message is decrypted.
150
+ /// {@endtemplate}
151
+ ///
152
+ /// {@template AesCbcSecretKey-encryptBytes/decryptBytes:example}
153
+ /// **Example**
154
+ /// ```dart
155
+ /// import 'dart:convert' show utf8;
156
+ /// import 'dart:typed_data' show Uint8List;
157
+ /// import 'package:webcrypto/webcrypto.dart';
158
+ ///
159
+ /// // Generate a new random HMAC secret key for AES-256.
160
+ /// final key = await AesCbcSecretKey.generate(256);
161
+ ///
162
+ /// // Use a unique IV for each message.
163
+ /// final iv = Uint8List(16);
164
+ /// fillRandomBytes(iv);
165
+ ///
166
+ /// // Encrypt a message
167
+ /// final c = await k.encryptBytes(utf8.encode('hello world'), iv);
168
+ ///
169
+ /// // Decrypt message (requires the same iv)
170
+ /// print(utf8.decode(await k.decryptBytes(c, iv))); // hello world
171
+ /// ```
172
+ /// {@endtemplate}
173
+ ///
174
+ /// [1] : https://csrc.nist.gov/publications/detail/sp/800-38a/final
75
175
Future <Uint8List > encryptBytes (List <int > data, List <int > iv);
76
176
177
+ /// Encrypt [data] with this [AesCbcSecretKey] using AES in _Cipher Block
178
+ /// Chaining_ mode, as specified in [NIST SP800-38A][1] .
179
+ ///
180
+ /// {@macro AesCbcSecretKey-encrypt:iv}
181
+ ///
182
+ /// {@macro AesCbcSecretKey-encrypt:padding}
183
+ ///
184
+ /// {@template AesCbcSecretKey-encryptStream/decryptStream:example}
185
+ /// **Example**
186
+ /// ```dart
187
+ /// import 'dart:io' show File;
188
+ /// import 'dart:convert' show utf8;
189
+ /// import 'dart:typed_data' show Uint8List;
190
+ /// import 'package:async/async.dart' show collectBytes;
191
+ /// import 'package:webcrypto/webcrypto.dart';
192
+ ///
193
+ /// // Generate a new random HMAC secret key for AES-256.
194
+ /// final key = await AesCbcSecretKey.generate(256);
195
+ ///
196
+ /// // Use a unique IV for each message.
197
+ /// final iv = Uint8List(16);
198
+ /// fillRandomBytes(iv);
199
+ ///
200
+ /// // Encrypt a message from file and write to file
201
+ /// final inputFile = File('message.txt');
202
+ /// final encryptedFile = File('encrypted-message.binary');
203
+ /// final c = await k.encryptStream(
204
+ /// inputFile.openRead(),
205
+ /// iv,
206
+ /// ).pipe(encryptedFile.openWrite());
207
+ ///
208
+ /// // Decrypt message (requires the same iv)
209
+ /// final decryptedBytes = await collectBytes(k.decryptStream(
210
+ /// encryptedFile.openRead(),
211
+ /// iv, // same iv as used for encryption
212
+ /// ));
213
+ /// // decryptedBytes should be equal to contents of inputFile
214
+ /// assert(utf8.decode(decryptedBytes) == inputFile.readAsStringSync());
215
+ /// ```
216
+ /// {@endtemplate}
217
+ ///
218
+ /// [1] : https://csrc.nist.gov/publications/detail/sp/800-38a/final
77
219
Stream <Uint8List > encryptStream (Stream <List <int >> data, List <int > iv);
78
220
221
+ /// Decrypt [data] with this [AesCbcSecretKey] using AES in _Cipher Block
222
+ /// Chaining_ mode, as specified in [NIST SP800-38A][1] .
223
+ ///
224
+ /// {@template AesCbcSecretKey-decrypt:iv}
225
+ /// To decrypt [data] the same _initalization vector_ [iv] as was used for
226
+ /// encryption must be specified. The [iv] must always be 16 bytes.
227
+ /// See [encryptBytes] for further discussion of the initialization vector.
228
+ /// {@endtemplate}
229
+ ///
230
+ /// {@template AesCbcSecretKey-decrypt:padding}
231
+ /// The encrypted [data] is always assumed to be padded in PKCS#7 mode,
232
+ /// as described in
233
+ /// [RFC 2315 Section 10.3 step 2] (https://tools.ietf.org/html/rfc2315#section-10.3).
234
+ /// This padding is stripped from the decrypted return value.
235
+ /// The [encryptBytes] and [encryptStream] methods always apply this padding.
236
+ /// {@endtemplate}
237
+ ///
238
+ /// {@macro AesCbcSecretKey-encryptBytes/decryptBytes:example}
239
+ ///
240
+ /// [1] : https://csrc.nist.gov/publications/detail/sp/800-38a/final
79
241
Future <Uint8List > decryptBytes (List <int > data, List <int > iv);
80
242
243
+ /// Decrypt [data] with this [AesCbcSecretKey] using AES in _Cipher Block
244
+ /// Chaining_ mode, as specified in [NIST SP800-38A][1] .
245
+ ///
246
+ /// {@macro AesCbcSecretKey-decrypt:iv}
247
+ ///
248
+ /// {@macro AesCbcSecretKey-decrypt:padding}
249
+ ///
250
+ /// {@macro AesCbcSecretKey-encryptStream/decryptStream:example}
251
+ ///
252
+ /// [1] : https://csrc.nist.gov/publications/detail/sp/800-38a/final
81
253
Stream <Uint8List > decryptStream (Stream <List <int >> data, List <int > iv);
82
254
255
+ /// Export [AesCbcSecretKey] as raw bytes.
256
+ ///
257
+ /// This returns raw bytes making up the secret key.
258
+ ///
259
+ /// **Example**
260
+ /// ```dart
261
+ /// import 'package:webcrypto/webcrypto.dart';
262
+ ///
263
+ /// // Generate a new random AES-258 secret key.
264
+ /// final key = await AesCbcSecretKey.generate(256);
265
+ ///
266
+ /// // Extract the secret key.
267
+ /// final secretBytes = await key.exportRawKey();
268
+ ///
269
+ /// // Print the key as base64
270
+ /// print(base64.encode(secretBytes));
271
+ ///
272
+ /// // If we wanted to we could import the key as follows:
273
+ /// // key = await AesCbcSecretKey.importRawKey(secretBytes);
274
+ /// ```
83
275
Future <Uint8List > exportRawKey ();
84
276
277
+ /// Export [AesCbcSecretKey] as [JSON Web Key][1] .
278
+ ///
279
+ /// {@macro exportJsonWebKey:returns}
280
+ ///
281
+ /// **Example**
282
+ /// ```dart
283
+ /// import 'package:webcrypto/webcrypto.dart';
284
+ /// import 'dart:convert' show jsonEncode;
285
+ ///
286
+ /// // Generate a new random AES-258 secret key.
287
+ /// final key = await AesCbcSecretKey.generate(256);
288
+ ///
289
+ /// // Export the secret key.
290
+ /// final jwk = await key.exportJsonWebKey();
291
+ ///
292
+ /// // The Map returned by `exportJsonWebKey()` can be converted to JSON with
293
+ /// // `jsonEncode` from `dart:convert`, this will print something like:
294
+ /// // {"kty": "oct", "alg": "A256CBC", "k": ...}
295
+ /// print(jsonEncode(jwk));
296
+ /// ```
297
+ ///
298
+ /// [1] : https://tools.ietf.org/html/rfc7517
85
299
Future <Map <String , dynamic >> exportJsonWebKey ();
86
300
}
0 commit comments