google / google/webcrypto.dart

Explore wrap/unwrap/derive-key and capabilities

Open
#53 1 comment 0 reactions 0 assignees View on GitHub
enhancement
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.