google / google/webcrypto.dart
Explore wrap/unwrap/derive-key and capabilities
- Dominant language
- Dart
- Stars
- 116
- Forks
- 110
- Avg merge
- 6d 8h
- Merged PRs (30d)
- 9
Description
The [web cryptography specification][1] the `CryptoKey` interface have the following attributes not currently supported in this package:
* `usages: 'encrypt' | 'decrypt' | 'sign' | 'verify' | 'deriveKey' | 'deriveBits' | 'wrapKey' | 'unwrapKey'`
* `extractable: true | false`
These capabilities limits what a key object can be used for. In a Dart API trying to export a key with `extractable: false` would probably throw a `StateError`.
The web crypto spec also have the following operations not currently supported in this package:
* `deriveKey` -- equivalent to derive bits and import key.
* `wrapKey` -- equivalent to exporting the key and encrypting it.
* `unwrapKey` -- equivalent to decrypting and importing key.
If we wish to add support for capabilities, then we need to also support wrap/unwrap/derive-key and vice-versus.
## Support for capabilities (`usages`/`extractable`) and wrap/unwrap/derive-key operations are mutually dependent.
Summary:
* A key exported in JWK format encodes capabilities (`usages`/`extractable`), thus, unwrapping in the browser enforces capabilities.
* Supporting capabilities (`usages`/`extractable`) without supporting all operations that can be encoded is weird.
When exporting a key in JWK format the `extractable` bit and the `usages` are encoded in the exported JSON Web Key.
If we unwrap an encrypted JWK key using `window.crypto` in the browser, then whatever capabilities was encoded in the encrypted JWK is what the decrypted+imported `CryptoKey` will have. And the browser won't give us an opportunity to increase the capabilities of the unwrapped key. So we abstractions that wrap `CryptoKey` in Dart must support capabilities, if we wish to support the `unwrapKey` operation.
This could be worked around by:
* Using `decrypt` + `import` instead of the `unwrap` operation.
* This would a bit of a hack in the browser, and reduce the security features expected by the user in the browser environment.
* It would not work for `AES-KW` which supports `wrap`/`unwrap` operations, but not `encrypt`/`decrypt`. But JWK does not work will with `AES-KW`, see https://github.com/w3c/webcrypto/issues/187
* Not supporting wrap/unwrap for JWK formatted keys.
------------------------------------------
A possible API design for this could be something along the lines of what is illustrated here:
```dart
final hmacKey = await HmacSecretKey.generate(Hash.sha256);
final wrappedKeyAsBytes = await secretKey.wrapKey(
// this is a WrapKeyOptions -- just a way type key + format, ensuring you
// can't use a format not supported for a given key.
// Also weird to have a format enum, not used in import/export methods!
hmacKey.wrapRawKeyOptions(),
// options for encryptBytes, differs depending on secretKey type!
iv,
);
final wrappedKeyAsBytes = await secretKey.wrapKey(
hmacKey.wrapJsonWebKeyOptions(),
iv,
);
final hmacKey = await secretKey.unwrapKey(
// This is a UnwrapKeyOptions -- which is format + import options
HmacSecretKey.unwrapJsonWebKeyOptions(Hash.sha256),
wrappedKeyAsBytes,
// options for decryptBytes, differs depending on secretKey type
iv,
);
final hmacKey = await secretKey.unwrapKey(
HmacSecretKey.unwrapRawKeyOptions(Hash.sha256),
wrappedKeyAsBytes,
iv,
);
final hmacKey = await hkdfKey.deriveKey(
// This must be a DeriveKeyOptions, and then deriveKey returns Future
// This allows us to limit what keys can be derived, webcrypto can only derive
// HMAC, AES-CBC, AES-CTR, AES-GCM and AES-KW.
HmacSecretKey.deriveKeyOptions(Hash.sha256),
salt,
info,
);
@sealed
abstract class WrapKeyOptions {}
@sealed
abstract class UnwrapKeyOptions {}
@sealed
abstract class DeriveKeyOptions {}
```
[1]: https://www.w3.org/TR/WebCryptoAPI/#cryptokey-interface
Contributor guide
Assessment
This issue has not been assessed yet.