silk.crypto.ml_key.

Read and write ML-KEM and ML-DSA keys in DER: PKCS #8 private keys and SubjectPublicKeyInfo public keys, in the IETF LAMPS profiles for X.509 (draft-ietf-lamps-dilithium-certificates and draft-ietf-lamps-kyber-certificates).

When to use#

Use MlKeyDer to load an ML-KEM or ML-DSA key that OpenSSL, Node.js, or another library wrote, or to export one in those forms. The reader tells you the algorithm; pass the bytes to silk.crypto.ml_kem or silk.crypto.ml_dsa.

Details#

A public key is the PUBLIC KEY form: the algorithm identifier and the raw encapsulation key in a BIT STRING. A private key is the PRIVATE KEY form of RFC 5958 whose inner value is one of three choices:

  • the seed alone, with the context tag [0]: 64 bytes d || z for ML-KEM, 32 bytes for ML-DSA;

  • the expanded private key alone, in an OCTET STRING;

  • both, as a SEQUENCE of the seed and the expanded key.

The algorithm identifiers have no parameters. The readers return views into the DER and do not copy. Nothing allocates. PEM is DER in Base64 between header lines; use silk.encoding.pem for it.

Errors#

  • MlKeyError for DER that is not valid, for another algorithm, or for a key of the wrong length.

Gotchas#

The readers check the structure and the lengths, but not the values. A private key with both forms can hold an expanded key that does not belong to its seed. Expand the seed with MlKem.keyPairFromSeed or MlDsa.keyPairFromSeed and compare when that matters, as the profiles ask. Check an ML-KEM expanded key alone with MlKem.checkDecapsulationKey.

Examples#

Write and read an ML-KEM-768 private key seed#

silk
import silk.collections.slice { Slice }
import silk.core.option { Option }
import silk.core.result { Result }
import silk.crypto.ml_key { MlKeyAlgorithm, MlKeyDer, MlKeyError, MlPrivateKeyDer }

pub fn main() -> i32 {
  let seed: [u8; 64] = [9; 64]
  let mut der: [u8; 86] = [0; 86]
  let none = Option<&[u8]>.none()
  let length = match MlKeyDer.writePkcs8(MlKeyAlgorithm.MlKem768, Option<&[u8]>.some(&seed), none, &mut der) {
    Result<usize, MlKeyError>.Success { value } => value
    Result<usize, MlKeyError>.Failure {..} => {
      return 1
    }
  }
  // The seed form of PKCS #8 is 86 bytes.
  if length != 86 {
    return 1
  }
  return match MlKeyDer.readPkcs8(Slice.view<u8>(&der, 0, length)) {
    Result<MlPrivateKeyDer, MlKeyError>.Success { value } => {
      if value.algorithm == MlKeyAlgorithm.MlKem768 {
        return 0
      }
      return 1
    }
    Result<MlPrivateKeyDer, MlKeyError>.Failure {..} => 1
  }
}

Import as MlKeyDer with import silk.crypto.ml_key { MlKeyDer }.

Public declarations: 5.

MlKeyAlgorithm#

silk
pub enum MlKeyAlgorithm

An algorithm whose keys MlKeyDer reads and writes.

MlKem512#

silk
MlKem512

MlKem768#

silk
MlKem768

MlKem1024#

silk
MlKem1024

MlDsa44#

silk
MlDsa44

MlDsa65#

silk
MlDsa65

MlDsa87#

silk
MlDsa87

Implementation MlKeyAlgorithm#

silk
impl MlKeyAlgorithm

Associated function fromMlKem#

silk
pub fn fromMlKem(parameters: MlKemParameters) -> MlKeyAlgorithm

Returns the algorithm of an ML-KEM parameter set.

Associated function fromMlDsa#

silk
pub fn fromMlDsa(parameters: MlDsaParameters) -> MlKeyAlgorithm

Returns the algorithm of an ML-DSA parameter set.

Method mlKemParameters#

silk
pub fn mlKemParameters(self: &Self) -> Option<MlKemParameters>

Returns the ML-KEM parameter set, or None for an algorithm that is not ML-KEM.

Method mlDsaParameters#

silk
pub fn mlDsaParameters(self: &Self) -> Option<MlDsaParameters>

Returns the ML-DSA parameter set, or None for an algorithm that is not ML-DSA.

Method seedLength#

silk
pub fn seedLength(self: &Self) -> usize

Returns the length of the private key seed: 64 bytes for ML-KEM and 32 for ML-DSA.

Method expandedKeyLength#

silk
pub fn expandedKeyLength(self: &Self) -> usize

Returns the length of the expanded private key: the ML-KEM decapsulation key, or the ML-DSA private key.

Method publicKeyLength#

silk
pub fn publicKeyLength(self: &Self) -> usize

Returns the length of the public key: the ML-KEM encapsulation key, or the ML-DSA public key.

Method objectIdentifier#

silk
pub fn objectIdentifier(self: &Self) -> &[u8]

Returns the DER content of the object identifier, without the tag and length.

Implementation Debug for MlKeyAlgorithm#

silk
impl Debug for MlKeyAlgorithm

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> ()
    ! WriterError
    ? &mut Writer

MlKeyError#

silk
pub enum MlKeyError

Why MlKeyDer refused a key.

InvalidEncoding#

silk
InvalidEncoding

The bytes are not the DER structure of the key, or an output buffer is too short.

UnsupportedAlgorithm#

silk
UnsupportedAlgorithm

The algorithm identifier names another algorithm.

InvalidLength#

silk
InvalidLength

A key has the wrong length for its algorithm, or a private key has neither form.

Implementation Debug for MlKeyError#

silk
impl Debug for MlKeyError

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> ()
    ! WriterError
    ? &mut Writer

MlPrivateKeyDer#

silk
pub struct MlPrivateKeyDer<'a>

A private key read from PKCS #8: views into the DER. At least one of seed and expandedKey is Some, and each has the length of the algorithm.

Field algorithm#

silk
pub algorithm: MlKeyAlgorithm

Field seed#

silk
pub seed: Option<&'a [u8]>

Field expandedKey#

silk
pub expandedKey: Option<&'a [u8]>

MlPublicKeyDer#

silk
pub struct MlPublicKeyDer<'a>

A public key read from SubjectPublicKeyInfo: a view into the DER.

Field algorithm#

silk
pub algorithm: MlKeyAlgorithm

Field publicKey#

silk
pub publicKey: &'a [u8]

MlKeyDer#

silk
pub struct MlKeyDer

Reads and writes ML-KEM and ML-DSA keys as PKCS #8 and SubjectPublicKeyInfo DER.

Implementation MlKeyDer#

silk
impl MlKeyDer

Associated function readPkcs8#

silk
pub fn readPkcs8<'a>(der: &'a [u8]) -> Result<MlPrivateKeyDer<'a>, MlKeyError>

Reads a PKCS #8 PRIVATE KEY in any of the three private key forms.

The version can be 0 or 1, and attributes are skipped. A version 1 public key is refused, since the profile has none.

Errors#

  • InvalidEncoding, UnsupportedAlgorithm, or InvalidLength.

Associated function readSpki#

silk
pub fn readSpki<'a>(der: &'a [u8]) -> Result<MlPublicKeyDer<'a>, MlKeyError>

Reads a SubjectPublicKeyInfo PUBLIC KEY.

Errors#

  • InvalidEncoding, UnsupportedAlgorithm, or InvalidLength.

Associated function pkcs8Length#

silk
pub fn pkcs8Length(algorithm: MlKeyAlgorithm, hasSeed: bool, hasExpandedKey: bool) -> usize

Returns the length of the PKCS #8 DER that MlKeyDer.writePkcs8 writes for the forms that are present.

Associated function writePkcs8#

silk
pub fn writePkcs8(
    algorithm: MlKeyAlgorithm,
    seed: Option<&[u8]>,
    expandedKey: Option<&[u8]>,
    output: &mut [u8],
  ) -> Result<usize, MlKeyError>

Writes a PKCS #8 PRIVATE KEY into output and returns its length. With seed alone it writes the seed form, with expandedKey alone the expanded form, and with both the form that holds both.

Errors#

  • InvalidLength if neither is given, or one has the wrong length.

  • InvalidEncoding if output is shorter than MlKeyDer.pkcs8Length.

Associated function spkiLength#

silk
pub fn spkiLength(algorithm: MlKeyAlgorithm) -> usize

Returns the length of the SubjectPublicKeyInfo DER for algorithm.

Associated function writeSpki#

silk
pub fn writeSpki(algorithm: MlKeyAlgorithm, publicKey: &[u8], output: &mut [u8]) -> Result<
    usize,
    MlKeyError,
  >

Writes a SubjectPublicKeyInfo PUBLIC KEY into output and returns its length.

Errors#

  • InvalidLength if publicKey has the wrong length.

  • InvalidEncoding if output is shorter than MlKeyDer.spkiLength.

Category: Cryptography.

Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, wasm32-wasip1, wasm32-wasip2, x86_64-pc-windows-msvc, wasm64-unknown-unknown.

View the source of this page