diff --git a/spec/Overview.html b/spec/Overview.html index 7b3080e..00894cf 100644 --- a/spec/Overview.html +++ b/spec/Overview.html @@ -3361,6 +3361,36 @@
- The "`AES-CTR`" algorithm identifier is used to perform - encryption and decryption using AES in Counter mode, - as described in [[NIST-SP800-38A]]. + The "`Ed25519`" algorithm identifier is used to perform signing + and verification using the Ed25519 algorithm specified in + [[RFC8032]].
The [= recognized algorithm name =] for - this algorithm is "`AES-CTR`". + this algorithm is "`Ed25519`".
encrypt | -{{AesCtrParams}} | +sign | +None | {{ArrayBuffer}} | |
decrypt | -{{AesCtrParams}} | -{{ArrayBuffer}} | +verify | +None | +boolean |
generateKey | -{{AesKeyGenParams}} | -{{CryptoKey}} | +None | +{{CryptoKeyPair}} | |
importKey | @@ -10038,140 +10068,107 @@None | object | |||
get key length | -{{AesDerivedKeyParams}} | -Integer | -
-dictionary AesCtrParams : Algorithm { - required BufferSource counter; - required [EnforceRange] octet length; -}; --
The counter member contains the initial value of the counter block. {{AesCtrParams/counter}} MUST be 16 bytes (the AES block size). The counter bits are the rightmost length - bits of the counter block. The rest of the counter block is for - the nonce. The counter bits are incremented using the standard - incrementing function specified in NIST SP 800-38A Appendix B.1: - the counter bits are interpreted as a big-endian integer and - incremented by one.
-The length member contains the length, in bits, of the rightmost part of the counter block that is incremented.
--dictionary AesKeyAlgorithm : KeyAlgorithm { - required unsigned short length; -}; --
The length member represents the length, in bits, of the key.
--dictionary AesKeyGenParams : Algorithm { - required [EnforceRange] unsigned short length; -}; --
The length member represents the length, in bits, of the key.
--dictionary AesDerivedKeyParams : Algorithm { - required [EnforceRange] unsigned short length; -}; --
The length member represents the length, in bits, of the key.
-- If the {{AesCtrParams/counter}} member of - |normalizedAlgorithm| does not have length 16 - bytes, - then [= exception/throw =] an - {{OperationError}}. -
-- If the {{AesCtrParams/length}} member of - |normalizedAlgorithm| is zero or is greater - than 128, - then [= exception/throw =] an - {{OperationError}}. + If the {{CryptoKey/[[type]]}} internal slot of + |key| is not {{KeyType/"private"}}, then [= exception/throw =] an {{InvalidAccessError}}.
- Let |ciphertext| be the result of performing the CTR Encryption - operation described in Section 6.5 of [[NIST-SP800-38A]] using AES as the block cipher, the contents of the {{AesCtrParams/counter}} member of - |normalizedAlgorithm| as the initial value of the counter block, the - {{AesCtrParams/length}} member of - |normalizedAlgorithm| as the input parameter |m| to the - standard counter block incrementing function defined in Appendix B.1 of - [[NIST-SP800-38A]] and the contents of - |plaintext| as the input plaintext. + Perform the Ed25519 signing process, as specified in [[RFC8032]], + Section 5.1.6, with |message| as |M|, + using the Ed25519 private key associated with |key|.
++ Some implementations may (wish to) generate randomized signatures + as per draft-irtf-cfrg-det-sigs-with-noise + instead of deterministic signatures as per [[RFC8032]]. +
+ +- Return the result of [= ArrayBuffer/create | creating =] - an {{ArrayBuffer}} containing |ciphertext|. + Return a new {{ArrayBuffer}} associated with the + [= relevant global object =] + of `this` [[HTML]], and containing the + bytes of the signature resulting from performing + the Ed25519 signing process.
- If the {{AesCtrParams/counter}} member of - |normalizedAlgorithm| does not have length 16 - bytes, - then [= exception/throw =] an - {{OperationError}}. + If the {{CryptoKey/[[type]]}} internal slot of + |key| is not {{KeyType/"public"}}, then [= exception/throw =] an {{InvalidAccessError}}.
- If the {{AesCtrParams/length}} member of - |normalizedAlgorithm| is zero or is greater - than 128, - then [= exception/throw =] an - {{OperationError}}. + If the key data of |key| represents an invalid point or a small-order element + on the Elliptic Curve of Ed25519, return `false`.
++ Not all implementations perform this check. +
+ +- Let |plaintext| be the result of performing the CTR Decryption - operation described in Section 6.5 of [[NIST-SP800-38A]] using AES as the block cipher, the contents of the {{AesCtrParams/counter}} member of - |normalizedAlgorithm| as the initial value of the counter block, the - {{AesCtrParams/length}} member of - |normalizedAlgorithm| as the input parameter |m| to the - standard counter block incrementing function defined in Appendix B.1 of - [[NIST-SP800-38A]] and the contents of - |ciphertext| as the input ciphertext. + If the point R, encoded in the first half of |signature|, + represents an invalid point or a small-order element + on the Elliptic Curve of Ed25519, return `false`. +
++ Not all implementations perform this check. +
+ ++ Perform the Ed25519 verification steps, as specified in [[RFC8032]], + Section 5.1.7, using the cofactorless (unbatched) equation, + `[S]B = R + [k]A'`, on the |signature|, with |message| as |M|, + using the Ed25519 public key associated with |key|.
- Return the result of [= ArrayBuffer/create | creating =] - an {{ArrayBuffer}} containing |plaintext|. + Let |result| be a boolean with the value `true` if the signature is valid + and the value `false` otherwise. +
++ Return |result|.
- If |usages| contains any entry which is not - one of "`encrypt`", "`decrypt`", - "`wrapKey`" or "`unwrapKey`", + If |usages| contains a value which is not + one of "`sign`" or "`verify`", then [= exception/throw =] a {{SyntaxError}}.
- If the {{AesKeyGenParams/length}} member of - |normalizedAlgorithm| is not equal to one of - 128, 192 or 256, - then [= exception/throw =] an - {{OperationError}}. + Generate an Ed25519 key pair, as defined in [[RFC8032]], section 5.1.5.
- Generate an AES key of length - equal to the {{AesKeyGenParams/length}} member of - |normalizedAlgorithm|. + Let |algorithm| be a new {{KeyAlgorithm}} object.
- If the key generation step fails, - then [= exception/throw =] an - {{OperationError}}. + Set the {{KeyAlgorithm/name}} attribute of + |algorithm| to "`Ed25519`".
- Let |key| be a new - {{CryptoKey}} object representing the - generated AES key. + Let |publicKey| be a new {{CryptoKey}} associated with the + [= relevant global object =] + of `this` [[HTML]], and + representing the public key of the generated key pair.
- Let |algorithm| be a new - {{AesKeyAlgorithm}}. + Set the {{CryptoKey/[[type]]}} internal slot of + |publicKey| to "`public`"
- Set the {{KeyAlgorithm/name}} attribute of - |algorithm| to "`AES-CTR`". + Set the {{CryptoKey/[[algorithm]]}} internal + slot of |publicKey| to |algorithm|.
- Set the {{AesKeyAlgorithm/length}} attribute of - |algorithm| to equal the - {{AesKeyGenParams/length}} member of - |normalizedAlgorithm|. + Set the {{CryptoKey/[[extractable]]}} internal + slot of |publicKey| to true. +
++ Set the {{CryptoKey/[[usages]]}} internal slot of + |publicKey| to be the [= usage intersection =] + of |usages| and `[ "verify" ]`. +
++ Let |privateKey| be a new {{CryptoKey}} associated with the + [= relevant global object =] + of `this` [[HTML]], and + representing the private key of the generated key pair.
Set the {{CryptoKey/[[type]]}} internal slot of - |key| to {{KeyType/"secret"}}. + |privateKey| to {{KeyType/"private"}}
Set the {{CryptoKey/[[algorithm]]}} internal - slot of |key| to |algorithm|. + slot of |privateKey| to |algorithm|.
Set the {{CryptoKey/[[extractable]]}} internal - slot of |key| to be |extractable|. + slot of |privateKey| to |extractable|.
Set the {{CryptoKey/[[usages]]}} internal slot of - |key| to be |usages|. + |privateKey| to be the [= usage intersection =] + of |usages| and `[ "sign" ]`.
- Return |key|. + Let |result| be a new {{CryptoKeyPair}} + dictionary. +
++ Set the {{CryptoKeyPair/publicKey}} attribute + of |result| to be |publicKey|. +
++ Set the {{CryptoKeyPair/privateKey}} attribute + of |result| to be |privateKey|. +
++ Return the result of converting |result| to an ECMAScript Object, as + defined by [[WebIDL]].
- If |usages| contains an entry which is not - one of "`encrypt`", "`decrypt`", - "`wrapKey`" or "`unwrapKey`", - then [= exception/throw =] a +
Let |keyData| be the key data to be imported.
++ If |usages| contains a value which is not + "`verify`" + then [= exception/throw =] a + {{SyntaxError}}. +
++ Let |spki| be the result of running the + [= parse a subjectPublicKeyInfo =] + algorithm over |keyData|. +
++ If an error occurred while parsing, + then [= exception/throw =] a + {{DataError}}. +
++ If the `algorithm` object identifier field of the + `algorithm` AlgorithmIdentifier field of |spki| is + not equal to the `id-Ed25519` + object identifier defined in [[RFC8410]], + then [= exception/throw =] a + {{DataError}}. +
++ If the `parameters` field of the `algorithm` + AlgorithmIdentifier field of |spki| is present, + then [= exception/throw =] a + {{DataError}}. +
++ Let |publicKey| be the Ed25519 public key identified by + the `subjectPublicKey` field of |spki|. +
++ Let |key| be a new {{CryptoKey}} associated with the + [= relevant global object =] + of `this` [[HTML]], and + that represents |publicKey|. +
++ Set the {{CryptoKey/[[type]]}} internal slot + of |key| to "`public`" +
++ Let |algorithm| be a new {{KeyAlgorithm}}. +
++ Set the {{KeyAlgorithm/name}} attribute of + |algorithm| to "`Ed25519`". +
++ Set the {{CryptoKey/[[algorithm]]}} + internal slot of |key| to |algorithm|. +
++ If |usages| contains a value which is not + "`sign`" + then [= exception/throw =] a + {{SyntaxError}}. +
++ Let |privateKeyInfo| be the result of running the + [= parse a privateKeyInfo =] + algorithm over |keyData|. +
++ If an error occurs while parsing, + then [= exception/throw =] a + {{DataError}}. +
++ If the `algorithm` object identifier field of the + `privateKeyAlgorithm` PrivateKeyAlgorithm field of + |privateKeyInfo| is not equal to the + `id-Ed25519` object identifier defined in [[RFC8410]], + then [= exception/throw =] a + {{DataError}}. +
++ If the `parameters` field of the + `privateKeyAlgorithm` PrivateKeyAlgorithmIdentifier field + of |privateKeyInfo| is present, + then [= exception/throw =] a + {{DataError}}. +
++ Let |curvePrivateKey| be the result of performing the + [= parse an ASN.1 structure =] + algorithm, with |data| as the `privateKey` field + of |privateKeyInfo|, |structure| as the ASN.1 + `CurvePrivateKey` structure specified in Section 7 of [[RFC8410]], and |exactData| set to true. +
++ If an error occurred while parsing, + then [= exception/throw =] a + {{DataError}}. +
++ Let |key| be a new {{CryptoKey}} associated with the + [= relevant global object =] + of `this` [[HTML]], and + that represents the Ed25519 private key identified by |curvePrivateKey|. +
++ Set the {{CryptoKey/[[type]]}} internal slot + of |key| to {{KeyType/"private"}} +
++ Let |algorithm| be a new {{KeyAlgorithm}}. +
++ Set the {{KeyAlgorithm/name}} attribute of + |algorithm| to "`Ed25519`". +
++ Set the {{CryptoKey/[[algorithm]]}} + internal slot of |key| to |algorithm|. +
+Let |jwk| equal |keyData|.
[= exception/Throw =] a {{DataError}}.
+ If the {{JsonWebKey/d}} field is present and |usages| contains + a value which is not + "`sign`", or, + if the {{JsonWebKey/d}} field is not present and |usages| contains + a value which is not + "`verify`" + then [= exception/throw =] a + {{SyntaxError}}. +
++ If the {{JsonWebKey/kty}} field of |jwk| is not + "`OKP`", + then [= exception/throw =] a + {{DataError}}. +
++ If the {{JsonWebKey/crv}} field of |jwk| is not + "`Ed25519`", + then [= exception/throw =] a + {{DataError}}. +
++ If |usages| is non-empty and the {{JsonWebKey/use}} field of |jwk| is present and is + not "`sig`", + then [= exception/throw =] a + {{DataError}}. +
++ If the {{JsonWebKey/key_ops}} field of |jwk| is present, and + is invalid according to the requirements of JSON Web + Key [[JWK]], or it does not contain all of the specified |usages| + values, + then [= exception/throw =] a + {{DataError}}. +
++ If the {{JsonWebKey/ext}} field of |jwk| is present and + has the value false and |extractable| is true, + then [= exception/throw =] a + {{DataError}}. +
++ If |jwk| does not meet the requirements of + the JWK private key format described in Section 2 + of [[RFC8037]], then [= exception/throw =] a {{DataError}}. +
++ Let |key| be a new {{CryptoKey}} object that represents the + Ed25519 private key identified by interpreting + |jwk| according to Section 2 of [[RFC8037]]. +
++ Set the {{CryptoKey/[[type]]}} + internal slot of |Key| to {{KeyType/"private"}}. +
++ If |jwk| does not meet the requirements of + the JWK public key format described in Section 2 + of [[RFC8037]], then [= exception/throw =] a {{DataError}}. +
++ Let |key| be a new {{CryptoKey}} object that represents the + Ed25519 public key identified by interpreting + |jwk| according to Section 2 of [[RFC8037]]. +
++ Set the {{CryptoKey/[[type]]}} + internal slot of |Key| to {{KeyType/"public"}}. +
++ Let |algorithm| be a new instance of a {{KeyAlgorithm}} object. +
++ Set the {{KeyAlgorithm/name}} attribute of + |algorithm| to "`Ed25519`". +
++ Set the {{CryptoKey/[[algorithm]]}} + internal slot of |key| to |algorithm|. +
++ If |usages| contains a value which is not + "`verify`" + then [= exception/throw =] a + {{SyntaxError}}. +
++ Let |algorithm| be a new {{KeyAlgorithm}} object. +
++ Set the {{KeyAlgorithm/name}} attribute of + |algorithm| to "`Ed25519`". +
++ Let |key| be a new {{CryptoKey}} associated with the + [= relevant global object =] + of `this` [[HTML]], and + representing the key data provided in |keyData|. +
++ Set the {{CryptoKey/[[type]]}} internal slot + of |key| to "`public`" +
++ Set the {{CryptoKey/[[algorithm]]}} + internal slot of |key| to |algorithm|. +
++ [= exception/throw =] a + {{NotSupportedError}}. +
++ Return |key| +
++ Let |key| be the {{CryptoKey}} to be + exported. +
++ If the underlying cryptographic key material represented by the {{CryptoKey/[[handle]]}} internal slot of |key| + cannot be accessed, then [= exception/throw =] an {{OperationError}}. +
++ If the {{CryptoKey/[[type]]}} internal slot + of |key| is not "`public`", then [= exception/throw =] an {{InvalidAccessError}}. +
++ Let |data| be an instance of the `subjectPublicKeyInfo` + ASN.1 structure defined in [[RFC5280]] + with the following properties: +
++ Set the |algorithm| field to an + `AlgorithmIdentifier` ASN.1 type with the following + properties: +
++ Set the |algorithm| object identifier to the + `id-Ed25519` OID defined in [[RFC8410]]. +
++ Set the |subjectPublicKey| field to |keyData|. +
++ Let |result| be a new {{ArrayBuffer}} associated with the + [= relevant global object =] + of `this` [[HTML]], and containing + |data|. +
++ If the {{CryptoKey/[[type]]}} internal slot + of |key| is not {{KeyType/"private"}}, then [= exception/throw =] an {{InvalidAccessError}}. +
++ Let |data| be an instance of the `privateKeyInfo` + ASN.1 structure defined in [[RFC5208]] + with the following properties: +
++ Set the |version| field to `0`. +
++ Set the |privateKeyAlgorithm| field to a + `PrivateKeyAlgorithmIdentifier` ASN.1 type with the + following properties: +
++ Set the |algorithm| object identifier to the + `id-Ed25519` OID defined in [[RFC8410]]. +
++ Set the |privateKey| field to the result of DER-encoding + a `CurvePrivateKey` ASN.1 type, as defined in Section 7 of [[RFC8410]], that represents the + Ed25519 private key represented by the {{CryptoKey/[[handle]]}} internal slot of + |key| +
++ Let |result| be a new {{ArrayBuffer}} associated with the + [= relevant global object =] + of `this` [[HTML]], and containing + |data|. +
++ Let |jwk| be a new {{JsonWebKey}} + dictionary. +
++ Set the `kty` attribute of |jwk| to + "`OKP`". +
++ Set the `crv` attribute of |jwk| to + "`Ed25519`". +
++ Set the {{JsonWebKey/x}} attribute of |jwk| according to the + definition in Section 2 of [[RFC8037]]. +
++ Set the `key_ops` attribute of |jwk| to the {{CryptoKey/usages}} attribute of |key|. +
++ Set the `ext` attribute of |jwk| to the {{CryptoKey/[[extractable]]}} internal slot + of |key|. +
++ Let |result| be the result of converting |jwk| + to an ECMAScript Object, as defined by [[WebIDL]]. +
++ If the {{CryptoKey/[[type]]}} internal slot + of |key| is not "`public`", then [= exception/throw =] an {{InvalidAccessError}}. +
++ Let |data| be an [= octet string =] representing the Ed25519 + public key represented by the {{CryptoKey/[[handle]]}} internal slot of + |key|. +
++ Let |result| be a new {{ArrayBuffer}} associated with the + [= relevant global object =] + of `this` [[HTML]], and containing + |data|. +
++ [= exception/throw =] a + {{NotSupportedError}}. +
++ Return |result|. +
++ The "`X25519`" algorithm identifier is used to perform + key agreement using the X25519 algorithm specified in + [[RFC7748]]. +
++ The [= recognized algorithm name =] for + this algorithm is "`X25519`". +
+Operation | +Parameters | +Result | +
---|---|---|
deriveBits | +{{EcdhKeyDeriveParams}} | +[= octet string =] | +
generateKey | +None | +{{CryptoKeyPair}} | +
importKey | +None | +{{CryptoKey}} | +
exportKey | +None | +object | +
+ If the {{CryptoKey/[[type]]}} internal slot of + |key| is not {{KeyType/"private"}}, then [= exception/throw =] an {{InvalidAccessError}}. +
++ Let |publicKey| be the + {{EcdhKeyDeriveParams/public}} member of + |normalizedAlgorithm|. +
++ If the {{CryptoKey/[[type]]}} internal slot of + |publicKey| is not "`public`", then [= exception/throw =] an {{InvalidAccessError}}. +
++ If the {{KeyAlgorithm/name}} attribute of + the {{CryptoKey/[[algorithm]]}} internal slot of + |publicKey| is not equal to the {{KeyAlgorithm/name}} property of the {{CryptoKey/[[algorithm]]}} internal slot of + |key|, then [= exception/throw =] an {{InvalidAccessError}}. +
++ Let |secret| be the result of performing the X25519 function specified in + [[RFC7748]] Section 5 with |key| as the X25519 private key |k| + and the X25519 public key represented by the {{CryptoKey/[[handle]]}} + internal slot of |publicKey| as the X25519 public key |u|. +
++ If |secret| is the all-zero value, + then [= exception/throw =] a {{OperationError}}. + This check must be performed in constant-time, as per [[RFC7748]] Section 6.1. +
++ If |usages| contains an entry which is not + "`deriveKey`" or "`deriveBits`" + then [= exception/throw =] a + {{SyntaxError}}. +
++ Generate an X25519 key pair, with the private key being 32 random bytes, + and the public key being `X25519(a, 9)`, + as defined in [[RFC7748]], section 6.1. +
++ Let |algorithm| be a new {{KeyAlgorithm}} object. +
++ Set the {{KeyAlgorithm/name}} attribute of + |algorithm| to "`X25519`". +
++ Let |publicKey| be a new {{CryptoKey}} associated with the + [= relevant global object =] + of `this` [[HTML]], and + representing the public key of the generated key pair. +
++ Set the {{CryptoKey/[[type]]}} internal slot of + |publicKey| to "`public`" +
++ Set the {{CryptoKey/[[algorithm]]}} internal + slot of |publicKey| to |algorithm|. +
++ Set the {{CryptoKey/[[extractable]]}} internal + slot of |publicKey| to true. +
++ Set the {{CryptoKey/[[usages]]}} internal slot of + |publicKey| to be the empty list. +
++ Let |privateKey| be a new {{CryptoKey}} associated with the + [= relevant global object =] + of `this` [[HTML]], and + representing the private key of the generated key pair. +
++ Set the {{CryptoKey/[[type]]}} internal slot of + |privateKey| to {{KeyType/"private"}} +
++ Set the {{CryptoKey/[[algorithm]]}} internal + slot of |privateKey| to |algorithm|. +
++ Set the {{CryptoKey/[[extractable]]}} internal + slot of |privateKey| to |extractable|. +
++ Set the {{CryptoKey/[[usages]]}} internal slot of + |privateKey| to be the + [= usage intersection =] of + |usages| and `[ "deriveKey", "deriveBits" ]`. +
++ Let |result| be a new {{CryptoKeyPair}} + dictionary. +
++ Set the {{CryptoKeyPair/publicKey}} attribute + of |result| to be |publicKey|. +
++ Set the {{CryptoKeyPair/privateKey}} attribute + of |result| to be |privateKey|. +
++ Return the result of converting |result| to an ECMAScript Object, as + defined by [[WebIDL]]. +
+Let |keyData| be the key data to be imported.
++ If |usages| is not empty + then [= exception/throw =] a + {{SyntaxError}}. +
++ Let |spki| be the result of running the + [= parse a subjectPublicKeyInfo =] + algorithm over |keyData|. +
++ If an error occurred while parsing, + then [= exception/throw =] a + {{DataError}}. +
++ If the `algorithm` object identifier field of the + `algorithm` AlgorithmIdentifier field of |spki| is + not equal to the `id-X25519` + object identifier defined in [[RFC8410]], + then [= exception/throw =] a + {{DataError}}. +
++ If the `parameters` field of the `algorithm` + AlgorithmIdentifier field of |spki| is present, + then [= exception/throw =] a + {{DataError}}. +
++ Let |publicKey| be the X25519 public key identified by + the `subjectPublicKey` field of |spki|. +
++ Let |key| be a new {{CryptoKey}} associated with the + [= relevant global object =] + of `this` [[HTML]], and + that represents |publicKey|. +
++ Set the {{CryptoKey/[[type]]}} internal slot + of |key| to "`public`" +
++ Let |algorithm| be a new {{KeyAlgorithm}}. +
++ Set the {{KeyAlgorithm/name}} attribute of + |algorithm| to "`X25519`". +
++ Set the {{CryptoKey/[[algorithm]]}} + internal slot of |key| to |algorithm|. +
++ If |usages| contains an entry which is not + "`deriveKey`" or "`deriveBits`" + then [= exception/throw =] a + {{SyntaxError}}. +
++ Let |privateKeyInfo| be the result of running the + [= parse a privateKeyInfo =] + algorithm over |keyData|. +
++ If an error occurs while parsing, + then [= exception/throw =] a + {{DataError}}. +
++ If the `algorithm` object identifier field of the + `privateKeyAlgorithm` PrivateKeyAlgorithm field of + |privateKeyInfo| is not equal to the + `id-X25519` object identifier defined in [[RFC8410]], + then [= exception/throw =] a + {{DataError}}. +
++ If the `parameters` field of the + `privateKeyAlgorithm` PrivateKeyAlgorithmIdentifier field + of |privateKeyInfo| is present, + then [= exception/throw =] a + {{DataError}}. +
++ Let |curvePrivateKey| be the result of performing the + [= parse an ASN.1 structure =] + algorithm, with |data| as the `privateKey` field + of |privateKeyInfo|, |structure| as the ASN.1 + `CurvePrivateKey` structure specified in Section 7 of [[RFC8410]], and |exactData| set to true. +
++ If an error occurred while parsing, + then [= exception/throw =] a + {{DataError}}. +
++ Let |key| be a new {{CryptoKey}} associated with the + [= relevant global object =] + of `this` [[HTML]], and + that represents the X25519 private key identified by |curvePrivateKey|. +
++ Set the {{CryptoKey/[[type]]}} internal slot + of |key| to {{KeyType/"private"}} +
++ Let |algorithm| be a new {{KeyAlgorithm}}. +
++ Set the {{KeyAlgorithm/name}} attribute of + |algorithm| to "`X25519`". +
++ Set the {{CryptoKey/[[algorithm]]}} + internal slot of |key| to |algorithm|. +
+Let |jwk| equal |keyData|.
[= exception/Throw =] a {{DataError}}.
+ If the {{JsonWebKey/d}} field is present and if |usages| + contains an entry which is not + "`deriveKey`" or "`deriveBits`" + then [= exception/throw =] a + {{SyntaxError}}. +
++ If the {{JsonWebKey/d}} field is not present and if |usages| is not + empty + then [= exception/throw =] a + {{SyntaxError}}. +
++ If the {{JsonWebKey/kty}} field of |jwk| is not + "`OKP`", + then [= exception/throw =] a + {{DataError}}. +
++ If the {{JsonWebKey/crv}} field of |jwk| is not + "`X25519`", + then [= exception/throw =] a + {{DataError}}. +
++ If |usages| is non-empty and the {{JsonWebKey/use}} field of |jwk| is present + and is not equal to "`enc`" then [= exception/throw =] a + {{DataError}}. +
++ If the {{JsonWebKey/key_ops}} field of |jwk| is present, and + is invalid according to the requirements of JSON Web + Key [[JWK]], or it does not contain all of the specified |usages| + values, + then [= exception/throw =] a + {{DataError}}. +
++ If the {{JsonWebKey/ext}} field of |jwk| is present and + has the value false and |extractable| is true, + then [= exception/throw =] a + {{DataError}}. +
++ If |jwk| does not meet the requirements of + the JWK private key format described in Section 2 + of [[RFC8037]], then [= exception/throw =] a {{DataError}}. +
++ Let |key| be a new {{CryptoKey}} object that represents the + X25519 private key identified by interpreting + |jwk| according to Section 2 of [[RFC8037]]. +
++ Set the {{CryptoKey/[[type]]}} + internal slot of |Key| to {{KeyType/"private"}}. +
++ If |jwk| does not meet the requirements of + the JWK public key format described in Section 2 + of [[RFC8037]], then [= exception/throw =] a {{DataError}}. +
++ Let |key| be a new {{CryptoKey}} object that represents the + X25519 public key identified by interpreting + |jwk| according to Section 2 of [[RFC8037]]. +
++ Set the {{CryptoKey/[[type]]}} + internal slot of |Key| to {{KeyType/"public"}}. +
++ Let |algorithm| be a new instance of a {{KeyAlgorithm}} object. +
++ Set the {{KeyAlgorithm/name}} attribute of + |algorithm| to "`X25519`". +
++ Set the {{CryptoKey/[[algorithm]]}} + internal slot of |key| to |algorithm|. +
++ If |usages| is not empty + then [= exception/throw =] a + {{SyntaxError}}. +
++ Let |algorithm| be a new {{KeyAlgorithm}} object. +
++ Set the {{KeyAlgorithm/name}} attribute of + |algorithm| to "`X25519`". +
++ Let |key| be a new {{CryptoKey}} associated with the + [= relevant global object =] + of `this` [[HTML]], and + representing the key data provided in |keyData|. +
++ Set the {{CryptoKey/[[type]]}} internal slot + of |key| to "`public`" +
++ Set the {{CryptoKey/[[algorithm]]}} + internal slot of |key| to |algorithm|. +
++ [= exception/throw =] a + {{NotSupportedError}}. +
++ Return |key| +
++ Let |key| be the {{CryptoKey}} to be + exported. +
++ If the underlying cryptographic key material represented by the {{CryptoKey/[[handle]]}} internal slot of |key| + cannot be accessed, then [= exception/throw =] an {{OperationError}}. +
++ If the {{CryptoKey/[[type]]}} internal slot + of |key| is not "`public`", then [= exception/throw =] an {{InvalidAccessError}}. +
++ Let |data| be an instance of the `subjectPublicKeyInfo` + ASN.1 structure defined in [[RFC5280]] + with the following properties: +
++ Set the |algorithm| field to an + `AlgorithmIdentifier` ASN.1 type with the following + properties: +
++ Set the |algorithm| object identifier to the + `id-X25519` OID defined in [[RFC8410]]. +
++ Set the |subjectPublicKey| field to |keyData|. +
++ Let |result| be a new {{ArrayBuffer}} associated with the + [= relevant global object =] + of `this` [[HTML]], and containing + |data|. +
++ If the {{CryptoKey/[[type]]}} internal slot + of |key| is not {{KeyType/"private"}}, then [= exception/throw =] an {{InvalidAccessError}}. +
++ Let |data| be an instance of the `privateKeyInfo` + ASN.1 structure defined in [[RFC5208]] + with the following properties: +
++ Set the |version| field to `0`. +
++ Set the |privateKeyAlgorithm| field to a + `PrivateKeyAlgorithmIdentifier` ASN.1 type with the + following properties: +
++ Set the |algorithm| object identifier to the + `id-X25519` OID defined in [[RFC8410]]. +
++ Set the |privateKey| field to the result of DER-encoding + a `CurvePrivateKey` ASN.1 type, as defined in Section 7 of [[RFC8410]], that represents the + X25519 private key represented by the {{CryptoKey/[[handle]]}} internal slot of + |key| +
++ Let |result| be a new {{ArrayBuffer}} associated with the + [= relevant global object =] + of `this` [[HTML]], and containing + |data|. +
++ Let |jwk| be a new {{JsonWebKey}} + dictionary. +
++ Set the `kty` attribute of |jwk| to + "`OKP`". +
++ Set the `crv` attribute of |jwk| to + "`X25519`". +
++ Set the {{JsonWebKey/x}} attribute of |jwk| according to the + definition in Section 2 of [[RFC8037]]. +
++ Set the `key_ops` attribute of |jwk| to the {{CryptoKey/usages}} attribute of |key|. +
++ Set the `ext` attribute of |jwk| to the {{CryptoKey/[[extractable]]}} internal slot + of |key|. +
++ Let |result| be the result of converting |jwk| + to an ECMAScript Object, as defined by [[WebIDL]]. +
++ If the {{CryptoKey/[[type]]}} internal slot + of |key| is not "`public`", then [= exception/throw =] an {{InvalidAccessError}}. +
++ Let |data| be an [= octet string =] representing the X25519 + public key represented by the {{CryptoKey/[[handle]]}} internal slot of + |key|. +
++ Let |result| be a new {{ArrayBuffer}} associated with the + [= relevant global object =] + of `this` [[HTML]], and containing + |data|. +
++ [= exception/throw =] a + {{NotSupportedError}}. +
++ Return |result|. +
++ The "`AES-CTR`" algorithm identifier is used to perform + encryption and decryption using AES in Counter mode, + as described in [[NIST-SP800-38A]]. +
++ The [= recognized algorithm name =] for + this algorithm is "`AES-CTR`". +
+Operation | +Parameters | +Result | +
---|---|---|
encrypt | +{{AesCtrParams}} | +{{ArrayBuffer}} | +
decrypt | +{{AesCtrParams}} | +{{ArrayBuffer}} | +
generateKey | +{{AesKeyGenParams}} | +{{CryptoKey}} | +
importKey | +None | +{{CryptoKey}} | +
exportKey | +None | +object | +
get key length | +{{AesDerivedKeyParams}} | +Integer | +
+dictionary AesCtrParams : Algorithm { + required BufferSource counter; + required [EnforceRange] octet length; +}; ++
The counter member contains the initial value of the counter block. {{AesCtrParams/counter}} MUST be 16 bytes (the AES block size). The counter bits are the rightmost length + bits of the counter block. The rest of the counter block is for + the nonce. The counter bits are incremented using the standard + incrementing function specified in NIST SP 800-38A Appendix B.1: + the counter bits are interpreted as a big-endian integer and + incremented by one.
+The length member contains the length, in bits, of the rightmost part of the counter block that is incremented.
++dictionary AesKeyAlgorithm : KeyAlgorithm { + required unsigned short length; +}; ++
The length member represents the length, in bits, of the key.
++dictionary AesKeyGenParams : Algorithm { + required [EnforceRange] unsigned short length; +}; ++
The length member represents the length, in bits, of the key.
++dictionary AesDerivedKeyParams : Algorithm { + required [EnforceRange] unsigned short length; +}; ++
The length member represents the length, in bits, of the key.
++ If the {{AesCtrParams/counter}} member of + |normalizedAlgorithm| does not have length 16 + bytes, + then [= exception/throw =] an + {{OperationError}}. +
++ If the {{AesCtrParams/length}} member of + |normalizedAlgorithm| is zero or is greater + than 128, + then [= exception/throw =] an + {{OperationError}}. +
++ Let |ciphertext| be the result of performing the CTR Encryption + operation described in Section 6.5 of [[NIST-SP800-38A]] using AES as the block cipher, the contents of the {{AesCtrParams/counter}} member of + |normalizedAlgorithm| as the initial value of the counter block, the + {{AesCtrParams/length}} member of + |normalizedAlgorithm| as the input parameter |m| to the + standard counter block incrementing function defined in Appendix B.1 of + [[NIST-SP800-38A]] and the contents of + |plaintext| as the input plaintext. +
++ Return the result of [= ArrayBuffer/create | creating =] + an {{ArrayBuffer}} containing |ciphertext|. +
++ If the {{AesCtrParams/counter}} member of + |normalizedAlgorithm| does not have length 16 + bytes, + then [= exception/throw =] an + {{OperationError}}. +
++ If the {{AesCtrParams/length}} member of + |normalizedAlgorithm| is zero or is greater + than 128, + then [= exception/throw =] an + {{OperationError}}. +
++ Let |plaintext| be the result of performing the CTR Decryption + operation described in Section 6.5 of [[NIST-SP800-38A]] using AES as the block cipher, the contents of the {{AesCtrParams/counter}} member of + |normalizedAlgorithm| as the initial value of the counter block, the + {{AesCtrParams/length}} member of + |normalizedAlgorithm| as the input parameter |m| to the + standard counter block incrementing function defined in Appendix B.1 of + [[NIST-SP800-38A]] and the contents of + |ciphertext| as the input ciphertext. +
++ Return the result of [= ArrayBuffer/create | creating =] + an {{ArrayBuffer}} containing |plaintext|. +
++ If |usages| contains any entry which is not + one of "`encrypt`", "`decrypt`", + "`wrapKey`" or "`unwrapKey`", + then [= exception/throw =] a + {{SyntaxError}}. +
++ If the {{AesKeyGenParams/length}} member of + |normalizedAlgorithm| is not equal to one of + 128, 192 or 256, + then [= exception/throw =] an + {{OperationError}}. +
++ Generate an AES key of length + equal to the {{AesKeyGenParams/length}} member of + |normalizedAlgorithm|. +
++ If the key generation step fails, + then [= exception/throw =] an + {{OperationError}}. +
++ Let |key| be a new + {{CryptoKey}} object representing the + generated AES key. +
++ Let |algorithm| be a new + {{AesKeyAlgorithm}}. +
++ Set the {{KeyAlgorithm/name}} attribute of + |algorithm| to "`AES-CTR`". +
++ Set the {{AesKeyAlgorithm/length}} attribute of + |algorithm| to equal the + {{AesKeyGenParams/length}} member of + |normalizedAlgorithm|. +
++ Set the {{CryptoKey/[[type]]}} internal slot of + |key| to {{KeyType/"secret"}}. +
++ Set the {{CryptoKey/[[algorithm]]}} internal + slot of |key| to |algorithm|. +
++ Set the {{CryptoKey/[[extractable]]}} internal + slot of |key| to be |extractable|. +
++ Set the {{CryptoKey/[[usages]]}} internal slot of + |key| to be |usages|. +
++ Return |key|. +
++ If |usages| contains an entry which is not + one of "`encrypt`", "`decrypt`", + "`wrapKey`" or "`unwrapKey`", + then [= exception/throw =] a {{SyntaxError}}.
+ This example generates two X25519 key pairs, one for Alice and one for Bob, performs + a key agreement between them, derives a 256-bit AES-GCM key from the result using + HKDF with SHA-256, and encrypts and decrypts some data with it. +
++ // Generate a key pair for Alice. + const alice_x25519_key = await crypto.subtle.generateKey('X25519', false /* extractable */, ['deriveKey']); + const alice_private_key = alice_x25519_key.privateKey; --var encoder = new TextEncoder('utf-8'); - -// Algorithm Object -var algorithmKeyGen = { - name: "RSASSA-PKCS1-v1_5", - // RsaHashedKeyGenParams - modulusLength: 2048, - publicExponent: new Uint8Array([0x01, 0x00, 0x01]), // Equivalent to 65537 - hash: { - name: "SHA-256" - } -}; + // Normally, the public key would be sent by Bob to Alice in advance over some authenticated channel. + // In this example, we'll generate another key pair and use its public key instead. + const bob_x25519_key = await crypto.subtle.generateKey('X25519', false /* extractable */, ['deriveKey']); + const bob_public_key = bob_x25519_key.publicKey; -var algorithmSign = { - name: "RSASSA-PKCS1-v1_5" -}; + // Perform the key agreement. + const alice_x25519_params = { name: 'X25519', public: bob_public_key }; + const alice_shared_key = await crypto.subtle.deriveKey(alice_x25519_params, alice_private_key, 'HKDF', false /* extractable */, ['deriveKey']); -window.crypto.subtle.generateKey(algorithmKeyGen, false, ["sign"]).then( - function(key) { - var dataPart1 = encoder.encode("hello,"); - var dataPart2 = encoder.encode(" world!"); - - return window.crypto.subtle.sign(algorithmSign, key.privateKey, [dataPart1, dataPart2]); - }, - console.error.bind(console, "Unable to generate a key") -).then( - console.log.bind(console, "The signature is: "), - console.error.bind(console, "Unable to sign") -); --
-var encoder = new TextEncoder('utf-8'); -var clearDataArrayBufferView = encoder.encode("Plain Text Data"); - -var aesAlgorithmKeyGen = { - name: "AES-CBC", - // AesKeyGenParams - length: 128 -}; + // Derive a symmetric key from the result. + const salt = crypto.getRandomValues(new Uint8Array(32)); + const info = new TextEncoder().encode('X25519 key agreement for an AES-GCM-256 key'); + const hkdf_params = { name: 'HKDF', hash: 'SHA-256', salt, info }; + const gcm_params = { name: 'AES-GCM', length: 256 }; + const alice_symmetric_key = await crypto.subtle.deriveKey(hkdf_params, alice_shared_key, gcm_params, false /* extractable */, ['encrypt', 'decrypt']); -var aesAlgorithmEncrypt = { - name: "AES-CBC", - // AesCbcParams - iv: window.crypto.getRandomValues(new Uint8Array(16)) -}; + // Encrypt some data with the symmetric key, and send it to Bob. The IV must be passed along as well. + const iv = crypto.getRandomValues(new Uint8Array(12)); + const message = new TextEncoder().encode('Hi Bob!'); + const encrypted = await crypto.subtle.encrypt({ ...gcm_params, iv }, alice_symmetric_key, message); -// Create a key generator to produce a one-time-use AES key to encrypt some data -window.crypto.subtle.generateKey(aesAlgorithmKeyGen, false, ["encrypt"]).then( - function(aesKey) { - return window.crypto.subtle.encrypt(aesAlgorithmEncrypt, aesKey, clearDataArrayBufferView); - } -).then(console.log.bind(console, "The ciphertext is: "), - console.error.bind(console, "Unable to encrypt")); -+ // On Bob's side, Alice's public key and Bob's private key are used, instead. + // To get the same result, Alice and Bob must agree on using the same salt and info. + const alice_public_key = alice_x25519_key.publicKey; + const bob_private_key = bob_x25519_key.privateKey; + const bob_x25519_params = { name: 'X25519', public: alice_public_key }; + const bob_shared_key = await crypto.subtle.deriveKey(bob_x25519_params, bob_private_key, 'HKDF', false /* extractable */, ['deriveKey']); + const bob_symmetric_key = await crypto.subtle.deriveKey(hkdf_params, bob_shared_key, gcm_params, false /* extractable */, ['encrypt', 'decrypt']); + + // On Bob's side, the data can be decrypted. + const decrypted = await crypto.subtle.decrypt({ ...gcm_params, iv }, bob_symmetric_key, encrypted); + const decrypted_message = new TextDecoder().decode(decrypted); + +
+ const data = new TextEncoder().encode('Hello, world!'); + const key = await crypto.subtle.generateKey('Ed25519', false, ['sign']); + const signature = await crypto.subtle.sign('Ed25519', key.privateKey, data); ++
+ const data = new TextEncoder().encode('Hello, world!'); + const aesAlgorithmKeyGen = { + name: 'AES-GCM', + // AesKeyGenParams + length: 256 + }; + const aesAlgorithmEncrypt = { + name: 'AES-GCM', + // AesGcmParams + iv: crypto.getRandomValues(new Uint8Array(16)) + }; + const key = await crypto.subtle.generateKey(aesAlgorithmKeyGen, false, ['encrypt']); + const encrypted = await crypto.subtle.encrypt(aesAlgorithmEncrypt, key, data); ++
+ const filename = `${crypto.randomUUID()}.txt`; ++
-const filename = `${crypto.randomUUID()}.txt`; --
+{ kty: "OKP", + crv: "Ed25519" } ++
+{ name: "Ed25519" } ++
+{ kty: "OKP", + crv: "X25519" } ++
+{ name: "X25519" } ++
{ kty: "oct", alg: "A128CTR" }@@ -14205,6 +16013,26 @@