Secure Curves in the Web Cryptography API

Draft Community Group Report

Latest published version:
https://www.w3.org/webcrypto-secure-curves/
Latest editor's draft:
https://wicg.github.io/webcrypto-secure-curves/
Editor:
(Proton AG)
Feedback:
GitHub WICG/webcrypto-secure-curves (pull requests, new issue, open issues)

Abstract

This specification defines a number of algorithms for the Web Cryptography API, namely X25519 and X448 [RFC7748], and Ed25519 and Ed448 [RFC8032].

Status of This Document

This specification was published by the Web Platform Incubator Community Group. It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups.

This is an unofficial proposal.

GitHub Issues are preferred for discussion of this specification.

1. Introduction

This section is non-normative.

The Web Cryptography API currently does not specify any "safe curves" [SafeCurves]. Among the safe curves, Curve25519 and Curve448 have gained the most traction, and have been specified for use in TLS 1.3, among others. This specification aims to expose these algorithms to the web platform. To this end, this specification defines a number of algorithms and operations for the Web Cryptography API, namely key agreement using X25519 and X448 [RFC7748], and signing and verifying using Ed25519 and Ed448 [RFC8032].

2. Specification Conventions

This specification follows the conventions laid out in Section 18.3 of [WebCryptoAPI]. None of the algorithms defined here are required to be implemented, but if a conforming User Agent implements an algorithm, it MUST implement all of the supported operations specified in this document, and must perform the steps to define an algorithm specified in section 18.4.3 of [WebCryptoAPI] for each of the supported operations.

3. X25519

3.1 Description

This section is non-normative.

The "X25519" algorithm identifier is used to perform key agreement using the X25519 algorithm specified in [RFC7748].

3.2 Registration

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

3.3 Operations

Derive Bits
  1. If the [[type]] internal slot of key is not "private", then throw an InvalidAccessError.

  2. Let publicKey be the public member of normalizedAlgorithm.

  3. If the [[type]] internal slot of publicKey is not "public", then throw an InvalidAccessError.

  4. If the name attribute of the [[algorithm]] internal slot of publicKey is not equal to the name property of the [[algorithm]] internal slot of key, then throw an InvalidAccessError.

  5. 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 [[handle]] internal slot of publicKey as the X25519 public key u.

  6. If secret is the all-zero value, then throw a OperationError. This check must be performed in constant-time, as per [RFC7748] Section 6.1.

  7. If length is null:
    Return secret
    Otherwise:
    If the length of secret in bits is less than length:
    throw an OperationError.
    Otherwise:
    Return an octet string containing the first length bits of secret.
Generate Key
  1. If usages contains an entry which is not "deriveKey" or "deriveBits" then throw a SyntaxError.

  2. 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.

  3. Let algorithm be a new KeyAlgorithm object.

  4. Set the name attribute of algorithm to "X25519".

  5. 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.

  6. Set the [[type]] internal slot of publicKey to "public"

  7. Set the [[algorithm]] internal slot of publicKey to algorithm.

  8. Set the [[extractable]] internal slot of publicKey to true.

  9. Set the [[usages]] internal slot of publicKey to be the empty list.

  10. 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.

  11. Set the [[type]] internal slot of privateKey to "private"

  12. Set the [[algorithm]] internal slot of privateKey to algorithm.

  13. Set the [[extractable]] internal slot of privateKey to extractable.

  14. Set the [[usages]] internal slot of privateKey to be the usage intersection of usages and [ "deriveKey", "deriveBits" ].

  15. Let result be a new CryptoKeyPair dictionary.

  16. Set the publicKey attribute of result to be publicKey.

  17. Set the privateKey attribute of result to be privateKey.

  18. Return the result of converting result to an ECMAScript Object, as defined by [WebIDL].

Import Key
  1. Let keyData be the key data to be imported.

  2. If format is "spki":
    1. If usages is not empty then throw a SyntaxError.

    2. Let spki be the result of running the parse a subjectPublicKeyInfo algorithm over keyData.

    3. If an error occurred while parsing, then throw a DataError.

    4. 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 throw a DataError.

    5. If the parameters field of the algorithm AlgorithmIdentifier field of spki is present, then throw a DataError.

    6. Let publicKey be the X25519 public key identified by the subjectPublicKey field of spki.

    7. Let key be a new CryptoKey associated with the relevant global object of this [HTML], and that represents publicKey.

    8. Set the [[type]] internal slot of key to "public"

    9. Let algorithm be a new KeyAlgorithm.

    10. Set the name attribute of algorithm to "X25519".

    11. Set the [[algorithm]] internal slot of key to algorithm.

    If format is "pkcs8":
    1. If usages contains an entry which is not "deriveKey" or "deriveBits" then throw a SyntaxError.

    2. Let privateKeyInfo be the result of running the parse a privateKeyInfo algorithm over keyData.

    3. If an error occurs while parsing, then throw a DataError.

    4. 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 throw a DataError.

    5. If the parameters field of the privateKeyAlgorithm PrivateKeyAlgorithmIdentifier field of privateKeyInfo is present, then throw a DataError.

    6. 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.

    7. If an error occurred while parsing, then throw a DataError.

    8. 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.

    9. Set the [[type]] internal slot of key to "private"

    10. Let algorithm be a new KeyAlgorithm.

    11. Set the name attribute of algorithm to "X25519".

    12. Set the [[algorithm]] internal slot of key to algorithm.

    If format is "jwk":
    1. If keyData is a JsonWebKey dictionary:

      Let jwk equal keyData.

      Otherwise:

      Throw a DataError.

    2. If the d field is present and if usages contains an entry which is not "deriveKey" or "deriveBits" then throw a SyntaxError.

    3. If the d field is not present and if usages is not empty then throw a SyntaxError.

    4. If the kty field of jwk is not "OKP", then throw a DataError.

    5. If the crv field of jwk is not "X25519", then throw a DataError.

    6. If usages is non-empty and the use field of jwk is present and is not equal to "enc" then throw a DataError.

    7. If the 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 throw a DataError.

    8. If the ext field of jwk is present and has the value false and extractable is true, then throw a DataError.

    9. If the d field is present:
      1. If jwk does not meet the requirements of the JWK private key format described in Section 2 of [RFC8037], then throw a DataError.

      2. Let key be a new CryptoKey object that represents the X25519 private key identified by interpreting jwk according to Section 2 of [RFC8037].

      3. Set the [[type]] internal slot of Key to "private".

      Otherwise:
      1. If jwk does not meet the requirements of the JWK public key format described in Section 2 of [RFC8037], then throw a DataError.

      2. Let key be a new CryptoKey object that represents the X25519 public key identified by interpreting jwk according to Section 2 of [RFC8037].

      3. Set the [[type]] internal slot of Key to "public".

    10. Let algorithm be a new instance of a KeyAlgorithm object.

    11. Set the name attribute of algorithm to "X25519".

    12. Set the [[algorithm]] internal slot of key to algorithm.

    If format is "raw":
    1. If usages is not empty then throw a SyntaxError.

    2. Let algorithm be a new KeyAlgorithm object.

    3. Set the name attribute of algorithm to "X25519".

    4. Let key be a new CryptoKey associated with the relevant global object of this [HTML], and representing the key data provided in keyData.

    5. Set the [[type]] internal slot of key to "public"

    6. Set the [[algorithm]] internal slot of key to algorithm.

    Otherwise:

    throw a NotSupportedError.

  3. Return key

Export Key
  1. Let key be the CryptoKey to be exported.

  2. If the underlying cryptographic key material represented by the [[handle]] internal slot of key cannot be accessed, then throw an OperationError.

  3. If format is "spki":
    1. If the [[type]] internal slot of key is not "public", then throw an InvalidAccessError.

    2. 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.

    3. Let result be a new ArrayBuffer associated with the relevant global object of this [HTML], and containing data.

    If format is "pkcs8":
    1. If the [[type]] internal slot of key is not "private", then throw an InvalidAccessError.

    2. 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 [[handle]] internal slot of key

    3. Let result be a new ArrayBuffer associated with the relevant global object of this [HTML], and containing data.

    If format is "jwk":
    1. Let jwk be a new JsonWebKey dictionary.

    2. Set the kty attribute of jwk to "OKP".

    3. Set the crv attribute of jwk to "X25519".

    4. Set the x attribute of jwk according to the definition in Section 2 of [RFC8037].

    5. If the [[type]] internal slot of key is "private"
      Set the d attribute of jwk according to the definition in Section 2 of [RFC8037].
    6. Set the key_ops attribute of jwk to the usages attribute of key.

    7. Set the ext attribute of jwk to the [[extractable]] internal slot of key.

    8. Let result be the result of converting jwk to an ECMAScript Object, as defined by [WebIDL].

    If format is "raw":
    1. If the [[type]] internal slot of key is not "public", then throw an InvalidAccessError.

    2. Let data be an octet string representing the X25519 public key represented by the [[handle]] internal slot of key.

    3. Let result be a new ArrayBuffer associated with the relevant global object of this [HTML], and containing data.

    Otherwise:

    throw a NotSupportedError.

  4. Return result.

4. X448

4.1 Description

This section is non-normative.

The "X448" algorithm identifier is used to perform key agreement using the X448 algorithm specified in [RFC7748].

4.2 Registration

The recognized algorithm name for this algorithm is "X448".

Operation Parameters Result
deriveBits EcdhKeyDeriveParams octet string
generateKey None CryptoKeyPair
importKey None CryptoKey
exportKey None object

4.3 Operations

Derive Bits
  1. If the [[type]] internal slot of key is not "private", then throw an InvalidAccessError.

  2. Let publicKey be the public member of normalizedAlgorithm.

  3. If the [[type]] internal slot of publicKey is not "public", then throw an InvalidAccessError.

  4. If the name attribute of the [[algorithm]] internal slot of publicKey is not equal to the name property of the [[algorithm]] internal slot of key, then throw an InvalidAccessError.

  5. Let secret be the result of performing the X448 function specified in [RFC7748] Section 5 with key as the X448 private key k and the X448 public key represented by the [[handle]] internal slot of publicKey as the X448 public key u.

  6. If secret is the all-zero value, then throw a OperationError. This check must be performed in constant-time, as per [RFC7748] Section 6.2.

  7. If length is null:
    Return secret
    Otherwise:
    If the length of secret in bits is less than length:
    throw an OperationError.
    Otherwise:
    Return an octet string containing the first length bits of secret.
Generate Key
  1. If usages contains an entry which is not "deriveKey" or "deriveBits" then throw a SyntaxError.

  2. Generate an X448 key pair, with the private key being 56 random bytes, and the public key being X448(a, 5), as defined in [RFC7748], section 6.2.

  3. Let algorithm be a new KeyAlgorithm object.

  4. Set the name attribute of algorithm to "X448".

  5. 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.

  6. Set the [[type]] internal slot of publicKey to "public"

  7. Set the [[algorithm]] internal slot of publicKey to algorithm.

  8. Set the [[extractable]] internal slot of publicKey to true.

  9. Set the [[usages]] internal slot of publicKey to be the empty list.

  10. 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.

  11. Set the [[type]] internal slot of privateKey to "private"

  12. Set the [[algorithm]] internal slot of privateKey to algorithm.

  13. Set the [[extractable]] internal slot of privateKey to extractable.

  14. Set the [[usages]] internal slot of privateKey to be the usage intersection of usages and [ "deriveKey", "deriveBits" ].

  15. Let result be a new CryptoKeyPair dictionary.

  16. Set the publicKey attribute of result to be publicKey.

  17. Set the privateKey attribute of result to be privateKey.

  18. Return the result of converting result to an ECMAScript Object, as defined by [WebIDL].

Import Key
  1. Let keyData be the key data to be imported.

  2. If format is "spki":
    1. If usages is not empty then throw a SyntaxError.

    2. Let spki be the result of running the parse a subjectPublicKeyInfo algorithm over keyData.

    3. If an error occurred while parsing, then throw a DataError.

    4. If the algorithm object identifier field of the algorithm AlgorithmIdentifier field of spki is not equal to the id-X448 object identifier defined in [RFC8410], then throw a DataError.

    5. If the parameters field of the algorithm AlgorithmIdentifier field of spki is present, then throw a DataError.

    6. Let publicKey be the X448 public key identified by the subjectPublicKey field of spki.

    7. Let key be a new CryptoKey associated with the relevant global object of this [HTML], and that represents publicKey.

    8. Set the [[type]] internal slot of key to "public"

    9. Let algorithm be a new KeyAlgorithm.

    10. Set the name attribute of algorithm to "X448".

    11. Set the [[algorithm]] internal slot of key to algorithm.

    If format is "pkcs8":
    1. If usages contains an entry which is not "deriveKey" or "deriveBits" then throw a SyntaxError.

    2. Let privateKeyInfo be the result of running the parse a privateKeyInfo algorithm over keyData.

    3. If an error occurs while parsing, then throw a DataError.

    4. If the algorithm object identifier field of the privateKeyAlgorithm PrivateKeyAlgorithm field of privateKeyInfo is not equal to the id-X448 object identifier defined in [RFC8410], then throw a DataError.

    5. If the parameters field of the privateKeyAlgorithm PrivateKeyAlgorithmIdentifier field of privateKeyInfo is present, then throw a DataError.

    6. 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.

    7. If an error occurred while parsing, then throw a DataError.

    8. Let key be a new CryptoKey associated with the relevant global object of this [HTML], and that represents the X448 private key identified by curvePrivateKey.

    9. Set the [[type]] internal slot of key to "private"

    10. Let algorithm be a new KeyAlgorithm.

    11. Set the name attribute of algorithm to "X448".

    12. Set the [[algorithm]] internal slot of key to algorithm.

    If format is "jwk":
    1. If keyData is a JsonWebKey dictionary:

      Let jwk equal keyData.

      Otherwise:

      Throw a DataError.

    2. If the d field is present and if usages contains an entry which is not "deriveKey" or "deriveBits" then throw a SyntaxError.

    3. If the d field is not present and if usages is not empty then throw a SyntaxError.

    4. If the kty field of jwk is not "OKP", then throw a DataError.

    5. If the crv field of jwk is not "X448", then throw a DataError.

    6. If usages is non-empty and the use field of jwk is present and is not equal to "enc" then throw a DataError.

    7. If the 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 throw a DataError.

    8. If the ext field of jwk is present and has the value false and extractable is true, then throw a DataError.

    9. If the d field is present:
      1. If jwk does not meet the requirements of the JWK private key format described in Section 2 of [RFC8037], then throw a DataError.

      2. Let key be a new CryptoKey object that represents the X448 private key identified by interpreting jwk according to Section 2 of [RFC8037].

      3. Set the [[type]] internal slot of Key to "private".

      Otherwise:
      1. If jwk does not meet the requirements of the JWK public key format described in Section 2 of [RFC8037], then throw a DataError.

      2. Let key be a new CryptoKey object that represents the X448 public key identified by interpreting jwk according to Section 2 of [RFC8037].

      3. Set the [[type]] internal slot of Key to "public".

    10. Let algorithm be a new instance of a KeyAlgorithm object.

    11. Set the name attribute of algorithm to "X448".

    12. Set the [[algorithm]] internal slot of key to algorithm.

    If format is "raw":
    1. If usages is not empty then throw a SyntaxError.

    2. Let algorithm be a new KeyAlgorithm object.

    3. Set the name attribute of algorithm to "X448".

    4. Let key be a new CryptoKey associated with the relevant global object of this [HTML], and representing the key data provided in keyData.

    5. Set the [[type]] internal slot of key to "public"

    6. Set the [[algorithm]] internal slot of key to algorithm.

    Otherwise:

    throw a NotSupportedError.

  3. Return key

Export Key
  1. Let key be the CryptoKey to be exported.

  2. If the underlying cryptographic key material represented by the [[handle]] internal slot of key cannot be accessed, then throw an OperationError.

  3. If format is "spki":
    1. If the [[type]] internal slot of key is not "public", then throw an InvalidAccessError.

    2. 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-X448 OID defined in [RFC8410].

      • Set the subjectPublicKey field to keyData.

    3. Let result be a new ArrayBuffer associated with the relevant global object of this [HTML], and containing data.

    If format is "pkcs8":
    1. If the [[type]] internal slot of key is not "private", then throw an InvalidAccessError.

    2. 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-X448 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 X448 private key represented by the [[handle]] internal slot of key

    3. Let result be a new ArrayBuffer associated with the relevant global object of this [HTML], and containing data.

    If format is "jwk":
    1. Let jwk be a new JsonWebKey dictionary.

    2. Set the kty attribute of jwk to "OKP".

    3. Set the crv attribute of jwk to "X448".

    4. Set the x attribute of jwk according to the definition in Section 2 of [RFC8037].

    5. If the [[type]] internal slot of key is "private"
      Set the d attribute of jwk according to the definition in Section 2 of [RFC8037].
    6. Set the key_ops attribute of jwk to the usages attribute of key.

    7. Set the ext attribute of jwk to the [[extractable]] internal slot of key.

    8. Let result be the result of converting jwk to an ECMAScript Object, as defined by [WebIDL].

    If format is "raw":
    1. If the [[type]] internal slot of key is not "public", then throw an InvalidAccessError.

    2. Let data be an octet string representing the X448 public key represented by the [[handle]] internal slot of key.

    3. Let result be a new ArrayBuffer associated with the relevant global object of this [HTML], and containing data.

    Otherwise:

    throw a NotSupportedError.

  4. Return result.

5. Ed25519

5.1 Description

This section is non-normative.

The "Ed25519" algorithm identifier is used to perform signing and verification using the Ed25519 algorithm specified in [RFC8032].

5.2 Registration

The recognized algorithm name for this algorithm is "Ed25519".

Operation Parameters Result
sign None ArrayBuffer
verify None boolean
generateKey None CryptoKeyPair
importKey None CryptoKey
exportKey None object

5.3 Operations

Sign
When signing, the following algorithm should be used:
  1. If the [[type]] internal slot of key is not "private", then throw an InvalidAccessError.

  2. 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.

    Issue 28: Allow creating randomized EdDSA signatures?

    Some implementations may (wish to) generate randomized signatures as per draft-irtf-cfrg-det-sigs-with-noise instead of deterministic signatures as per [RFC8032].

  3. 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.

Verify
When verifying, the following algorithm should be used:
  1. If the [[type]] internal slot of key is not "public", then throw an InvalidAccessError.

  2. If the key data of key represents an invalid point or a small-order element on the Elliptic Curve of Ed25519, return false.

    Issue 27: Make small-order checks in EdDSA optional?

    Not all implementations perform this check.

  3. 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.

    Issue 27: Make small-order checks in EdDSA optional?

    Not all implementations perform this check.

  4. 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.

  5. Let result be a boolean with the value true if the signature is valid and the value false otherwise.

  6. Return result.

Generate Key
  1. If usages contains a value which is not one of "sign" or "verify", then throw a SyntaxError.

  2. Generate an Ed25519 key pair, as defined in [RFC8032], section 5.1.5.

  3. Let algorithm be a new KeyAlgorithm object.

  4. Set the name attribute of algorithm to "Ed25519".

  5. 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.

  6. Set the [[type]] internal slot of publicKey to "public"

  7. Set the [[algorithm]] internal slot of publicKey to algorithm.

  8. Set the [[extractable]] internal slot of publicKey to true.

  9. Set the [[usages]] internal slot of publicKey to be the usage intersection of usages and [ "verify" ].

  10. 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.

  11. Set the [[type]] internal slot of privateKey to "private"

  12. Set the [[algorithm]] internal slot of privateKey to algorithm.

  13. Set the [[extractable]] internal slot of privateKey to extractable.

  14. Set the [[usages]] internal slot of privateKey to be the usage intersection of usages and [ "sign" ].

  15. Let result be a new CryptoKeyPair dictionary.

  16. Set the publicKey attribute of result to be publicKey.

  17. Set the privateKey attribute of result to be privateKey.

  18. Return the result of converting result to an ECMAScript Object, as defined by [WebIDL].

Import Key
  1. Let keyData be the key data to be imported.

  2. If format is "spki":
    1. If usages contains a value which is not "verify" then throw a SyntaxError.

    2. Let spki be the result of running the parse a subjectPublicKeyInfo algorithm over keyData.

    3. If an error occurred while parsing, then throw a DataError.

    4. 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 throw a DataError.

    5. If the parameters field of the algorithm AlgorithmIdentifier field of spki is present, then throw a DataError.

    6. Let publicKey be the Ed25519 public key identified by the subjectPublicKey field of spki.

    7. Let key be a new CryptoKey associated with the relevant global object of this [HTML], and that represents publicKey.

    8. Set the [[type]] internal slot of key to "public"

    9. Let algorithm be a new KeyAlgorithm.

    10. Set the name attribute of algorithm to "Ed25519".

    11. Set the [[algorithm]] internal slot of key to algorithm.

    If format is "pkcs8":
    1. If usages contains a value which is not "sign" then throw a SyntaxError.

    2. Let privateKeyInfo be the result of running the parse a privateKeyInfo algorithm over keyData.

    3. If an error occurs while parsing, then throw a DataError.

    4. 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 throw a DataError.

    5. If the parameters field of the privateKeyAlgorithm PrivateKeyAlgorithmIdentifier field of privateKeyInfo is present, then throw a DataError.

    6. 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.

    7. If an error occurred while parsing, then throw a DataError.

    8. 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.

    9. Set the [[type]] internal slot of key to "private"

    10. Let algorithm be a new KeyAlgorithm.

    11. Set the name attribute of algorithm to "Ed25519".

    12. Set the [[algorithm]] internal slot of key to algorithm.

    If format is "jwk":
    1. If keyData is a JsonWebKey dictionary:

      Let jwk equal keyData.

      Otherwise:

      Throw a DataError.

    2. If the d field is present and usages contains a value which is not "sign", or, if the d field is not present and usages contains a value which is not "verify" then throw a SyntaxError.

    3. If the kty field of jwk is not "OKP", then throw a DataError.

    4. If the crv field of jwk is not "Ed25519", then throw a DataError.

    5. If usages is non-empty and the use field of jwk is present and is not "sig", then throw a DataError.

    6. If the 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 throw a DataError.

    7. If the ext field of jwk is present and has the value false and extractable is true, then throw a DataError.

    8. If the d field is present:
      1. If jwk does not meet the requirements of the JWK private key format described in Section 2 of [RFC8037], then throw a DataError.

      2. Let key be a new CryptoKey object that represents the Ed25519 private key identified by interpreting jwk according to Section 2 of [RFC8037].

      3. Set the [[type]] internal slot of Key to "private".

      Otherwise:
      1. If jwk does not meet the requirements of the JWK public key format described in Section 2 of [RFC8037], then throw a DataError.

      2. Let key be a new CryptoKey object that represents the Ed25519 public key identified by interpreting jwk according to Section 2 of [RFC8037].

      3. Set the [[type]] internal slot of Key to "public".

    9. Let algorithm be a new instance of a KeyAlgorithm object.

    10. Set the name attribute of algorithm to "Ed25519".

    11. Set the [[algorithm]] internal slot of key to algorithm.

    If format is "raw":
    1. If usages contains a value which is not "verify" then throw a SyntaxError.

    2. Let algorithm be a new KeyAlgorithm object.

    3. Set the name attribute of algorithm to "Ed25519".

    4. Let key be a new CryptoKey associated with the relevant global object of this [HTML], and representing the key data provided in keyData.

    5. Set the [[type]] internal slot of key to "public"

    6. Set the [[algorithm]] internal slot of key to algorithm.

    Otherwise:

    throw a NotSupportedError.

  3. Return key

Export Key
  1. Let key be the CryptoKey to be exported.

  2. If the underlying cryptographic key material represented by the [[handle]] internal slot of key cannot be accessed, then throw an OperationError.

  3. If format is "spki":
    1. If the [[type]] internal slot of key is not "public", then throw an InvalidAccessError.

    2. 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.

    3. Let result be a new ArrayBuffer associated with the relevant global object of this [HTML], and containing data.

    If format is "pkcs8":
    1. If the [[type]] internal slot of key is not "private", then throw an InvalidAccessError.

    2. 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 [[handle]] internal slot of key

    3. Let result be a new ArrayBuffer associated with the relevant global object of this [HTML], and containing data.

    If format is "jwk":
    1. Let jwk be a new JsonWebKey dictionary.

    2. Set the kty attribute of jwk to "OKP".

    3. Set the crv attribute of jwk to "Ed25519".

    4. Set the x attribute of jwk according to the definition in Section 2 of [RFC8037].

    5. If the [[type]] internal slot of key is "private"
      Set the d attribute of jwk according to the definition in Section 2 of [RFC8037].
    6. Set the key_ops attribute of jwk to the usages attribute of key.

    7. Set the ext attribute of jwk to the [[extractable]] internal slot of key.

    8. Let result be the result of converting jwk to an ECMAScript Object, as defined by [WebIDL].

    If format is "raw":
    1. If the [[type]] internal slot of key is not "public", then throw an InvalidAccessError.

    2. Let data be an octet string representing the Ed25519 public key represented by the [[handle]] internal slot of key.

    3. Let result be a new ArrayBuffer associated with the relevant global object of this [HTML], and containing data.

    Otherwise:

    throw a NotSupportedError.

  4. Return result.

6. Ed448

6.1 Description

This section is non-normative.

The "Ed448" algorithm identifier is used to perform signing and verification using the Ed448 algorithm specified in [RFC8032].

6.2 Registration

The recognized algorithm name for this algorithm is "Ed448".

Operation Parameters Result
sign Ed448Params ArrayBuffer
verify Ed448Params boolean
generateKey None CryptoKeyPair
importKey None CryptoKey
exportKey None object

6.3 Ed448Params dictionary

WebIDLdictionary Ed448Params : Algorithm {
  BufferSource context;
};

The context member represents the optional context data to associate with the message.

6.4 Operations

Sign
When signing, the following algorithm should be used:
  1. If the [[type]] internal slot of key is not "private", then throw an InvalidAccessError.

  2. Let context be the contents of the context member of normalizedAlgorithm or the empty octet string if the context member of normalizedAlgorithm is not present.

  3. If context has a length greater than 255 bytes, then throw an OperationError.

  4. Perform the Ed448 signing process, as specified in [RFC8032], Section 5.2.6, with message as M and context as C, using the Ed448 private key associated with key.

    Issue 28: Allow creating randomized EdDSA signatures?

    Some implementations may (wish to) generate randomized signatures as per draft-irtf-cfrg-det-sigs-with-noise instead of deterministic signatures as per [RFC8032].

  5. Return a new ArrayBuffer associated with the relevant global object of this [HTML], and containing the bytes of the signature resulting from performing the Ed448 signing process.

Verify
When verifying, the following algorithm should be used:
  1. If the [[type]] internal slot of key is not "public", then throw an InvalidAccessError.

  2. Let context be the contents of the context member of normalizedAlgorithm or the empty octet string if the context member of normalizedAlgorithm is not present.

  3. If context has a length greater than 255 bytes, then throw an OperationError.

  4. If the key data of key represents an invalid point or a small-order element on the Elliptic Curve of Ed448, return false.

    Issue 27: Make small-order checks in EdDSA optional?

    Not all implementations perform this check.

  5. 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 Ed448, return false.

    Issue 27: Make small-order checks in EdDSA optional?

    Not all implementations perform this check.

  6. Perform the Ed448 verification steps, as specified in [RFC8032], Section 5.2.7, using the cofactorless (unbatched) equation, [S]B = R + [k]A', on the signature, with message as M and context as C, using the Ed448 public key associated with key.

  7. Let result be a boolean with the value true if the signature is valid and the value false otherwise.

  8. Return result.

Generate Key
  1. If usages contains a value which is not one of "sign" or "verify", then throw a SyntaxError.

  2. Generate an Ed448 key pair, as defined in [RFC8032], section 5.1.5.

  3. Let algorithm be a new KeyAlgorithm object.

  4. Set the name attribute of algorithm to "Ed448".

  5. 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.

  6. Set the [[type]] internal slot of publicKey to "public"

  7. Set the [[algorithm]] internal slot of publicKey to algorithm.

  8. Set the [[extractable]] internal slot of publicKey to true.

  9. Set the [[usages]] internal slot of publicKey to be the usage intersection of usages and [ "verify" ].

  10. 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.

  11. Set the [[type]] internal slot of privateKey to "private"

  12. Set the [[algorithm]] internal slot of privateKey to algorithm.

  13. Set the [[extractable]] internal slot of privateKey to extractable.

  14. Set the [[usages]] internal slot of privateKey to be the usage intersection of usages and [ "sign" ].

  15. Let result be a new CryptoKeyPair dictionary.

  16. Set the publicKey attribute of result to be publicKey.

  17. Set the privateKey attribute of result to be privateKey.

  18. Return the result of converting result to an ECMAScript Object, as defined by [WebIDL].

Import Key
  1. Let keyData be the key data to be imported.

  2. If format is "spki":
    1. If usages contains a value which is not "verify" then throw a SyntaxError.

    2. Let spki be the result of running the parse a subjectPublicKeyInfo algorithm over keyData.

    3. If an error occurred while parsing, then throw a DataError.

    4. If the algorithm object identifier field of the algorithm AlgorithmIdentifier field of spki is not equal to the id-Ed448 object identifier defined in [RFC8410], then throw a DataError.

    5. If the parameters field of the algorithm AlgorithmIdentifier field of spki is present, then throw a DataError.

    6. Let publicKey be the Ed448 public key identified by the subjectPublicKey field of spki.

    7. Let key be a new CryptoKey associated with the relevant global object of this [HTML], and that represents publicKey.

    8. Set the [[type]] internal slot of key to "public"

    9. Let algorithm be a new KeyAlgorithm.

    10. Set the name attribute of algorithm to "Ed448".

    11. Set the [[algorithm]] internal slot of key to algorithm.

    If format is "pkcs8":
    1. If usages contains a value which is not "sign" then throw a SyntaxError.

    2. Let privateKeyInfo be the result of running the parse a privateKeyInfo algorithm over keyData.

    3. If an error occurs while parsing, then throw a DataError.

    4. If the algorithm object identifier field of the privateKeyAlgorithm PrivateKeyAlgorithm field of privateKeyInfo is not equal to the id-Ed448 object identifier defined in [RFC8410], then throw a DataError.

    5. If the parameters field of the privateKeyAlgorithm PrivateKeyAlgorithmIdentifier field of privateKeyInfo is present, then throw a DataError.

    6. 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.

    7. If an error occurred while parsing, then throw a DataError.

    8. Let key be a new CryptoKey associated with the relevant global object of this [HTML], and that represents the Ed448 private key identified by curvePrivateKey.

    9. Set the [[type]] internal slot of key to "private"

    10. Let algorithm be a new KeyAlgorithm.

    11. Set the name attribute of algorithm to "Ed448".

    12. Set the [[algorithm]] internal slot of key to algorithm.

    If format is "jwk":
    1. If keyData is a JsonWebKey dictionary:

      Let jwk equal keyData.

      Otherwise:

      Throw a DataError.

    2. If the d field is present and usages contains a value which is not "sign", or, if the d field is not present and usages contains a value which is not "verify" then throw a SyntaxError.

    3. If the kty field of jwk is not "OKP", then throw a DataError.

    4. If the crv field of jwk is not "Ed448", then throw a DataError.

    5. If usages is non-empty and the use field of jwk is present and is not "sig", then throw a DataError.

    6. If the 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 throw a DataError.

    7. If the ext field of jwk is present and has the value false and extractable is true, then throw a DataError.

    8. If the d field is present:
      1. If jwk does not meet the requirements of the JWK private key format described in Section 2 of [RFC8037], then throw a DataError.

      2. Let key be a new CryptoKey object that represents the Ed448 private key identified by interpreting jwk according to Section 2 of [RFC8037].

      3. Set the [[type]] internal slot of Key to "private".

      Otherwise:
      1. If jwk does not meet the requirements of the JWK public key format described in Section 2 of [RFC8037], then throw a DataError.

      2. Let key be a new CryptoKey object that represents the Ed448 public key identified by interpreting jwk according to Section 2 of [RFC8037].

      3. Set the [[type]] internal slot of Key to "public".

    9. Let algorithm be a new instance of a KeyAlgorithm object.

    10. Set the name attribute of algorithm to "Ed448".

    11. Set the [[algorithm]] internal slot of key to algorithm.

    If format is "raw":
    1. If usages contains a value which is not "verify" then throw a SyntaxError.

    2. Let algorithm be a new KeyAlgorithm object.

    3. Set the name attribute of algorithm to "Ed448".

    4. Let key be a new CryptoKey associated with the relevant global object of this [HTML], and representing the key data provided in keyData.

    5. Set the [[type]] internal slot of key to "public"

    6. Set the [[algorithm]] internal slot of key to algorithm.

    Otherwise:

    throw a NotSupportedError.

  3. Return key

Export Key
  1. Let key be the CryptoKey to be exported.

  2. If the underlying cryptographic key material represented by the [[handle]] internal slot of key cannot be accessed, then throw an OperationError.

  3. If format is "spki":
    1. If the [[type]] internal slot of key is not "public", then throw an InvalidAccessError.

    2. 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-Ed448 OID defined in [RFC8410].

      • Set the subjectPublicKey field to keyData.

    3. Let result be a new ArrayBuffer associated with the relevant global object of this [HTML], and containing data.

    If format is "pkcs8":
    1. If the [[type]] internal slot of key is not "private", then throw an InvalidAccessError.

    2. 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-Ed448 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 Ed448 private key represented by the [[handle]] internal slot of key

    3. Let result be a new ArrayBuffer associated with the relevant global object of this [HTML], and containing data.

    If format is "jwk":
    1. Let jwk be a new JsonWebKey dictionary.

    2. Set the kty attribute of jwk to "OKP".

    3. Set the crv attribute of jwk to "Ed448".

    4. Set the x attribute of jwk according to the definition in Section 2 of [RFC8037].

    5. If the [[type]] internal slot of key is "private"
      Set the d attribute of jwk according to the definition in Section 2 of [RFC8037].
    6. Set the key_ops attribute of jwk to the usages attribute of key.

    7. Set the ext attribute of jwk to the [[extractable]] internal slot of key.

    8. Let result be the result of converting jwk to an ECMAScript Object, as defined by [WebIDL].

    If format is "raw":
    1. If the [[type]] internal slot of key is not "public", then throw an InvalidAccessError.

    2. Let data be an octet string representing the Ed448 public key represented by the [[handle]] internal slot of key.

    3. Let result be a new ArrayBuffer associated with the relevant global object of this [HTML], and containing data.

    Otherwise:

    throw a NotSupportedError.

  4. Return result.

7. Usage Example

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.

Example 1: X25519 key agreement
// 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;

// 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;

// 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']);

// 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']);

// 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);

// 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);

A. Mapping between JSON Web Key / JSON Web Algorithm

This section is non-normative.

Refer to algorithm-specific sections for the normative requirements of importing and exporting JWK.

A.1 Algorithm mappings

JSON Web Key AlgorithmIdentifier
{ kty: "OKP",
  crv: "X25519" }
{ name: "X25519" }
{ kty: "OKP",
  crv: "X448" }
{ name: "X448" }
{ kty: "OKP",
  crv: "Ed25519" }
{ name: "Ed25519" }
{ kty: "OKP",
  crv: "Ed448" }
{ name: "Ed448" }

B. Mapping between Algorithm and SubjectPublicKeyInfo

This section is non-normative.

Refer to algorithm-specific sections for the normative requirements of importing and exporting SPKI.

Algorithm OID subjectPublicKey ASN.1 structure AlgorithmIdentifier Reference
id-X25519 (1.3.101.110) BIT STRING "X25519" [RFC8410]
id-X448 (1.3.101.111) BIT STRING "X448" [RFC8410]
id-Ed25519 (1.3.101.112) BIT STRING "Ed25519" [RFC8410]
id-Ed448 (1.3.101.113) BIT STRING "Ed448" [RFC8410]

C. Mapping between Algorithm and PKCS#8 PrivateKeyInfo

This section is non-normative.

Refer to algorithm-specific sections for the normative requirements of importing and exporting PKCS#8 PrivateKeyInfo.

privateKeyAlgorithm privateKey format AlgorithmIdentifier Reference
id-X25519 (1.3.101.110) CurvePrivateKey "X25519" [RFC8410]
id-X448 (1.3.101.111) CurvePrivateKey "X448" [RFC8410]
id-Ed25519 (1.3.101.112) CurvePrivateKey "Ed25519" [RFC8410]
id-Ed448 (1.3.101.113) CurvePrivateKey "Ed448" [RFC8410]

D. Conformance

As well as sections marked as non-normative, all authoring guidelines, diagrams, examples, and notes in this specification are non-normative. Everything else in this specification is normative.

The key word MUST in this document is to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.

E. References

E.1 Normative references

[HTML]
HTML Standard. Anne van Kesteren; Domenic Denicola; Dominic Farolino; Ian Hickson; Philip Jägenstedt; Simon Pieters. WHATWG. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[JWK]
JSON Web Key (JWK). M. Jones. IETF. May 2015. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc7517
[RFC2119]
Key words for use in RFCs to Indicate Requirement Levels. S. Bradner. IETF. March 1997. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc2119
[RFC5208]
Public-Key Cryptography Standards (PKCS) #8: Private-Key Information Syntax Specification Version 1.2. B. Kaliski. IETF. May 2008. Informational. URL: https://www.rfc-editor.org/rfc/rfc5208
[RFC5280]
Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile. D. Cooper; S. Santesson; S. Farrell; S. Boeyen; R. Housley; W. Polk. IETF. May 2008. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc5280
[RFC7748]
Elliptic Curves for Security. A. Langley; M. Hamburg; S. Turner. IETF. January 2016. Informational. URL: https://www.rfc-editor.org/rfc/rfc7748
[RFC8032]
Edwards-Curve Digital Signature Algorithm (EdDSA). S. Josefsson; I. Liusvaara. IETF. January 2017. Informational. URL: https://www.rfc-editor.org/rfc/rfc8032
[RFC8037]
CFRG Elliptic Curve Diffie-Hellman (ECDH) and Signatures in JSON Object Signing and Encryption (JOSE). I. Liusvaara. IETF. January 2017. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc8037
[RFC8174]
Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words. B. Leiba. IETF. May 2017. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc8174
[RFC8410]
Algorithm Identifiers for Ed25519, Ed448, X25519, and X448 for Use in the Internet X.509 Public Key Infrastructure. S. Josefsson; J. Schaad. IETF. August 2018. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc8410
[WebCryptoAPI]
Web Cryptography API. Mark Watson. W3C. 26 January 2017. W3C Recommendation. URL: https://www.w3.org/TR/WebCryptoAPI/
[WebIDL]
Web IDL Standard. Edgar Chen; Timothy Gu. WHATWG. Living Standard. URL: https://webidl.spec.whatwg.org/

E.2 Informative references

[SafeCurves]
Daniel J. Bernstein and Tanja Lange. SafeCurves: choosing safe curves for elliptic-curve cryptography. URL: https://safecurves.cr.yp.to/