@@ -20,8 +20,11 @@ part of webcrypto;
20
20
/// [Hash] , which can be used to create and verify HMAC signatures as
21
21
/// specified in [FIPS PUB 180-4][1] .
22
22
///
23
- /// Instances of [HmacSecretKey] can be imported using
24
- /// [HmacSecretKey.importRawKey] or generated using [HmacSecretKey.generateKey] .
23
+ /// Instances of [HmacSecretKey] can be imported from:
24
+ /// * Raw bytes using [HmacSecretKey.importRawKey] , and,
25
+ /// * [JWK] format using [HmacSecretKey.importJsonWebKey] .
26
+ ///
27
+ /// A random key can also be generated using [HmacSecretKey.generateKey] .
25
28
///
26
29
/// [1] : https://doi.org/10.6028/NIST.FIPS.180-4
27
30
@sealed
@@ -33,7 +36,7 @@ abstract class HmacSecretKey {
33
36
/// Creates an [HmacSecretKey] using [keyData] as secret key, and running
34
37
/// HMAC with given [hash] algorithm.
35
38
///
36
- /// If given [length] specifies the length of the key, this must be not be
39
+ /// If given, [length] specifies the length of the key, this must be not be
37
40
/// less than number of bits in [keyData] - 7. The [length] only allows
38
41
/// cutting bits of the last byte in [keyData] . In practice this is the same
39
42
/// as zero'ing the last bits in [keyData] .
@@ -44,7 +47,7 @@ abstract class HmacSecretKey {
44
47
/// import 'package:webcrypto/webcrypto.dart';
45
48
///
46
49
/// final key = await HmacSecretKey.importRawKey(
47
- /// utf8.encode('a-secret-key '), // don't use string in practice
50
+ /// base64.decode('WzIxLDg0LDEwMCw5OSwxMCwxMDUsMjIsODAsMTkwLDExNiwyMDMsMjQ5XQ== '),
48
51
/// Hash.sha256,
49
52
/// );
50
53
/// ```
@@ -71,12 +74,49 @@ abstract class HmacSecretKey {
71
74
return impl.hmacSecretKey_importRawKey (keyData, hash, length: length);
72
75
}
73
76
74
- /// Import [HmacSecretKey] from [JWK][1] .
77
+ /// Import [HmacSecretKey] from [JSON Web Key][1] .
78
+ ///
79
+ /// {@macro importJsonWebKey:jwk}
80
+ ///
81
+ /// JSON Web Keys imported using [HmacSecretKey.importJsonWebKey] must
82
+ /// have `"kty": "oct"` , and the [hash] given must match the hash algorithm
83
+ /// implied by the `"alg"` property of the imported [jwk] .
84
+ ///
85
+ /// For importing a JWK with:
86
+ /// * `"alg": "HS1"` use [Hash.sha1] (**SHA-1 is weak**),
87
+ /// * `"alg": "HS256"` use [Hash.sha256] ,
88
+ /// * `"alg": "HS384"` use [Hash.sha384] , and,
89
+ /// * `"alg": "HS512"` use [Hash.sha512] .
90
+ ///
91
+ /// If specified the `"use"` property of the imported [jwk] must be
92
+ /// `"use": "sig"` .
93
+ ///
94
+ /// {@macro importJsonWebKey:throws-FormatException-if-jwk}
95
+ ///
96
+ /// **Example**
97
+ /// ```dart
98
+ /// import 'package:webcrypto/webcrypto.dart';
99
+ /// import 'dart:convert' show jsonEncode, jsonDecode;
100
+ ///
101
+ /// // JSON Web Key as a string containing JSON.
102
+ /// final jwk = '{"kty": "oct", "alg": "HS256", "k": ...}';
103
+ ///
104
+ /// // Import private key from decoded JSON.
105
+ /// final privateKey = await HmacSecretKey.importJsonWebKey(
106
+ /// jsonDecode(jwk),
107
+ /// Hash.sha256, // Must match the hash used the JWK key "alg"
108
+ /// );
75
109
///
76
- /// TODO: finish documentation.
110
+ /// // Export the key (print it in same format as it was given).
111
+ /// Map<String, dynamic> keyData = await privateKey.exportJsonWebKey();
112
+ /// print(jsonEncode(keyData));
113
+ /// ```
77
114
///
78
115
/// [1] : https://tools.ietf.org/html/rfc7517
79
116
static Future <HmacSecretKey > importJsonWebKey (
117
+ // TODO: Determine if the "alg" property can be omitted, and update documentation accordingly
118
+ // also make tests covering cases where "alg" is omitted.
119
+ // TODO: Determine if there is any restrictions on "use" and "key_ops".
80
120
Map <String , dynamic > jwk,
81
121
// TODO: Discuss if hash parameter is really necessary, it's in the JWK.
82
122
// Presumably webcrypto requires as a sanity check. Notice, that this
@@ -150,11 +190,14 @@ abstract class HmacSecretKey {
150
190
/// print(base64.encode(signature));
151
191
/// ```
152
192
///
153
- /// **Warning**, this method should **not** be used for **validating**
154
- /// other signatures by generating a new signature and then comparing the two.
155
- /// While this technically works, you application might be vulnerable to
156
- /// timing attacks. To validate signatures use [verifyBytes()] , this method
157
- /// computes a signature and does a fixed-time comparison.
193
+ /// {@template HMAC-sign:do-not-validate-using-sign}
194
+ /// This method should not be used for **validating** other signatures by
195
+ /// generating a new signature and then comparing the two signatures.
196
+ /// While this technically works, your application might be vulnerable to
197
+ /// timing attacks. To validate signatures use [verifyBytes] or [verifyStream]
198
+ /// instead, these methods computes a signature and does a
199
+ /// fixed-time comparison.
200
+ /// {@template}
158
201
Future <Uint8List > signBytes (List <int > data);
159
202
160
203
/// Compute an HMAC signature of given [data] stream.
@@ -181,23 +224,22 @@ abstract class HmacSecretKey {
181
224
/// print(base64.encode(signature));
182
225
/// ```
183
226
///
184
- /// **Warning**, this method should **not** be used for **validating**
185
- /// other signatures by generating a new signature and then comparing the two.
186
- /// While this technically works, you application might be vulnerable to
187
- /// timing attacks. To validate signatures use [verifyStream()] , this method
188
- /// computes a signature and does a fixed-time comparison.
227
+ /// {@macro HMAC-sign:do-not-validate-using-sign}
189
228
Future <Uint8List > signStream (Stream <List <int >> data);
190
229
191
230
/// Verify the HMAC [signature] of given [data] .
192
231
///
193
232
/// This computes an HMAC signature of the [data] in the same manner
194
- /// as [signBytes() ] and conducts a fixed-time comparison against [signature] ,
233
+ /// as [signBytes] and conducts a fixed-time comparison against [signature] ,
195
234
/// returning `true` if the two signatures are equal.
196
235
///
197
- /// Notice that it's possible to compute a signature for [data] using
198
- /// [signBytes()] and then simply compare the two signatures. This is strongly
199
- /// discouraged as it is easy to introduce side-channels opening your
200
- /// application to timing attacks. Use this method to verify signatures.
236
+ /// {@template HMAC-verify:do-not-validate-using-sign}
237
+ /// It is possible to compute a signature for [data] using
238
+ /// [signBytes] or [signStream] and then simply compare the two signatures.
239
+ /// This is strongly discouraged as it is easy to introduce side-channels
240
+ /// opening your application to timing attacks.
241
+ /// Use [verifyBytes] or [verifyStream] to verify signatures.
242
+ /// {@endtemplate}
201
243
///
202
244
/// **Example**
203
245
/// ```dart
@@ -224,13 +266,10 @@ abstract class HmacSecretKey {
224
266
/// Verify the HMAC [signature] of given [data] stream.
225
267
///
226
268
/// This computes an HMAC signature of the [data] stream in the same manner
227
- /// as [signBytes() ] and conducts a fixed-time comparison against [signature] ,
269
+ /// as [signStream ] and conducts a fixed-time comparison against [signature] ,
228
270
/// returning `true` if the two signatures are equal.
229
271
///
230
- /// Notice that it's possible to compute a signature for [data] using
231
- /// [signBytes()] and then simply compare the two signatures. This is strongly
232
- /// discouraged as it is easy to introduce side-channels opening your
233
- /// application to timing attacks. Use this method to verify signatures.
272
+ /// {@macro HMAC-verify:do-not-validate-using-sign}
234
273
///
235
274
/// **Example**
236
275
/// ```dart
@@ -278,9 +317,26 @@ abstract class HmacSecretKey {
278
317
/// ```
279
318
Future <Uint8List > exportRawKey ();
280
319
281
- /// Export [HmacSecretKey] from [JWK][1] .
320
+ /// Export [HmacSecretKey] from [JSON Web Key][1] .
321
+ ///
322
+ /// {@macro exportJsonWebKey:returns}
323
+ ///
324
+ /// **Example**
325
+ /// ```dart
326
+ /// import 'package:webcrypto/webcrypto.dart';
327
+ /// import 'dart:convert' show jsonEncode;
328
+ ///
329
+ /// // Generate a new random HMAC secret key.
330
+ /// final key = await HmacSecretKey.generate(Hash.sha256);
331
+ ///
332
+ /// // Export the private key.
333
+ /// final jwk = await key.exportJsonWebKey();
282
334
///
283
- /// TODO: finish documentation.
335
+ /// // The Map returned by `exportJsonWebKey()` can be converted to JSON with
336
+ /// // `jsonEncode` from `dart:convert`, this will print something like:
337
+ /// // {"kty": "oct", "alg": "HS256", "k": ...}
338
+ /// print(jsonEncode(jwk));
339
+ /// ```
284
340
///
285
341
/// [1] : https://tools.ietf.org/html/rfc7517
286
342
Future <Map <String , dynamic >> exportJsonWebKey ();
0 commit comments