1use std::fmt;
20use std::str::FromStr;
21
22use secp256k1::ecdsa::{RecoverableSignature, RecoveryId};
23use secp256k1::{Message, PublicKey as SecpPublicKey, SECP256K1, SecretKey};
24use sha3::{Digest, Keccak256};
25use subtle::ConstantTimeEq;
26use zeroize::{Zeroize, ZeroizeOnDrop};
27
28use crate::swarm::bytes::{decode_hex, encode_hex};
29use crate::swarm::errors::Error;
30use crate::swarm::typed_bytes::{
31 ETH_ADDRESS_LENGTH, EthAddress, PRIVATE_KEY_LENGTH, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH,
32 Signature,
33};
34
35#[derive(Clone, Zeroize, ZeroizeOnDrop)]
43pub struct PrivateKey([u8; PRIVATE_KEY_LENGTH]);
44
45impl PartialEq for PrivateKey {
46 fn eq(&self, other: &Self) -> bool {
47 self.0.ct_eq(&other.0).into()
48 }
49}
50
51impl Eq for PrivateKey {}
52
53impl PrivateKey {
54 pub const LENGTH: usize = PRIVATE_KEY_LENGTH;
56
57 pub fn new(b: &[u8]) -> Result<Self, Error> {
59 if b.len() != PRIVATE_KEY_LENGTH {
60 return Err(Error::LengthMismatch {
61 kind: "PrivateKey",
62 expected: &[PRIVATE_KEY_LENGTH],
63 got: b.len(),
64 });
65 }
66 let mut a = [0u8; PRIVATE_KEY_LENGTH];
67 a.copy_from_slice(b);
68 Ok(Self(a))
69 }
70
71 pub fn from_hex(s: &str) -> Result<Self, Error> {
73 Self::new(&decode_hex(s)?)
74 }
75
76 pub fn as_bytes(&self) -> &[u8] {
78 &self.0
79 }
80
81 pub fn to_hex(&self) -> String {
83 encode_hex(&self.0)
84 }
85
86 fn secret_key(&self) -> Result<SecretKey, Error> {
87 SecretKey::from_slice(&self.0).map_err(Error::crypto)
88 }
89
90 pub fn public_key(&self) -> Result<PublicKey, Error> {
92 let sk = self.secret_key()?;
93 let pk = SecpPublicKey::from_secret_key(SECP256K1, &sk);
94 let serialized = pk.serialize_uncompressed();
96 let mut a = [0u8; PUBLIC_KEY_LENGTH];
97 a.copy_from_slice(&serialized[1..]);
98 Ok(PublicKey(a))
99 }
100
101 pub fn sign(&self, data: &[u8]) -> Result<Signature, Error> {
104 let digest = eth_signed_message_digest(data);
105 let sk = self.secret_key()?;
106 let msg = Message::from_digest(digest);
107 let sig: RecoverableSignature = SECP256K1.sign_ecdsa_recoverable(&msg, &sk);
109 let (recid, compact) = sig.serialize_compact();
110
111 let mut out = [0u8; SIGNATURE_LENGTH];
112 out[..64].copy_from_slice(&compact);
113 out[64] = (recid as i32) as u8 + 27;
114 Signature::new(&out)
115 }
116}
117
118impl fmt::Debug for PrivateKey {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 f.write_str("PrivateKey(<redacted>)")
122 }
123}
124
125impl FromStr for PrivateKey {
126 type Err = Error;
127 fn from_str(s: &str) -> Result<Self, Self::Err> {
128 Self::from_hex(s)
129 }
130}
131
132#[derive(Clone, Copy, PartialEq, Eq, Hash)]
138pub struct PublicKey([u8; PUBLIC_KEY_LENGTH]);
139
140impl PublicKey {
141 pub const LENGTH: usize = PUBLIC_KEY_LENGTH;
143
144 pub fn new(b: &[u8]) -> Result<Self, Error> {
147 match b.len() {
148 PUBLIC_KEY_LENGTH => {
149 let mut a = [0u8; PUBLIC_KEY_LENGTH];
150 a.copy_from_slice(b);
151 Ok(Self(a))
152 }
153 33 => {
154 let pk = SecpPublicKey::from_slice(b).map_err(Error::crypto)?;
155 let serialized = pk.serialize_uncompressed();
156 let mut a = [0u8; PUBLIC_KEY_LENGTH];
157 a.copy_from_slice(&serialized[1..]);
158 Ok(Self(a))
159 }
160 n => Err(Error::LengthMismatch {
161 kind: "PublicKey",
162 expected: &[33, PUBLIC_KEY_LENGTH],
163 got: n,
164 }),
165 }
166 }
167
168 pub fn from_hex(s: &str) -> Result<Self, Error> {
171 Self::new(&decode_hex(s)?)
172 }
173
174 pub fn as_bytes(&self) -> &[u8] {
176 &self.0
177 }
178
179 pub fn to_hex(&self) -> String {
181 encode_hex(&self.0)
182 }
183
184 pub fn address(&self) -> EthAddress {
186 let mut h = Keccak256::new();
187 h.update(self.0);
188 let out = h.finalize();
189 let mut a = [0u8; ETH_ADDRESS_LENGTH];
190 a.copy_from_slice(&out[12..]);
191 EthAddress::new(&a).expect("hash slice has fixed length")
192 }
193
194 pub fn compressed_bytes(&self) -> Result<[u8; 33], Error> {
196 let mut full = [0u8; 65];
197 full[0] = 0x04;
198 full[1..].copy_from_slice(&self.0);
199 let pk = SecpPublicKey::from_slice(&full).map_err(Error::crypto)?;
200 Ok(pk.serialize())
201 }
202
203 pub fn compressed_hex(&self) -> Result<String, Error> {
205 Ok(encode_hex(&self.compressed_bytes()?))
206 }
207}
208
209impl fmt::Debug for PublicKey {
210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211 write!(f, "PublicKey({})", self.to_hex())
212 }
213}
214
215impl fmt::Display for PublicKey {
216 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217 f.write_str(&self.to_hex())
218 }
219}
220
221impl FromStr for PublicKey {
222 type Err = Error;
223 fn from_str(s: &str) -> Result<Self, Self::Err> {
224 Self::from_hex(s)
225 }
226}
227
228impl Signature {
231 pub fn recover_public_key(&self, data: &[u8]) -> Result<PublicKey, Error> {
234 let digest = eth_signed_message_digest(data);
235 let bytes = self.as_bytes();
236 let v = bytes[64];
237 let recovery_byte = if v >= 27 { v - 27 } else { v };
238 let recid = RecoveryId::try_from(recovery_byte as i32)
239 .map_err(|_| Error::crypto("invalid V byte"))?;
240 let recsig =
241 RecoverableSignature::from_compact(&bytes[..64], recid).map_err(Error::crypto)?;
242 let msg = Message::from_digest(digest);
243 let pk = SECP256K1
244 .recover_ecdsa(&msg, &recsig)
245 .map_err(Error::crypto)?;
246 let serialized = pk.serialize_uncompressed();
247 let mut a = [0u8; PUBLIC_KEY_LENGTH];
248 a.copy_from_slice(&serialized[1..]);
249 Ok(PublicKey(a))
250 }
251
252 pub fn is_valid(&self, data: &[u8], expected: EthAddress) -> bool {
255 match self.recover_public_key(data) {
256 Ok(pk) => pk.address() == expected,
257 Err(_) => false,
258 }
259 }
260}
261
262pub fn eth_signed_message_digest(data: &[u8]) -> [u8; 32] {
267 let mut h = Keccak256::new();
268 h.update(data);
269 let inner = h.finalize();
270
271 let mut h = Keccak256::new();
272 h.update(b"\x19Ethereum Signed Message:\n32");
273 h.update(inner);
274 let out = h.finalize();
275 let mut a = [0u8; 32];
276 a.copy_from_slice(&out);
277 a
278}
279
280#[cfg(test)]
281mod tests {
282 use super::*;
283
284 fn priv_repeat(byte: u8) -> PrivateKey {
285 PrivateKey::new(&[byte; PRIVATE_KEY_LENGTH]).unwrap()
286 }
287
288 #[test]
289 fn private_to_public_to_address_is_consistent() {
290 let pk = priv_repeat(0x11);
291 let pub_a = pk.public_key().unwrap();
292 let pub_b = pk.public_key().unwrap();
293 assert_eq!(pub_a.as_bytes(), pub_b.as_bytes());
294 assert_eq!(pub_a.address(), pub_b.address());
295 }
296
297 #[test]
298 fn public_key_compressed_round_trip() {
299 let pk = priv_repeat(0x22);
300 let pub_a = pk.public_key().unwrap();
301 let compressed = pub_a.compressed_bytes().unwrap();
302 assert_eq!(compressed.len(), 33);
303 let pub_b = PublicKey::new(&compressed).unwrap();
304 assert_eq!(pub_a.as_bytes(), pub_b.as_bytes());
305 }
306
307 #[test]
308 fn sign_recover_round_trip() {
309 let pk = priv_repeat(0x33);
310 let data = b"hello swarm";
311 let sig = pk.sign(data).unwrap();
312 let v = sig.as_bytes()[64];
314 assert!(v == 27 || v == 28, "V was {v}");
315 let recovered = sig.recover_public_key(data).unwrap();
316 assert_eq!(recovered.as_bytes(), pk.public_key().unwrap().as_bytes());
317 assert!(sig.is_valid(data, pk.public_key().unwrap().address()));
318 assert!(!sig.is_valid(b"tampered", pk.public_key().unwrap().address()));
319 }
320
321 #[test]
322 fn debug_does_not_leak_private_bytes() {
323 let pk = priv_repeat(0x44);
324 let s = format!("{pk:?}");
325 assert!(!s.contains("44"));
326 assert!(s.contains("redacted"));
327 }
328
329 #[test]
330 fn zeroize_clears_private_bytes() {
331 let mut pk = priv_repeat(0x55);
332 assert_eq!(pk.as_bytes(), &[0x55; PRIVATE_KEY_LENGTH]);
333 pk.zeroize();
334 assert_eq!(pk.as_bytes(), &[0u8; PRIVATE_KEY_LENGTH]);
335 }
336
337 #[test]
338 fn private_key_is_zeroize_on_drop() {
339 fn assert_zod<T: zeroize::ZeroizeOnDrop>() {}
343 assert_zod::<PrivateKey>();
344 }
345
346 #[test]
347 fn private_key_eq_is_correct() {
348 let a = priv_repeat(0x66);
349 let b = priv_repeat(0x66);
350 let c = priv_repeat(0x77);
351 assert_eq!(a, b);
352 assert_ne!(a, c);
353 }
354}