bitcoinpqc/
lib.rs

1#![allow(non_upper_case_globals)]
2#![allow(non_camel_case_types)]
3#![allow(non_snake_case)]
4
5use std::error::Error;
6use std::fmt;
7use std::ptr;
8
9// Include the auto-generated bindings using our wrapper
10// Make it pub(crate) so doctests can access these symbols
11pub(crate) mod bindings_include;
12// Use a glob import to get all the symbols consistently
13use bindings_include::*;
14
15/// Error type for PQC operations
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub enum PqcError {
18    /// Invalid arguments provided
19    BadArgument,
20    /// Invalid key provided
21    BadKey,
22    /// Invalid signature provided
23    BadSignature,
24    /// Algorithm not implemented
25    NotImplemented,
26    /// Other unexpected error
27    Other(i32),
28}
29
30impl fmt::Display for PqcError {
31    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32        match self {
33            PqcError::BadArgument => write!(f, "Invalid arguments provided"),
34            PqcError::BadKey => write!(f, "Invalid key provided"),
35            PqcError::BadSignature => write!(f, "Invalid signature provided"),
36            PqcError::NotImplemented => write!(f, "Algorithm not implemented"),
37            PqcError::Other(code) => write!(f, "Unexpected error code: {}", code),
38        }
39    }
40}
41
42impl Error for PqcError {}
43
44impl From<bitcoin_pqc_error_t> for Result<(), PqcError> {
45    fn from(error: bitcoin_pqc_error_t) -> Self {
46        match error {
47            bitcoin_pqc_error_t::BITCOIN_PQC_OK => Ok(()),
48            bitcoin_pqc_error_t::BITCOIN_PQC_ERROR_BAD_ARG => Err(PqcError::BadArgument),
49            bitcoin_pqc_error_t::BITCOIN_PQC_ERROR_BAD_KEY => Err(PqcError::BadKey),
50            bitcoin_pqc_error_t::BITCOIN_PQC_ERROR_BAD_SIGNATURE => Err(PqcError::BadSignature),
51            bitcoin_pqc_error_t::BITCOIN_PQC_ERROR_NOT_IMPLEMENTED => Err(PqcError::NotImplemented),
52            _ => Err(PqcError::Other(error.0)),
53        }
54    }
55}
56
57/// PQC Algorithm type
58#[derive(Debug, Clone, Copy, PartialEq, Eq)]
59pub enum Algorithm {
60    /// BIP-340 Schnorr + X-Only - Elliptic Curve Digital Signature Algorithm
61    SECP256K1_SCHNORR,
62    /// FN-DSA-512 (FALCON) - Fast Fourier lattice-based signature scheme
63    FN_DSA_512,
64    /// ML-DSA-44 (CRYSTALS-Dilithium) - Lattice-based signature scheme
65    ML_DSA_44,
66    /// SLH-DSA-Shake-128s (SPHINCS+) - Hash-based signature scheme
67    SLH_DSA_128S,
68}
69
70impl From<Algorithm> for bitcoin_pqc_algorithm_t {
71    fn from(alg: Algorithm) -> Self {
72        match alg {
73            Algorithm::SECP256K1_SCHNORR => bitcoin_pqc_algorithm_t::BITCOIN_PQC_SECP256K1_SCHNORR,
74            Algorithm::FN_DSA_512 => bitcoin_pqc_algorithm_t::BITCOIN_PQC_FN_DSA_512,
75            Algorithm::ML_DSA_44 => bitcoin_pqc_algorithm_t::BITCOIN_PQC_ML_DSA_44,
76            Algorithm::SLH_DSA_128S => bitcoin_pqc_algorithm_t::BITCOIN_PQC_SLH_DSA_SHAKE_128S,
77        }
78    }
79}
80
81/// Public key wrapper
82#[derive(Debug)]
83pub struct PublicKey {
84    /// The algorithm this key belongs to
85    pub algorithm: Algorithm,
86    /// The raw key bytes
87    pub bytes: Vec<u8>,
88}
89
90/// Secret key wrapper
91#[derive(Debug)]
92pub struct SecretKey {
93    /// The algorithm this key belongs to
94    pub algorithm: Algorithm,
95    /// The raw key bytes
96    pub bytes: Vec<u8>,
97}
98
99/// Signature wrapper
100#[derive(Debug, Clone)]
101pub struct Signature {
102    /// The algorithm this signature belongs to
103    pub algorithm: Algorithm,
104    /// The raw signature bytes
105    pub bytes: Vec<u8>,
106}
107
108impl Drop for SecretKey {
109    fn drop(&mut self) {
110        // Zero out secret key memory on drop
111        for byte in &mut self.bytes {
112            *byte = 0;
113        }
114    }
115}
116
117/// Key pair containing both public and secret keys
118#[derive(Debug)]
119pub struct KeyPair {
120    /// The public key
121    pub public_key: PublicKey,
122    /// The secret key
123    pub secret_key: SecretKey,
124}
125
126/// Generate a key pair for the specified algorithm
127///
128/// # Arguments
129///
130/// * `algorithm` - The PQC algorithm to use
131/// * `random_data` - Random bytes for key generation (must be at least 128 bytes)
132///
133/// # Returns
134///
135/// A new key pair on success, or an error
136pub fn generate_keypair(algorithm: Algorithm, random_data: &[u8]) -> Result<KeyPair, PqcError> {
137    if random_data.len() < 128 {
138        return Err(PqcError::BadArgument);
139    }
140
141    unsafe {
142        let mut keypair = bitcoin_pqc_keypair_t {
143            algorithm: algorithm.into(),
144            public_key: ptr::null_mut(),
145            secret_key: ptr::null_mut(),
146            public_key_size: 0,
147            secret_key_size: 0,
148        };
149
150        let result = bitcoin_pqc_keygen(
151            algorithm.into(),
152            &mut keypair,
153            random_data.as_ptr(),
154            random_data.len(),
155        );
156
157        if result != bitcoin_pqc_error_t::BITCOIN_PQC_OK {
158            return Err(match result {
159                bitcoin_pqc_error_t::BITCOIN_PQC_ERROR_BAD_ARG => PqcError::BadArgument,
160                bitcoin_pqc_error_t::BITCOIN_PQC_ERROR_BAD_KEY => PqcError::BadKey,
161                bitcoin_pqc_error_t::BITCOIN_PQC_ERROR_NOT_IMPLEMENTED => PqcError::NotImplemented,
162                _ => PqcError::Other(result.0 as i32),
163            });
164        }
165
166        // Extract and copy the keys
167        let pk_slice =
168            std::slice::from_raw_parts(keypair.public_key as *const u8, keypair.public_key_size);
169        let sk_slice =
170            std::slice::from_raw_parts(keypair.secret_key as *const u8, keypair.secret_key_size);
171
172        let pk_bytes = pk_slice.to_vec();
173        let sk_bytes = sk_slice.to_vec();
174
175        // Free the C memory
176        bitcoin_pqc_keypair_free(&mut keypair);
177
178        Ok(KeyPair {
179            public_key: PublicKey {
180                algorithm,
181                bytes: pk_bytes,
182            },
183            secret_key: SecretKey {
184                algorithm,
185                bytes: sk_bytes,
186            },
187        })
188    }
189}
190
191/// Sign a message using the specified secret key
192///
193/// # Arguments
194///
195/// * `secret_key` - The secret key to sign with
196/// * `message` - The message to sign
197///
198/// # Returns
199///
200/// A signature on success, or an error
201pub fn sign(secret_key: &SecretKey, message: &[u8]) -> Result<Signature, PqcError> {
202    unsafe {
203        let mut signature = bitcoin_pqc_signature_t {
204            algorithm: secret_key.algorithm.into(),
205            signature: ptr::null_mut(),
206            signature_size: 0,
207        };
208
209        let result = bitcoin_pqc_sign(
210            secret_key.algorithm.into(),
211            secret_key.bytes.as_ptr(),
212            secret_key.bytes.len(),
213            message.as_ptr(),
214            message.len(),
215            &mut signature,
216        );
217
218        if result != bitcoin_pqc_error_t::BITCOIN_PQC_OK {
219            return Err(match result {
220                bitcoin_pqc_error_t::BITCOIN_PQC_ERROR_BAD_ARG => PqcError::BadArgument,
221                bitcoin_pqc_error_t::BITCOIN_PQC_ERROR_BAD_KEY => PqcError::BadKey,
222                bitcoin_pqc_error_t::BITCOIN_PQC_ERROR_BAD_SIGNATURE => PqcError::BadSignature,
223                bitcoin_pqc_error_t::BITCOIN_PQC_ERROR_NOT_IMPLEMENTED => PqcError::NotImplemented,
224                _ => PqcError::Other(result.0 as i32),
225            });
226        }
227
228        // Extract and copy the signature
229        let sig_slice =
230            std::slice::from_raw_parts(signature.signature as *const u8, signature.signature_size);
231        let sig_bytes = sig_slice.to_vec();
232
233        // Free the C memory
234        bitcoin_pqc_signature_free(&mut signature);
235
236        Ok(Signature {
237            algorithm: secret_key.algorithm,
238            bytes: sig_bytes,
239        })
240    }
241}
242
243/// Verify a signature using the specified public key
244///
245/// # Arguments
246///
247/// * `public_key` - The public key to verify with
248/// * `message` - The message that was signed
249/// * `signature` - The signature to verify
250///
251/// # Returns
252///
253/// Ok(()) if the signature is valid, an error otherwise
254pub fn verify(
255    public_key: &PublicKey,
256    message: &[u8],
257    signature: &Signature,
258) -> Result<(), PqcError> {
259    if public_key.algorithm != signature.algorithm {
260        return Err(PqcError::BadArgument);
261    }
262
263    unsafe {
264        let result = bitcoin_pqc_verify(
265            public_key.algorithm.into(),
266            public_key.bytes.as_ptr(),
267            public_key.bytes.len(),
268            message.as_ptr(),
269            message.len(),
270            signature.bytes.as_ptr(),
271            signature.bytes.len(),
272        );
273
274        result.into()
275    }
276}
277
278/// Get the public key size for an algorithm
279///
280/// # Arguments
281///
282/// * `algorithm` - The algorithm to get the size for
283///
284/// # Returns
285///
286/// The size in bytes
287pub fn public_key_size(algorithm: Algorithm) -> usize {
288    unsafe { bitcoin_pqc_public_key_size(algorithm.into()) }
289}
290
291/// Get the secret key size for an algorithm
292///
293/// # Arguments
294///
295/// * `algorithm` - The algorithm to get the size for
296///
297/// # Returns
298///
299/// The size in bytes
300pub fn secret_key_size(algorithm: Algorithm) -> usize {
301    unsafe { bitcoin_pqc_secret_key_size(algorithm.into()) }
302}
303
304/// Get the signature size for an algorithm
305///
306/// # Arguments
307///
308/// * `algorithm` - The algorithm to get the size for
309///
310/// # Returns
311///
312/// The size in bytes
313pub fn signature_size(algorithm: Algorithm) -> usize {
314    unsafe { bitcoin_pqc_signature_size(algorithm.into()) }
315}