stellar_baselib/
signing.rs

1//! This module provides the signing functionality used by the stellar network
2
3/// Sign the message with the given secrey key
4pub fn sign(data: &[u8], secret_key: &[u8]) -> [u8; 64] {
5    signing_impl::sign(data, secret_key)
6}
7/// Verify the signature
8pub fn verify(data: &[u8], signature: &[u8], public_key: &[u8]) -> bool {
9    signing_impl::verify(data, signature, public_key)
10}
11
12/// Generate Keypair
13pub fn generate(secret_key: &[u8]) -> [u8; 32] {
14    signing_impl::generate(secret_key)
15}
16
17#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
18mod signing_impl {
19    pub fn generate(secret_key: &[u8]) -> [u8; 32] {
20        let secret_key_u8: &[u8; 32] = secret_key.try_into().unwrap();
21        let nacl_keys = nacl::sign::generate_keypair(secret_key_u8);
22        nacl_keys.pkey
23    }
24
25    pub fn sign(data: &[u8], secret_key: &[u8]) -> [u8; 64] {
26        let mut data_u8 = data;
27        let mut secret_key_u8 = secret_key;
28        let signed_msg = nacl::sign::signature(&mut data_u8, &mut secret_key_u8).unwrap();
29        let mut signature = [0u8; 64];
30
31        for i in 0..signed_msg.len() {
32            signature[i] = signed_msg[i];
33        }
34        signature
35    }
36
37    pub fn verify(data: &[u8], signature: &[u8], public_key: &[u8]) -> bool {
38        let data_u8 = data;
39        let signature_u8 = signature;
40        let public_key_u8 = public_key;
41        nacl::sign::verify(data_u8, signature_u8, public_key_u8).unwrap()
42    }
43}
44
45#[cfg(not(target_arch = "wasm32"))]
46mod signing_impl {
47    use libsodium_sys::crypto_sign_detached;
48    use libsodium_sys::crypto_sign_seed_keypair;
49
50    macro_rules! raw_ptr_char {
51        ($name: ident) => {
52            $name.as_mut_ptr() as *mut libc::c_uchar
53        };
54    }
55
56    /// make invoking ffi functions more readable
57    macro_rules! raw_ptr_char_immut {
58        ($name: ident) => {
59            $name.as_ptr() as *const libc::c_uchar
60        };
61    }
62
63    pub fn generate(secret_key: &[u8]) -> [u8; 32] {
64        unsafe {
65            libsodium_sys::sodium_init();
66        };
67
68        unsafe {
69            let mut pk = [0u8; libsodium_sys::crypto_sign_PUBLICKEYBYTES as usize];
70            let mut sk = [0u8; libsodium_sys::crypto_sign_SECRETKEYBYTES as usize];
71
72            libsodium_sys::crypto_sign_seed_keypair(
73                raw_ptr_char!(pk),
74                raw_ptr_char!(sk),
75                raw_ptr_char_immut!(secret_key),
76            );
77
78            pk
79        }
80    }
81
82    pub fn sign(data: &[u8], secret_key: &[u8]) -> [u8; 64] {
83        unsafe {
84            unsafe {
85                let mut signature = [0u8; libsodium_sys::crypto_sign_BYTES as usize];
86
87                libsodium_sys::crypto_sign_detached(
88                    raw_ptr_char!(signature),
89                    std::ptr::null_mut(),
90                    raw_ptr_char_immut!(data),
91                    data.len() as libc::c_ulonglong,
92                    secret_key.as_ptr(),
93                );
94
95                signature
96            }
97        }
98    }
99
100    pub fn verify(data: &[u8], signature: &[u8], public_key: &[u8]) -> bool {
101        unsafe {
102            let val = libsodium_sys::crypto_sign_verify_detached(
103                raw_ptr_char_immut!(signature),
104                raw_ptr_char_immut!(data),
105                data.len() as libc::c_ulonglong,
106                raw_ptr_char_immut!(public_key),
107            );
108
109            val == 0
110        }
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117    use hex_literal::hex;
118
119    #[test]
120    fn test_hash_string() {
121        let data = b"hello world";
122        let expected_sig = hex!(
123            "587d4b472eeef7d07aafcd0b049640b0bb3f39784118c2e2b73a04fa2f64c9c538b4b2d0f5335e968a480021fdc23e98c0ddf424cb15d8131df8cb6c4bb58309"
124        );
125        let actual_sig = sign(data, &hex!(
126            "1123740522f11bfef6b3671f51e159ccf589ccf8965262dd5f97d1721d383dd4ffbdd7ef9933fe7249dc5ca1e7120b6d7b7b99a7a367e1a2fc6cb062fe420437"
127        ));
128        assert_eq!(expected_sig, actual_sig);
129    }
130    #[test]
131    fn test_verify_string() {
132        let data = "hello world".as_bytes();
133        let sig = hex!(
134            "587d4b472eeef7d07aafcd0b049640b0bb3f39784118c2e2b73a04fa2f64c9c538b4b2d0f5335e968a480021fdc23e98c0ddf424cb15d8131df8cb6c4bb58309"
135        );
136        let bad_sig = hex!(
137            "687d4b472eeef7d07aafcd0b049640b0bb3f39784118c2e2b73a04fa2f64c9c538b4b2d0f5335e968a480021fdc23e98c0ddf424cb15d8131df8cb6c4bb58309"
138        );
139        let public_key = hex!("ffbdd7ef9933fe7249dc5ca1e7120b6d7b7b99a7a367e1a2fc6cb062fe420437");
140
141        assert!(verify(data, &sig, &public_key));
142        assert!(!verify(b"corrupted", &sig, &public_key));
143        assert!(!verify(data, &bad_sig, &public_key));
144    }
145}