Skip to content

Falcon: expose API to recover public key from encoded private key #2297

@Federico2014

Description

@Federico2014

Summary

FalconPrivateKeyParameters does not expose a way to recover the corresponding FalconPublicKeyParameters (the polynomial h) from an already-encoded private key (f ‖ g ‖ F). Mathematically h = g · f⁻¹ mod (q, xⁿ+1) is fully determined by (f, g), but the FalconNIST.crypto_sign_keypair flow that computes h is internal and only runs during fresh key generation.

Reproduction

BC version: 1.79 (also reproduced on bcprov-jdk18on:1.78.1)

// Round-trip a Falcon-512 private key through its encoded form and try to
// reconstruct the keypair without the public key.
FalconKeyPairGenerator gen = new FalconKeyPairGenerator();
gen.init(new FalconKeyGenerationParameters(new SecureRandom(), FalconParameters.falcon_512));
AsymmetricCipherKeyPair kp = gen.generateKeyPair();

byte[] encodedSk = ((FalconPrivateKeyParameters) kp.getPrivate()).getEncoded();
// encodedSk layout for Falcon-512: f(384) || g(384) || F(512) — no h, no G.

// There is no public API to get back to FalconPublicKeyParameters from encodedSk.
// FalconPrivateKeyParameters has no getPublicKeyParameters() / derivePublicKey().
// Reconstructing via `new FalconPrivateKeyParameters(params, f, g, F, new byte[0])`
// gives a valid signing key but no way to obtain h.

Why this matters

  • Wallets / HSMs: a stored Falcon private key should be sufficient to recover its address. Today every consumer must persist (sk, pk) side-by-side or persist the 48-byte keygen seed and re-run keygen on every load, which is significantly slower than a one-shot g · f⁻¹ and is awkward when the seed was never retained (imported keys, HSM-extracted keys, etc.).
  • Symmetry with other PQC algorithms in BC: MLDSAPrivateKeyParameters.getPublicKeyParameters() and SLHDSAPrivateKeyParameters.getPublicKeyParameters() already exist; Falcon is the odd one out.
  • NIST FN-DSA (FIPS 206 draft) is converging on Falcon as SLH-DSA's sibling; downstream libraries are starting to assume "private key implies public key".

Suggested API

Either of these would resolve it; preferably both:

public class FalconPrivateKeyParameters extends FalconKeyParameters {
    /** Recover the public key (h = g · f⁻¹ mod q) corresponding to this private key. */
    public FalconPublicKeyParameters getPublicKeyParameters();
}

…and/or a stateless helper alongside the existing FalconKeyPairGenerator:

public class FalconKeyPairGenerator {
    public static FalconPublicKeyParameters derivePublicKey(FalconPrivateKeyParameters sk);
}

The math is already implemented inside FalconNIST (the compute_public step of crypto_sign_keypair); exposing it just requires lifting it out of the keygen-only path and feeding it the (f, g) decoded from the existing encoded private key.

Workaround

Currently consumers must either (a) store the 896-byte encoded h next to the 1280-byte private key, or (b) persist the 48-byte SHAKE256 keygen seed and re-derive the entire keypair on load. We're currently doing (a) and would happily switch to a getPublicKeyParameters() once available.

Happy to send a PR if the maintainers agree on the shape — please confirm whether getPublicKeyParameters() on the parameters class is the preferred location, or whether you'd rather keep this on the generator.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions