radix_common/crypto/ed25519/
private_key.rs1use super::Ed25519Signature;
2use crate::internal_prelude::*;
3use core::pin::Pin;
4use ed25519_dalek::{Signer, SigningKey};
5use zeroize::Zeroize;
6
7pub struct Ed25519PrivateKey(Pin<Box<Option<SigningKey>>>);
13
14impl Zeroize for Ed25519PrivateKey {
19 fn zeroize(&mut self) {
20 *self.0 = None;
21 }
22}
23
24impl Ed25519PrivateKey {
25 pub const LENGTH: usize = 32;
26
27 fn signing_key(&self) -> &SigningKey {
28 let option = &*self.0;
29 option
30 .as_ref()
31 .expect("Cannot access signing key after zeroizing")
32 }
33
34 pub fn public_key(&self) -> Ed25519PublicKey {
35 Ed25519PublicKey(self.signing_key().verifying_key().to_bytes())
36 }
37
38 pub fn sign(&self, msg: impl AsRef<[u8]>) -> Ed25519Signature {
39 Ed25519Signature(self.signing_key().sign(msg.as_ref()).to_bytes())
42 }
43
44 pub fn to_bytes(&self) -> Vec<u8> {
45 self.signing_key().to_bytes().to_vec()
46 }
47
48 #[allow(clippy::result_unit_err)]
49 pub fn from_bytes(slice: &[u8]) -> Result<Self, ()> {
50 if slice.len() != Ed25519PrivateKey::LENGTH {
51 return Err(());
52 }
53
54 let signing_key = SigningKey::try_from(slice).map_err(|_| ())?;
55
56 Ok(Self(Box::pin(Some(signing_key))))
57 }
58
59 #[allow(clippy::result_unit_err)]
60 pub fn from_u64(n: u64) -> Result<Self, ()> {
61 let mut bytes = [0u8; Ed25519PrivateKey::LENGTH];
62 bytes[Ed25519PrivateKey::LENGTH - 8..Ed25519PrivateKey::LENGTH]
63 .copy_from_slice(&n.to_be_bytes());
64
65 Ok(Self(Box::pin(Some(SigningKey::from_bytes(&bytes)))))
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72 use sbor::rust::str::FromStr;
73
74 #[test]
75 fn sign_and_verify() {
76 let test_sk = "0000000000000000000000000000000000000000000000000000000000000001";
77 let test_pk = "4cb5abf6ad79fbf5abbccafcc269d85cd2651ed4b885b5869f241aedf0a5ba29";
78 let test_message_hash = hash("Test");
79 let test_signature = "cf0ca64435609b85ab170da339d415bbac87d678dfd505969be20adc6b5971f4ee4b4620c602bcbc34fd347596546675099d696265f4a42a16df343da1af980e";
80 let sk = Ed25519PrivateKey::from_bytes(&hex::decode(test_sk).unwrap()).unwrap();
81 let pk = Ed25519PublicKey::from_str(test_pk).unwrap();
82 let sig = Ed25519Signature::from_str(test_signature).unwrap();
83
84 assert_eq!(sk.public_key(), pk);
85 assert_eq!(sk.sign(test_message_hash), sig);
86 assert!(verify_ed25519(test_message_hash, &pk, &sig));
87 }
88
89 fn find_slice_in_memory(ptr: *const u8, slice: &[u8]) -> Option<usize> {
90 let size = mem::size_of::<Option<SigningKey>>();
95
96 unsafe {
97 for i in 0..size - slice.len() + 1 {
98 let memory_slice: &[u8] = core::slice::from_raw_parts(ptr.add(i), slice.len());
99 if memory_slice == slice {
100 return Some(i);
101 }
102 }
103 }
104 None
105 }
106
107 #[test]
108 fn verify_zeroize() {
109 let bytes = "4fd3fb62d6b7a4749f75d56d06b0aea1ec2c2a6986d2bfa975d7891585590fea";
110 let key_bytes = hex::decode(bytes).unwrap();
111 let mut secret_key = Ed25519PrivateKey::from_bytes(&key_bytes).unwrap();
112
113 let secret_key_inner_ptr = &*secret_key.0 as *const Option<SigningKey> as *const u8;
114
115 let key_offset =
116 find_slice_in_memory(secret_key_inner_ptr, &key_bytes).expect("Key bytes not found");
117
118 secret_key.zeroize();
119
120 let zero_bytes = [0u8; 32];
121
122 let memory_slice_after_zeroize = unsafe {
123 core::slice::from_raw_parts(secret_key_inner_ptr.add(key_offset), zero_bytes.len())
124 };
125
126 assert_eq!(memory_slice_after_zeroize, zero_bytes,);
127 }
128
129 #[test]
130 fn verify_zeroize_on_drop() {
131 let bytes = "4fd3fb62d6b7a4749f75d56d06b0aea1ec2c2a6986d2bfa975d7891585590fea";
132 let key_bytes = hex::decode(bytes).unwrap();
133 let secret_key = Ed25519PrivateKey::from_bytes(&key_bytes).unwrap();
134
135 let secret_key_inner_ptr = &*secret_key.0 as *const Option<SigningKey> as *const u8;
136
137 let key_offset =
138 find_slice_in_memory(secret_key_inner_ptr, &key_bytes).expect("Key bytes not found");
139
140 drop(secret_key);
141
142 let zero_bytes = [0u8; 32];
143 let memory_slice_after_zeroize = unsafe {
144 core::slice::from_raw_parts(secret_key_inner_ptr.add(key_offset), zero_bytes.len())
145 };
146
147 assert_eq!(memory_slice_after_zeroize, zero_bytes,);
148 }
149}