Skip to main content

falcon_multisig/
keypair.rs

1//! Falcon-512 keypair — signing and public key management.
2//!
3//! [`KeyPair`] is the primary signing primitive. It wraps a Falcon-512 secret key
4//! and its associated public key, ensures the secret key bytes are zeroized on drop,
5//! and enforces domain separation on every signed message.
6
7#[cfg(not(feature = "std"))]
8use alloc::vec::Vec;
9
10use falcon_rust::falcon512::{self as fr, SecretKey as FrSecretKey};
11use rand::RngCore;
12use sha3::{Digest, Sha3_256};
13use zeroize::{Zeroize, ZeroizeOnDrop};
14
15use crate::{
16    error::Error,
17    verify::verify_raw,
18    DOMAIN_TAG, PUBLIC_KEY_BYTES,
19};
20
21// ---------------------------------------------------------------------------
22// PublicKey newtype
23// ---------------------------------------------------------------------------
24
25/// A validated Falcon-512 public key (897 bytes).
26///
27/// Constructing a `PublicKey` from raw bytes validates the length at the point
28/// of construction, so a value of this type always has the correct size.
29#[derive(Clone, Debug, PartialEq, Eq)]
30#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
31pub struct PublicKey(
32    #[cfg_attr(feature = "serde", serde(with = "hex_bytes"))]
33    Vec<u8>,
34);
35
36impl PublicKey {
37    /// Construct a `PublicKey` from raw bytes, validating the length.
38    pub fn from_bytes(bytes: Vec<u8>) -> Result<Self, Error> {
39        if bytes.len() != PUBLIC_KEY_BYTES {
40            return Err(Error::InvalidPublicKeyLength {
41                expected: PUBLIC_KEY_BYTES,
42                actual: bytes.len(),
43            });
44        }
45        Ok(Self(bytes))
46    }
47
48    /// Return a reference to the raw public key bytes.
49    pub fn as_bytes(&self) -> &[u8] {
50        &self.0
51    }
52
53    /// Return the inner byte vector, consuming the `PublicKey`.
54    pub fn into_bytes(self) -> Vec<u8> {
55        self.0
56    }
57
58    /// Derive the canonical single-key address from this public key.
59    ///
60    /// The address is the first 20 bytes of SHA3-256(public_key), encoded as
61    /// a lowercase hex string prefixed with `"0x"`.
62    pub fn to_address(&self) -> crate::address::SingleKeyAddress {
63        crate::address::SingleKeyAddress::from_public_key(self)
64    }
65}
66
67// ---------------------------------------------------------------------------
68// KeyPair
69// ---------------------------------------------------------------------------
70
71/// A Falcon-512 keypair with a zeroizing secret key.
72///
73/// The secret key bytes are zeroed in memory when the `KeyPair` is dropped,
74/// using [`zeroize`].
75///
76/// # Serialization
77///
78/// When the `serde` feature is enabled, `KeyPair` serializes **only the public
79/// key**. The secret key is never written to any serialized form; this is
80/// intentional. To persist a secret key, use [`KeyPair::secret_key_bytes`] and
81/// store the bytes in an encrypted wallet or HSM.
82///
83/// # Thread Safety
84///
85/// `KeyPair` is `Send` but not `Sync` because the underlying `FrSecretKey` does
86/// not implement `Sync`. Wrap in a `Mutex` for shared use across threads.
87pub struct KeyPair {
88    public_key: PublicKey,
89    secret_key: SecretKeyBytes,
90}
91
92/// Zeroizing wrapper for the raw secret key bytes.
93#[derive(Zeroize, ZeroizeOnDrop)]
94struct SecretKeyBytes(Vec<u8>);
95
96impl KeyPair {
97    /// Generate a new Falcon-512 keypair using the OS random number generator.
98    ///
99    /// Keygen is computationally expensive (~10 ms on a typical server CPU).
100    /// Pre-generate keys and cache them when throughput matters.
101    pub fn generate() -> Self {
102        let mut seed = [0u8; 32];
103        rand::thread_rng().fill_bytes(&mut seed);
104        let (sk, pk) = fr::keygen(seed);
105        Self {
106            public_key: PublicKey(pk.to_bytes().to_vec()),
107            secret_key: SecretKeyBytes(sk.to_bytes().to_vec()),
108        }
109    }
110
111    /// Reconstruct a `KeyPair` from raw secret key and public key bytes.
112    ///
113    /// Both inputs are validated for length. The public key is not mathematically
114    /// verified against the secret key — callers are responsible for ensuring
115    /// the pair is consistent.
116    pub fn from_bytes(sk_bytes: &[u8], pk_bytes: &[u8]) -> Result<Self, Error> {
117        if pk_bytes.len() != PUBLIC_KEY_BYTES {
118            return Err(Error::InvalidPublicKeyLength {
119                expected: PUBLIC_KEY_BYTES,
120                actual: pk_bytes.len(),
121            });
122        }
123        Ok(Self {
124            public_key: PublicKey(pk_bytes.to_vec()),
125            secret_key: SecretKeyBytes(sk_bytes.to_vec()),
126        })
127    }
128
129    /// Return a reference to the public key.
130    pub fn public_key(&self) -> &PublicKey {
131        &self.public_key
132    }
133
134    /// Return the raw secret key bytes.
135    ///
136    /// Handle the returned slice with care. Do not log, serialize, or store it
137    /// in plaintext. Prefer storing in an encrypted wallet.
138    pub fn secret_key_bytes(&self) -> &[u8] {
139        &self.secret_key.0
140    }
141
142    /// Sign a message with this keypair.
143    ///
144    /// Internally, the function computes:
145    ///
146    /// ```text
147    /// digest = SHA3-256(FALCON_MULTISIG_V1: || message)
148    /// signature = Falcon512::sign(digest, secret_key)
149    /// ```
150    ///
151    /// The returned bytes are the raw Falcon-512 signature (variable length,
152    /// up to [`SIGNATURE_MAX_BYTES`] bytes). The digest is **not** appended;
153    /// callers hold the message and recompute the digest during verification.
154    ///
155    /// # Errors
156    ///
157    /// Returns [`Error::InvalidPublicKeyLength`] if the stored key is somehow
158    /// malformed (should never occur for keys produced by [`KeyPair::generate`]).
159    pub fn sign(&self, message: &[u8]) -> Vec<u8> {
160        let digest = domain_hash(message);
161        let sk = FrSecretKey::from_bytes(&self.secret_key.0)
162            .expect("KeyPair::sign: stored secret key is malformed — this is a bug");
163        fr::sign(&digest, &sk).to_bytes().to_vec()
164    }
165
166    /// Derive the canonical single-key address from this keypair.
167    pub fn address(&self) -> crate::address::SingleKeyAddress {
168        self.public_key.to_address()
169    }
170
171    /// Verify that a raw signature was produced by this keypair's secret key
172    /// over the given message.
173    pub fn verify_own_signature(&self, message: &[u8], signature: &[u8]) -> Result<bool, Error> {
174        verify_raw(message, signature, self.public_key.as_bytes(), 0)
175    }
176}
177
178impl core::fmt::Debug for KeyPair {
179    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
180        f.debug_struct("KeyPair")
181            .field("public_key", &self.public_key)
182            .field("secret_key", &"[REDACTED]")
183            .finish()
184    }
185}
186
187// Serialization: public key only.
188#[cfg(feature = "serde")]
189impl serde::Serialize for KeyPair {
190    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
191        use serde::ser::SerializeStruct;
192        let mut state = serializer.serialize_struct("KeyPair", 1)?;
193        state.serialize_field("public_key", &self.public_key)?;
194        state.end()
195    }
196}
197
198// ---------------------------------------------------------------------------
199// Domain-separated hash helper
200// ---------------------------------------------------------------------------
201
202/// Compute `SHA3-256(DOMAIN_TAG || message)`.
203///
204/// This is the canonical digest that both signing and verification operate over.
205/// All callers within this crate use this function to ensure consistency.
206pub(crate) fn domain_hash(message: &[u8]) -> [u8; 32] {
207    let mut hasher = Sha3_256::new();
208    hasher.update(DOMAIN_TAG);
209    hasher.update(message);
210    let result = hasher.finalize();
211    let mut out = [0u8; 32];
212    out.copy_from_slice(&result);
213    out
214}
215
216// ---------------------------------------------------------------------------
217// Hex serde helper (used by PublicKey)
218// ---------------------------------------------------------------------------
219
220#[cfg(feature = "serde")]
221mod hex_bytes {
222    use serde::{Deserialize, Deserializer, Serializer};
223
224    #[cfg(not(feature = "std"))]
225    use alloc::vec::Vec;
226
227    pub fn serialize<S: Serializer>(bytes: &Vec<u8>, s: S) -> Result<S::Ok, S::Error> {
228        s.serialize_str(&hex::encode(bytes))
229    }
230
231    pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> {
232        let s = String::deserialize(d)?;
233        hex::decode(&s).map_err(serde::de::Error::custom)
234    }
235}
236
237// ---------------------------------------------------------------------------
238// Unit tests
239// ---------------------------------------------------------------------------
240
241#[cfg(test)]
242mod tests {
243    use super::*;
244    use crate::{SIGNATURE_MAX_BYTES, SIGNATURE_MIN_BYTES};
245
246    #[test]
247    fn generate_and_sign_verify_roundtrip() {
248        let kp = KeyPair::generate();
249        let message = b"test payload for signing";
250        let sig = kp.sign(message);
251
252        assert!(sig.len() >= SIGNATURE_MIN_BYTES);
253        assert!(sig.len() <= SIGNATURE_MAX_BYTES);
254
255        let ok = kp.verify_own_signature(message, &sig).unwrap();
256        assert!(ok, "freshly generated signature must verify");
257    }
258
259    #[test]
260    fn wrong_message_fails_verification() {
261        let kp = KeyPair::generate();
262        let sig = kp.sign(b"correct message");
263        let ok = kp.verify_own_signature(b"wrong message", &sig).unwrap();
264        assert!(!ok);
265    }
266
267    #[test]
268    fn wrong_key_fails_verification() {
269        let kp1 = KeyPair::generate();
270        let kp2 = KeyPair::generate();
271        let message = b"some message";
272        let sig = kp1.sign(message);
273        // kp2's public key cannot verify kp1's signature
274        let ok = verify_raw(message, &sig, kp2.public_key().as_bytes(), 0).unwrap();
275        assert!(!ok);
276    }
277
278    #[test]
279    fn public_key_length_is_correct() {
280        let kp = KeyPair::generate();
281        assert_eq!(kp.public_key().as_bytes().len(), PUBLIC_KEY_BYTES);
282    }
283
284    #[test]
285    fn from_bytes_rejects_bad_pk_length() {
286        let sk = KeyPair::generate();
287        let result = KeyPair::from_bytes(sk.secret_key_bytes(), &[0u8; 64]);
288        assert!(matches!(result, Err(Error::InvalidPublicKeyLength { .. })));
289    }
290
291    #[test]
292    fn debug_does_not_expose_secret_key() {
293        let kp = KeyPair::generate();
294        let debug_str = format!("{kp:?}");
295        assert!(debug_str.contains("[REDACTED]"));
296        assert!(!debug_str.contains("secret_key_bytes"));
297    }
298
299    #[test]
300    fn domain_hash_includes_tag() {
301        let with_tag = domain_hash(b"hello");
302        // Compute without tag manually
303        let mut hasher = Sha3_256::new();
304        hasher.update(b"hello");
305        let raw: [u8; 32] = hasher.finalize().into();
306        assert_ne!(with_tag, raw, "domain hash must differ from untagged hash");
307    }
308
309    #[test]
310    fn domain_hash_is_deterministic() {
311        assert_eq!(domain_hash(b"data"), domain_hash(b"data"));
312    }
313}