#post-quantum #pqc #ml-kem #generate-keys #crypto #ml-dsa

QuantCrypt

Library for post-quantum cryptography and PKI

2 unstable releases

0.2.0 Oct 14, 2024
0.1.0 Oct 11, 2024

#365 in Cryptography

Download history 129/week @ 2024-10-07 192/week @ 2024-10-14

52 downloads per month

Apache-2.0 OR MIT

445KB
7.5K SLoC

QuantCrypt

Crates.io Version example workflow dependency status

The goal of this library is to provide a simple and easy-to-use interface for generating key pairs, certificates, signing and verifying messages, and encrypting and decrypting messages using post-quantum cryptographic algorithms.

A secondary goal is to provide a set of cryptographic algorithms that are compatible with existing X.509, PKIX, and CMS data structures and protocols and to support the efforts of the LAMPS Working Group in the IETF especially the draft-ietf-lamps-pq-composite-sigs and draft-ietf-lamps-pq-composite-kem drafts.

Including quantcrypt in your project

Import quantcrypt into your project by adding the following lines to your Cargo.toml.

[dependencies]
quantcrypt = "0.2.0"

For the purposes of the PQC Hackathon, the library can also be included in IPD mode (for ML-DSA and ML-KEM only). This mode is enabled by setting the ipd feature in your Cargo.toml.

[dependencies]
quantcrypt = { version = "0.2.0", features = ["ipd"] }

When the ipd feature is enabled, the library will use IPD OIDs and will not use the newly introduced context parameter in the finalized FIPS 204 standard. SLH-DSA signatures will not be supported in this mode.

Otherwise, it will use the finalized OIDs where possible, and will use the context parameter in the finalized FIPS 204 standard by setting it to an empty string.

Generating PQC Hackathon Artifacts for IETF Hackathon - PQC Certificates

For IPD artifacts (presently compatible with OQS provider)

cargo test gen_pq_hackathon_artifacts_r3 --release --features ipd # generate artifacts in r3 format
cargo test gen_pq_hackathon_artifacts_r4 --release --features ipd # generate artifacts in r4 format

For non-IPD artifacts (with the NIST context parameter)

cargo test gen_pq_hackathon_artifacts_r3 --release # generate artifacts in r3 format
cargo test gen_pq_hackathon_artifacts_r4 --release # generate artifacts in r4 format

To generate submissios zips:

python prepare_submission.py # Select appropriate certificates and archive them as zips for submission

Artifacts in both r3 and r4 format are generated. They can be found in artifacts/submission folder.

Interoperability Results

Once the artifacts are submitted to the IETF Hackathon PQC Certificates repository, the interoperability results can be found at the IETF PQC Hackathon Certificate Automated Verification Interoperability Results page.

Generating Key Pairs and Certificates

The following snippet demonstrates how to generate a key pair and a certificate using the DSA and KEM algorithms. In addition to pure ML-DSA and ML-KEM algorithms, the library also supports composite algorithms that combine a traditional and post-quantum algorithm into a single key pair and certificate.

use quantcrypt::certificates::CertificateBuilder;
use quantcrypt::dsas::DsaAlgorithm;
use quantcrypt::kems::KemAlgorithm;
use quantcrypt::certificates::Profile;
use quantcrypt::dsas::DsaKeyGenerator;
use quantcrypt::kems::KemKeyGenerator;
use quantcrypt::certificates::CertValidity;

// Create a TA key pair
let (pk_root, sk_root) = DsaKeyGenerator::new(DsaAlgorithm::MlDsa44).generate().unwrap();

let profile = Profile::Root;
let serial_no = None; // This will generate a random serial number
let validity = CertValidity::new(None, "2035-01-01T00:00:00Z").unwrap(); // Not before is now
let subject = "CN=example.com".to_string();
let cert_public_key = pk_root.clone();
let signer = &sk_root;

// Create the TA certificate builder
let builder = CertificateBuilder::new(
    profile,
    serial_no,
    validity.clone(),
    subject.clone(),
    cert_public_key,
    signer
).unwrap();
let cert_root = builder.build().unwrap();
assert!(cert_root.verify_self_signed().unwrap());

// Create a leaf (EE) key pair for KEM
let (pk_kem, sk_kem) = KemKeyGenerator::new(KemAlgorithm::MlKem512).generate().unwrap();
let builder = CertificateBuilder::new(
    Profile::Leaf {
        issuer: cert_root.get_subject(),
        enable_key_agreement: false,
        enable_key_encipherment: true,
    },
    serial_no,
    validity,
    subject,
    pk_kem,
    signer
).unwrap();
let cert_kem = builder.build().unwrap();

// It's not self signed so verification so self signed should fail
assert!(!cert_kem.verify_self_signed().unwrap());

// But it should verify against the root
assert!(cert_root.verify_child(&cert_kem).unwrap());

Generating Enveloped Data CMS Message

The following snippet demonstrates how to generate a CMS message using the DSA and KEM algorithms.

use quantcrypt::content::EnvelopedDataContent;
use quantcrypt::content::ContentEncryptionAlgorithm;
use quantcrypt::certificates::Certificate;
use quantcrypt::keys::PrivateKey;
use quantcrypt::kdfs::KdfType;
use quantcrypt::wraps::WrapType;
use quantcrypt::content::UserKeyingMaterial;
use quantcrypt::content::ObjectIdentifier;
use quantcrypt::content::Attribute;
use quantcrypt::content::Tag;
use quantcrypt::content::AttributeValue;
use quantcrypt::content::SetOfVec;

// Based on whether IPD feature is enabled or not, use the appropriate test data
let rc_filename = if quantcrypt::is_ipd_mode_enabled() {
    "test/data_ipd/cms_cw/1.3.6.1.4.1.22554.5.6.1_ML-KEM-512-ipd_ee.der"
} else {
    "test/data/cms/2.16.840.1.101.3.4.4.1_MlKem512_ee.der"
};

let recipient_cert = Certificate::from_file(
    rc_filename,
).unwrap();

let sk_filename = if quantcrypt::is_ipd_mode_enabled() {
    "test/data_ipd/cms_cw/1.3.6.1.4.1.22554.5.6.1_ML-KEM-512-ipd_priv.der"
} else {
    "test/data/cms/2.16.840.1.101.3.4.4.1_MlKem512_priv.der"
};

let private_key = PrivateKey::from_file(
    sk_filename
).unwrap();

let ukm = UserKeyingMaterial::new("test".as_bytes()).unwrap();
let data = b"abc";

let attribute_oid = ObjectIdentifier::new("1.3.6.1.4.1.22554.5.6").unwrap();
let mut attribute_vals: SetOfVec<AttributeValue> = SetOfVec::<AttributeValue>::new();

let attr_val = AttributeValue::new(Tag::OctetString, data.to_vec()).unwrap();
attribute_vals.insert(attr_val).unwrap();

let attribute = Attribute {
    oid: attribute_oid,
    values: attribute_vals,
};

let mut builder = EnvelopedDataContent::get_builder(ContentEncryptionAlgorithm::Aes128Cbc).unwrap();

builder
    .kem_recipient(
        &recipient_cert,
        &KdfType::HkdfWithSha256,
        &WrapType::Aes256,
        Some(ukm),
    )
    .unwrap()
    .content(data)
    .unwrap()
    .unprotected_attribute(&attribute)
    .unwrap();

let content = builder.build().unwrap();

// Now use this content to create a new EnvelopedDataContent
let edc = EnvelopedDataContent::from_bytes_for_kem_recipient(
    &content,
    &recipient_cert,
    &private_key,
).unwrap();
assert_eq!(edc.get_content(), data);

Generating Auth Enveloped Data CMS Message

Auth Enveloped Data is much like the above snippet but using AuthEnvelopedDataContent instead of EnvelopedDataContent.

use quantcrypt::content::AuthEnvelopedDataContent;
use quantcrypt::content::ContentEncryptionAlgorithmAead;
use quantcrypt::certificates::Certificate;
use quantcrypt::keys::PrivateKey;
use quantcrypt::kdfs::KdfType;
use quantcrypt::wraps::WrapType;
use quantcrypt::content::UserKeyingMaterial;
use quantcrypt::content::ObjectIdentifier;
use quantcrypt::content::Attribute;
use quantcrypt::content::Tag;
use quantcrypt::content::AttributeValue;
use quantcrypt::content::SetOfVec;

let rc_filename = if quantcrypt::is_ipd_mode_enabled() {
    "test/data_ipd/cms_cw/1.3.6.1.4.1.22554.5.6.1_ML-KEM-512-ipd_ee.der"
} else {
    "test/data/cms/2.16.840.1.101.3.4.4.1_MlKem512_ee.der"
};

let recipient_cert = Certificate::from_file(
    rc_filename,
).unwrap();

let sk_filename = if quantcrypt::is_ipd_mode_enabled() {
    "test/data_ipd/cms_cw/1.3.6.1.4.1.22554.5.6.1_ML-KEM-512-ipd_priv.der"
} else {
    "test/data/cms/2.16.840.1.101.3.4.4.1_MlKem512_priv.der"
};

let private_key = PrivateKey::from_file(
    sk_filename
).unwrap();

let ukm = UserKeyingMaterial::new("test".as_bytes()).unwrap();
let data = b"abc";

let attribute_oid = ObjectIdentifier::new("1.3.6.1.4.1.22554.5.6").unwrap();
let mut attribute_vals: SetOfVec<AttributeValue> = SetOfVec::<AttributeValue>::new();

let attr_val = AttributeValue::new(Tag::OctetString, data.to_vec()).unwrap();
attribute_vals.insert(attr_val).unwrap();

let attribute = Attribute {
    oid: attribute_oid,
    values: attribute_vals,
};

let mut builder = AuthEnvelopedDataContent::get_builder(ContentEncryptionAlgorithmAead::Aes256Gcm).unwrap();

builder
    .kem_recipient(
        &recipient_cert,
        &KdfType::HkdfWithSha256,
        &WrapType::Aes256,
        Some(ukm),
    )
    .unwrap()
    .content(data)
    .unwrap()
    .auth_attribute(&attribute)
    .unwrap();

let content = builder.build().unwrap();

// Now use this content to create a new AuthEnvelopedDataContent
let edc = AuthEnvelopedDataContent::from_bytes_for_kem_recipient(
    &content,
    &recipient_cert,
    &private_key,
).unwrap();
assert_eq!(edc.get_content(), data);

Minimum Supported Rust Version (MSRV)

The minimum supported Rust version for this library is 1.81.0

License

All crates licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Dependencies

~23MB
~312K SLoC