libsignal_rust/
curve.rs

1use x25519_dalek::{StaticSecret, PublicKey};
2use ed25519_dalek::{SigningKey, VerifyingKey, Signature, Signer, Verifier};
3use rand::rngs::OsRng;
4
5#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
6pub struct KeyPair {
7    pub priv_key: Vec<u8>,
8    pub pub_key: Vec<u8>,
9}
10
11#[allow(dead_code)]
12const PUBLIC_KEY_DER_PREFIX: &[u8] = &[
13    48, 42, 48, 5, 6, 3, 43, 101, 110, 3, 33, 0
14];
15
16#[allow(dead_code)]
17const PRIVATE_KEY_DER_PREFIX: &[u8] = &[
18    48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 110, 4, 34, 4, 32
19];
20
21fn validate_priv_key(priv_key: &[u8]) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
22    if priv_key.len() != 32 {
23        return Err(format!("Incorrect private key length: {}", priv_key.len()).into());
24    }
25    Ok(())
26}
27
28fn scrub_pub_key_format(pub_key: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
29    match pub_key.len() {
30        33 => {
31            // Standard format with version byte 
32            // should be 0x05 for curve25519
33            if pub_key[0] == 0x05 {
34                Ok(pub_key[1..].to_vec())
35            } else {
36                Err(format!("Invalid public key version byte: expected 0x05, got 0x{:02x}", pub_key[0]).into())
37            }
38        }
39        32 => {
40            // Raw 32-byte key format 
41            // this is valid but non-standard
42            // This is acceptable but indicates the key was generated without a version prefix
43            Ok(pub_key.to_vec())
44        }
45        len => {
46            Err(format!("Invalid public key length: expected 32 or 33 bytes, got {} bytes", len).into())
47        }
48    }
49}
50
51/// Generate a new X25519 key pair for ECDH/key agreement
52pub fn generate_key_pair() -> KeyPair {
53    let private = StaticSecret::random_from_rng(OsRng);
54    let public = PublicKey::from(&private);
55
56    let mut pub_key = vec![5u8];
57    pub_key.extend_from_slice(public.as_bytes());
58    
59    KeyPair {
60        priv_key: private.to_bytes().to_vec(),
61        pub_key,
62    }
63}
64
65/// Generate a new Ed25519 key pair for signing/verification
66pub fn generate_signing_key_pair() -> KeyPair {
67    let signing_key = SigningKey::generate(&mut OsRng);
68    let verifying_key = signing_key.verifying_key();
69    
70    let mut pub_key = vec![5u8];
71    pub_key.extend_from_slice(verifying_key.as_bytes());
72    
73    KeyPair {
74        priv_key: signing_key.to_bytes().to_vec(),
75        pub_key,
76    }
77}
78
79/// Calculate X25519 key agreement (ECDH)
80pub fn calculate_agreement(pub_key: &[u8], priv_key: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
81    let pub_key_scrubbed = scrub_pub_key_format(pub_key)?;
82    validate_priv_key(priv_key)?;
83    
84    if pub_key_scrubbed.len() != 32 {
85        return Err("Invalid public key".into());
86    }
87    
88    let private_key_array: [u8; 32] = priv_key.try_into()
89        .map_err(|_| "Private key must be exactly 32 bytes")?;
90    let public_key_array: [u8; 32] = pub_key_scrubbed.try_into()
91        .map_err(|_| "Public key must be exactly 32 bytes")?;
92    
93    let secret = StaticSecret::from(private_key_array);
94    let public = PublicKey::from(public_key_array);
95    
96    let shared_secret = secret.diffie_hellman(&public);
97    Ok(shared_secret.to_bytes().to_vec())
98}
99
100/// Calculate Ed25519 signature
101pub fn calculate_signature(priv_key: &[u8], message: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
102    validate_priv_key(priv_key)?;
103    
104    if message.is_empty() {
105        return Err("Invalid message".into());
106    }
107    
108    let private_key_array: [u8; 32] = priv_key.try_into()
109        .map_err(|_| "Private key must be exactly 32 bytes")?;
110    
111    let signing_key = SigningKey::from_bytes(&private_key_array);
112    let signature: Signature = signing_key.sign(message);
113    
114    Ok(signature.to_bytes().to_vec())
115}
116
117/// Verify Ed25519 signature
118pub fn verify_signature(pub_key: &[u8], message: &[u8], signature: &[u8]) -> Result<bool, Box<dyn std::error::Error + Send + Sync>> {
119    let pub_key_scrubbed = scrub_pub_key_format(pub_key)?;
120    
121    if pub_key_scrubbed.len() != 32 {
122        return Err("Invalid public key".into());
123    }
124    
125    if message.is_empty() {
126        return Err("Invalid message".into());
127    }
128    
129    if signature.len() != 64 {
130        return Err("Invalid signature".into());
131    }
132    
133    let public_key_array: [u8; 32] = pub_key_scrubbed.try_into()
134        .map_err(|_| "Public key must be exactly 32 bytes")?;
135    let signature_array: [u8; 64] = signature.try_into()
136        .map_err(|_| "Signature must be exactly 64 bytes")?;
137    
138    let verifying_key = VerifyingKey::from_bytes(&public_key_array)
139        .map_err(|e| format!("Invalid public key: {}", e))?;
140    let sig = Signature::from_bytes(&signature_array);
141    
142    match verifying_key.verify(message, &sig) {
143        Ok(()) => Ok(true),
144        Err(_) => Ok(false),
145    }
146}