Skip to content

Security: theQRL/qrypto.js

SECURITY.md

Security Considerations

This document describes security considerations for using qrypto.js, including JavaScript-specific limitations and best practices.

Overview

qrypto.js implements post-quantum digital signature algorithms (Dilithium5 and ML-DSA-87). While the cryptographic algorithms are secure, JavaScript/Node.js environments have inherent limitations that affect how secret key material can be handled.

JavaScript Memory Security Limitations

No Guaranteed Secure Zeroization

JavaScript does not provide:

  1. Direct memory access - Cannot guarantee memory is overwritten at a specific location
  2. Compiler barrier semantics - JIT compilers may optimize away "dead store" writes
  3. Control over garbage collection - Memory may persist until GC runs (non-deterministic)
  4. Protection against memory swapping - Secrets may be written to disk swap space

TypedArray Limitations

Even with Uint8Array.fill(0):

  • The operation may be optimized away by the JIT compiler if the array is not used afterward
  • Previous values may remain in memory until the garbage collector reclaims the buffer
  • The JavaScript runtime may have copied the data internally (e.g., during Buffer operations)

Buffer Behavior (Node.js)

  • Buffer.alloc() initializes memory to zero but doesn't prevent later exposure
  • Buffer.allocUnsafe() may contain previous memory contents
  • Buffer pooling may cause memory reuse across different operations

String Immutability

Converting secret key data to/from strings creates copies that:

  • Cannot be overwritten (strings are immutable in JavaScript)
  • May be interned or cached by the runtime
  • Persist until garbage collected

What qrypto.js Does

Despite these limitations, qrypto.js implements defense-in-depth measures:

1. Uses Uint8Array for Secrets

All secret key material is stored in Uint8Array buffers, not strings. This allows:

  • Explicit zeroing (even if not guaranteed to be secure)
  • Avoiding string interning
  • Clearer intent about data sensitivity

2. Constant-Time Comparison

Signature verification uses constant-time comparison to prevent timing attacks:

// From cryptoSignVerify:
let diff = 0;
for (i = 0; i < length; ++i) {
  diff |= a[i] ^ b[i];
}
return diff === 0;

Timing Considerations for Arithmetic Operations

The Montgomery reduction and other arithmetic operations use JavaScript's BigInt type. Important: The JavaScript specification does not guarantee that BigInt operations are constant-time. The execution time of operations like multiplication and division may vary based on operand values.

Implications:

  • Signing operations that use these arithmetic functions may have timing variations
  • This is a known limitation of JavaScript cryptographic implementations
  • Signature verification uses constant-time comparison (see above), which is the critical path for timing attacks

Mitigations for sensitive deployments:

  • For applications with strict constant-time requirements, consider using the Go implementation (go-qrllib) which provides better timing guarantees
  • Rate-limit signature operations at the application layer to reduce timing attack feasibility
  • Run signing operations in isolated environments where timing cannot be observed

3. Input Validation

All cryptographic functions validate input lengths and types to prevent:

  • Buffer overflow/underflow issues
  • Type confusion attacks
  • Invalid parameter combinations

4. Zeroize Utility (Best Effort)

The zeroize() function is provided for clearing sensitive buffers:

import { zeroize } from '@aspect-build/qrypto-common';

const sk = new Uint8Array(CryptoSecretKeyBytes);
cryptoSignKeypair(seed, pk, sk);

// Use the secret key...
const signature = cryptoSign(message, sk);

// Clear when done (best effort)
zeroize(sk);

Important: Due to JavaScript limitations, this is a best-effort operation. There is no guarantee that:

  • The memory is actually zeroed (JIT optimization)
  • Copies don't exist elsewhere (GC, Buffer pooling)
  • The data wasn't swapped to disk

Recommendations

For Application Developers

  1. Minimize secret lifetime - Generate keys only when needed, zero them as soon as possible
  2. Avoid serialization - Don't convert secrets to strings, JSON, or other formats
  3. Don't log secrets - Never log, print, or transmit secret key material
  4. Use secure storage - For persistent keys, consider:
    • Hardware Security Modules (HSMs)
    • Operating system keychains
    • Encrypted storage with proper key management
  5. Consider WebCrypto - For browser environments, WebCrypto provides non-extractable keys

For High-Security Applications

If your threat model requires strong memory protection:

  1. Use native implementations - Consider go-qrllib or C implementations that provide better memory control
  2. Use HSMs - Hardware Security Modules provide the strongest protection
  3. Isolate processes - Run cryptographic operations in isolated processes/containers
  4. Disable swap - On systems handling secrets, consider disabling swap or using encrypted swap

Algorithm Security

Dilithium5 (Round 3)

  • NIST PQC Round 3 finalist
  • Security level: Category 5 (equivalent to AES-256)
  • Key sizes: PK=2592, SK=4896, Sig=4595 bytes
  • Cross-verified against pq-crystals reference (ac743d5)

ML-DSA-87 (FIPS 204)

  • NIST FIPS 204 standardized algorithm
  • Security level: Category 5 (equivalent to AES-256)
  • Key sizes: PK=2592, SK=4896, Sig=4627 bytes
  • Includes context parameter for domain separation
  • Cross-verified against pq-crystals reference (latest)

Reporting Security Issues

If you discover a security vulnerability in qrypto.js:

  1. Do not open a public GitHub issue
  2. Contact the QRL security team privately
  3. Provide detailed reproduction steps
  4. Allow reasonable time for a fix before public disclosure

Supply Chain Security

npm Provenance

All npm packages are published with npm provenance, which cryptographically links published packages to their source repository and build workflow.

Verify provenance on npm:

npm audit signatures

Sigstore Attestations

All releases include GitHub attestations backed by Sigstore:

  • Build provenance for checksums and package files
  • SBOM attestations in SPDX and CycloneDX formats
  • SLSA Level 3 provenance for build verification

Dependency Tracking

Each release includes Software Bill of Materials (SBOM) files:

  • sbom-spdx.json - SPDX format
  • sbom-cyclonedx.json - CycloneDX format

Release Verification

All releases include cryptographic attestations and checksums for verification.

Verifying with GitHub CLI

# Verify attestations for package files
gh attestation verify package.json --owner theQRL
gh attestation verify package-lock.json --owner theQRL

# Verify SBOM attestation
gh attestation verify sbom-spdx.json --owner theQRL

Verifying Checksums

Download and verify checksums from the release:

# Download checksums file
curl -LO https://github.com/theQRL/qrypto.js/releases/download/vX.Y.Z/checksums-sha256.txt

# Verify package files
sha256sum -c checksums-sha256.txt

Verifying SLSA Provenance

# Install slsa-verifier: https://github.com/slsa-framework/slsa-verifier#installation

# Download provenance
curl -LO https://github.com/theQRL/qrypto.js/releases/download/vX.Y.Z/provenance.intoto.jsonl

# Verify provenance
slsa-verifier verify-artifact package.json \
  --provenance-path provenance.intoto.jsonl \
  --source-uri github.com/theQRL/qrypto.js

Software Bill of Materials (SBOM)

Each release includes SBOMs in two formats:

  • SPDX: sbom-spdx.json
  • CycloneDX: sbom-cyclonedx.json

These can be analyzed with tools like:

# Using grype for vulnerability scanning
grype sbom:sbom-spdx.json

# Using syft for inspection
syft convert sbom-cyclonedx.json -o table

What Gets Attested

Artifact Attestation Type Purpose
package.json, package-lock.json Build provenance Verify package dependencies
checksums-sha256.txt Build provenance Integrity verification
sbom-spdx.json SBOM Software composition
sbom-cyclonedx.json SBOM Software composition
Source code SLSA provenance Build reproducibility
npm package npm provenance Package authenticity

Trust Model

Attestations are signed using GitHub's Sigstore integration:

  • Identity: GitHub Actions OIDC token
  • Transparency: Logged in Sigstore's Rekor transparency log
  • Verification: Proves release came from official CI workflow

References

There aren’t any published security advisories