JDK-8339010 : JEP 497: Quantum-Resistant Module-Lattice-Based Digital Signature Algorithm
  • Type: JEP
  • Component: security-libs
  • Sub-Component: java.security
  • Priority: P3
  • Status: Candidate
  • Resolution: Unresolved
  • Submitted: 2024-08-26
  • Updated: 2024-11-06
Related Reports
Blocks :  
Blocks :  
Blocks :  
Blocks :  
Description
Summary
-------

Enhance the security of Java applications by providing an implementation of the
quantum-resistant Module-Lattice-Based Digital Signature Algorithm (ML-DSA).
Digital signatures are used to detect unauthorized modifications to data and to
authenticate the identity of signatories. ML-DSA is designed to be secure
against future quantum computing attacks. It has been standardized by the United
States National Institute of Standards and Technology (NIST) in
[FIPS 204](https://csrc.nist.gov/pubs/fips/204/final).


Goals
-----

- Provide ML-DSA implementations of the [`KeyPairGenerator`], [`Signature`], and
  [`KeyFactory`] APIs, with support for the parameter sets ML-DSA-44, ML-DSA-65,
  and ML-DSA-87 standardized in FIPS 204.

[`KeyPairGenerator`]: https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/security/KeyPairGenerator.html
[`Signature`]: https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/security/Signature.html
[`KeyFactory`]: https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/security/KeyFactory.html


Non Goals
---------

- It is not a goal to implement the [Dilithium
  algorithm](https://en.wikipedia.org/wiki/Lattice-based_cryptography#CRYSTALS-Dilithium),
  from which ML-DSA was derived. The two algorithms are not interoperable.

- It is not a goal to add support for ML-DSA to components of the Java Platform
  for which the necessary standards do not yet exist. That is the case for, in
  particular, JAR-file signing as well as the implementation of Transport Layer
  Security (TLS) in the
  [`javax.net.ssl`](https://docs.oracle.com/en/java/javase/23/docs/api/java.base/javax/net/ssl/package-summary.html)
  package. We will add such support once the standards do exist.

- It is not a goal to support Pre-Hash ML-DSA (FIPS 204 §5.4) or allow
  users to set application-specific context strings (FIPS 204 §5.2). We may
  implement these features in a future release.


Motivation
----------

The field of [quantum
computing](https://en.wikipedia.org/wiki/Quantum_computing) has been advancing
steadily for years. A future large-scale quantum computer could use [Shor’s
algorithm](https://en.wikipedia.org/wiki/Shor's_algorithm), which is capable of
factoring integers and solving the discrete logarithm problem, to compromise the
security of widely-deployed public-key based algorithms including
Rivest-Shamir-Adleman (RSA) and Diffie-Hellman. Such algorithms are used by the
Java Platform to, among other things, digitally sign JAR files and establish
secure network connections via the Transport Layer Security (TLS) protocol. An
attack that a conventional supercomputer might need thousands to millions of
years to complete could be accomplished by a quantum computer using Shor's
algorithm in mere hours. Cryptographers have responded to this threat by inventing
[quantum-resistant](https://en.wikipedia.org/wiki/Post-quantum_cryptography)
cryptographic algorithms, which cannot be defeated by Shor's algorithm.

For the purposes of signing data and authenticating identities in a
quantum-resistant fashion, NIST standardized the Module-Lattice-Based Digital
Signature Algorithm (ML-DSA) in
[FIPS 204](https://csrc.nist.gov/pubs/fips/204/final). In the United
States, [government computer systems that handle sensitive
information](https://media.defense.gov/2022/Sep/07/2003071836/-1/-1/0/CSI_CNSA_2.0_FAQ_.PDF)
must be upgraded over the next decade to use ML-DSA. It is thus essential for
the Java Platform to provide an implementation of this algorithm.


Description
-----------

We will provide ML-DSA implementations of the [`KeyPairGenerator`] API to
generate ML-DSA key pairs, of the [`Signature`] API to sign and verify ML-DSA
signatures, and of the [`KeyFactory`] API to convert ML-DSA keys to and from
their encodings.

In the [Java Security Standard Algorithm Names
Specification](https://docs.oracle.com/en/java/javase/23/docs/specs/security/standard-names.html),
we will define a new standard algorithm family name, `"ML-DSA"`, for the
`KeyPairGenerator`, `Signature`, and `KeyFactory` APIs.

FIPS 204 specifies three parameter sets for ML-DSA. In order of increasing
security strength and decreasing performance, they are named `"ML-DSA-44"`,
`"ML-DSA-65"`, and `"ML-DSA-87"`. These parameter-set names will also be defined
as standard algorithm names for the `KeyPairGenerator`, `Signature`, and
`KeyFactory` APIs, and, further, will be represented by the new
[`NamedParameterSpec`] constants `ML_DSA_44`, `ML_DSA_65`, and `ML_DSA_87`.

[`NamedParameterSpec`]: https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/security/spec/NamedParameterSpec.html


### Generating ML-DSA key pairs

You can generate an ML-DSA key pair in one of three ways:

- Instantiate a `KeyPairGenerator` with the family name and initialize it with a
parameter-set name:

  ```
  KeyPairGenerator g = KeyPairGenerator.getInstance("ML-DSA");
  g.initialize(NamedParameterSpec.ML_DSA_44);
  KeyPair kp = g.generateKeyPair(); // an ML-DSA-44 key pair
  ```

- If you do not initialize the `KeyPairGenerator` with a parameter set, the
implementation will use ML-DSA-65 as the default:

  ```
  KeyPairGenerator g = KeyPairGenerator.getInstance("ML-DSA");
  KeyPair kp = g.generateKeyPair(); // an ML-DSA-65 key pair
  ```

- Directly instantiate a `KeyPairGenerator` with a parameter-set name:

  ```
  KeyPairGenerator g = KeyPairGenerator.getInstance("ML-DSA-87");
  KeyPair kp = g.generateKeyPair(); // an ML-DSA-87 key pair
  ```

The `KeyPairGenerator` API allows an integer key size to be specified during
initialization, but this is not supported for ML-DSA key pairs and will cause an
`InvalidParameterException` to be thrown.

The `keytool` command will support generating ML-DSA key pairs and certificates.
For example:

```
$ keytool -keystore ks -storepass changeit -genkeypair -alias mldsa \
          -keyalg ML-DSA -groupname ML-DSA-65 -dname CN=ML-DSA
```

You can also provide the parameter-set name, `ML-DSA-65`, directly via the
`-keyalg` option:

```
$ keytool -keystore ks -storepass changeit -genkeypair -alias mldsa \
          -keyalg ML-DSA-65 -dname CN=ML-DSA2
```


### Signing with ML-DSA keys

You can use the ML-DSA `Signature` implementation to sign and verify ML-DSA
signatures.

For example, to sign a message using a private key:

```
byte[] msg = ...;
Signature ss = Signature.getInstance("ML-DSA");
ss.initSign(privateKey);
ss.update(msg);
byte[] sig = ss.sign();
```

To verify a signature with a public key:

```
byte[] msg = ...;
byte[] sig = ...;
Signature sv = Signature.getInstance("ML-DSA");
sv.initVerify(publicKey);
sv.update(msg);
boolean verified = sv.verify(sig);
```

If a `Signature` object is instantiated with a family name, it accepts ML-DSA
keys in the family with any parameter set. If it is instantiated with a
parameter-set name, it only accepts ML-DSA keys that use that parameter set;
otherwise, the `initSign` and `initVerify` methods throw an
`InvalidKeyException`.


### Encoding and decoding ML-DSA keys

You can use the ML-DSA `KeyFactory` implementation to convert an ML-DSA private
key to or from its PKCS #8 encoding, or an ML-DSA public key to or from its
X.509 encoding.

For example, to convert a ML-DSA private key to its PKCS #8 encoding, and
vice-versa:

```
KeyFactory f = KeyFactory.getInstance("ML-DSA");
PKCS8EncodedKeySpec p8spec = f.getKeySpec(kp.getPrivate(),
                                          PKCS8EncodedKeySpec.class);
PrivateKey sk2 = f.generatePrivate(p8spec);
```

Similarly, to convert a ML-DSA public key to its X.509 encoding, and vice-versa:

```
X509EncodedKeySpec x509spec = f.getKeySpec(kp.getPublic(),
                                           X509EncodedKeySpec.class);
PublicKey pk2 = f.generatePublic(x509spec);
```

The `KeyFactory` implementation can also translate a key from another security
provider using the `translateKey` method, as long as its encoding format is
supported.

The `getAlgorithm` method of a `Key` object generated by an ML-DSA
`KeyPairGenerator` or `KeyFactory` implementation always returns the family name
`"ML-DSA"`, regardless of whether the `KeyPairGenerator` or `KeyFactory` was
instantiated with the `"ML-DSA"` family name or one of the parameter-set names.
The `getParams` method of an ML-DSA key returns a `NamedParameterSpec` object
that matches the key's parameter-set name.

If a `KeyFactory` object is instantiated with a family name, it encodes or
decodes ML-DSA keys in the family with any parameter set. If it is instantiated
with a parameter-set name, it only encodes or decodes ML-DSA keys that use that
parameter set; otherwise, the `translateKey` method throws an
`InvalidKeyException`, and the `generatePrivate`, `generatePublic`, and
`getKeySpec` methods throw an `InvalidKeySpecException`.

The encoding used by the ML-DSA `KeyFactory` is defined in a [draft IETF
RFC](https://datatracker.ietf.org/doc/html/draft-ietf-lamps-dilithium-certificates).
We will track changes in this draft until it is published.


Alternatives
------------

- The [Open Quantum Safe project](https://openquantumsafe.org/) provides a [JNI
wrapper](https://github.com/open-quantum-safe/liboqs-java) for their [`liboqs` C
library](https://github.com/open-quantum-safe/liboqs/), which implements a
collection of quantum-resistant algorithms including Dilithium and ML-DSA. If
Open Quantum Safe achieves its goal of becoming the primary quantum-resistant
cryptography implementation for major projects such as OpenSSL, BoringSSL,
OpenSSH, and Mozilla, then it will gain substantial performance and robustness
through widespread testing and usage.

  Compared to a native implementation, a Java implementation of ML-DSA provides
the key benefit of being integrated directly into the JDK. This makes it
immediately available on all of the platforms to which the JDK is already
ported.


Testing
-------

- Unit tests will confirm that the implementations comply with the
  specifications of the `KeyGenerator`, `Signature`, and `KeyFactory` APIs,
  including edge cases such as invalid input parameters, boundary values, and
  unsupported operations.

- Known Answer Tests (KATs) will cover both valid cryptographic operations
  (positive cases) and invalid operations or known vulnerabilities (negative
  cases), ensuring comprehensive validation. These will include but not be
  limited to:

    * KATs
      ([here](https://github.com/usnistgov/ACVP-Server/tree/master/gen-val/json-files/ML-DSA-keyGen-FIPS204),
      [here](https://github.com/usnistgov/ACVP-Server/tree/master/gen-val/json-files/ML-DSA-sigGen-FIPS204),
      and
      [here](https://github.com/usnistgov/ACVP-Server/tree/master/gen-val/json-files/ML-DSA-sigVer-FIPS204))
      generated by NIST's [Cryptographic Algorithm Validation Program
      service](https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program),
      and

    * The ML-DSA tests from [Project
      Wycheproof](https://github.com/C2SP/wycheproof), which are are [in
      development](https://github.com/C2SP/wycheproof/pull/112).

- Interoperation tests with implementations from other vendors, including but
  not limited to `liboqs`, will confirm that our ML-DSA implementation works
  well with others.