1use std::fmt;
17use std::str::FromStr;
18
19use k256::ecdsa::{
20 RecoveryId, Signature as K256Signature, SigningKey, VerifyingKey,
21 signature::hazmat::PrehashSigner,
22};
23use sha3::{Digest, Keccak256};
24
25use crate::swarm::bytes::{decode_hex, encode_hex};
26use crate::swarm::errors::Error;
27use crate::swarm::typed_bytes::{
28 ETH_ADDRESS_LENGTH, EthAddress, PRIVATE_KEY_LENGTH, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH,
29 Signature,
30};
31
32#[derive(Clone, PartialEq, Eq)]
38pub struct PrivateKey([u8; PRIVATE_KEY_LENGTH]);
39
40impl PrivateKey {
41 pub const LENGTH: usize = PRIVATE_KEY_LENGTH;
43
44 pub fn new(b: &[u8]) -> Result<Self, Error> {
46 if b.len() != PRIVATE_KEY_LENGTH {
47 return Err(Error::LengthMismatch {
48 kind: "PrivateKey",
49 expected: &[PRIVATE_KEY_LENGTH],
50 got: b.len(),
51 });
52 }
53 let mut a = [0u8; PRIVATE_KEY_LENGTH];
54 a.copy_from_slice(b);
55 Ok(Self(a))
56 }
57
58 pub fn from_hex(s: &str) -> Result<Self, Error> {
60 Self::new(&decode_hex(s)?)
61 }
62
63 pub fn as_bytes(&self) -> &[u8] {
65 &self.0
66 }
67
68 pub fn to_hex(&self) -> String {
70 encode_hex(&self.0)
71 }
72
73 fn signing_key(&self) -> Result<SigningKey, Error> {
74 SigningKey::from_slice(&self.0).map_err(Error::crypto)
75 }
76
77 pub fn public_key(&self) -> Result<PublicKey, Error> {
79 let sk = self.signing_key()?;
80 let vk = VerifyingKey::from(&sk);
81 let point = vk.to_encoded_point(false);
82 let bytes = point.as_bytes();
84 debug_assert_eq!(bytes.len(), 65);
85 let mut a = [0u8; PUBLIC_KEY_LENGTH];
86 a.copy_from_slice(&bytes[1..]);
87 Ok(PublicKey(a))
88 }
89
90 pub fn sign(&self, data: &[u8]) -> Result<Signature, Error> {
93 let digest = eth_signed_message_digest(data);
94 let sk = self.signing_key()?;
95 let (sig, recovery_id): (K256Signature, RecoveryId) =
96 sk.sign_prehash(&digest).map_err(Error::crypto)?;
97 let normalized = sig.normalize_s().unwrap_or(sig);
98 let recovery_id = if normalized != sig {
101 RecoveryId::from_byte(recovery_id.to_byte() ^ 1).expect("flipped recovery id valid")
102 } else {
103 recovery_id
104 };
105
106 let mut out = [0u8; SIGNATURE_LENGTH];
107 let bytes = normalized.to_bytes();
108 out[..64].copy_from_slice(&bytes);
109 out[64] = recovery_id.to_byte() + 27;
110 Signature::new(&out)
111 }
112}
113
114impl fmt::Debug for PrivateKey {
115 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116 f.write_str("PrivateKey(<redacted>)")
118 }
119}
120
121impl FromStr for PrivateKey {
122 type Err = Error;
123 fn from_str(s: &str) -> Result<Self, Self::Err> {
124 Self::from_hex(s)
125 }
126}
127
128#[derive(Clone, Copy, PartialEq, Eq, Hash)]
134pub struct PublicKey([u8; PUBLIC_KEY_LENGTH]);
135
136impl PublicKey {
137 pub const LENGTH: usize = PUBLIC_KEY_LENGTH;
139
140 pub fn new(b: &[u8]) -> Result<Self, Error> {
143 match b.len() {
144 PUBLIC_KEY_LENGTH => {
145 let mut a = [0u8; PUBLIC_KEY_LENGTH];
146 a.copy_from_slice(b);
147 Ok(Self(a))
148 }
149 33 => {
150 let vk = VerifyingKey::from_sec1_bytes(b).map_err(Error::crypto)?;
151 let point = vk.to_encoded_point(false);
152 let bytes = point.as_bytes();
153 let mut a = [0u8; PUBLIC_KEY_LENGTH];
154 a.copy_from_slice(&bytes[1..]);
155 Ok(Self(a))
156 }
157 n => Err(Error::LengthMismatch {
158 kind: "PublicKey",
159 expected: &[33, PUBLIC_KEY_LENGTH],
160 got: n,
161 }),
162 }
163 }
164
165 pub fn from_hex(s: &str) -> Result<Self, Error> {
168 Self::new(&decode_hex(s)?)
169 }
170
171 pub fn as_bytes(&self) -> &[u8] {
173 &self.0
174 }
175
176 pub fn to_hex(&self) -> String {
178 encode_hex(&self.0)
179 }
180
181 pub fn address(&self) -> EthAddress {
183 let mut h = Keccak256::new();
184 h.update(self.0);
185 let out = h.finalize();
186 let mut a = [0u8; ETH_ADDRESS_LENGTH];
187 a.copy_from_slice(&out[12..]);
188 EthAddress::new(&a).expect("hash slice has fixed length")
189 }
190
191 pub fn compressed_bytes(&self) -> Result<[u8; 33], Error> {
193 let mut full = [0u8; 65];
194 full[0] = 0x04;
195 full[1..].copy_from_slice(&self.0);
196 let vk = VerifyingKey::from_sec1_bytes(&full).map_err(Error::crypto)?;
197 let point = vk.to_encoded_point(true);
198 let bytes = point.as_bytes();
199 if bytes.len() != 33 {
200 return Err(Error::crypto("compressed point not 33 bytes"));
201 }
202 let mut a = [0u8; 33];
203 a.copy_from_slice(bytes);
204 Ok(a)
205 }
206
207 pub fn compressed_hex(&self) -> Result<String, Error> {
209 Ok(encode_hex(&self.compressed_bytes()?))
210 }
211}
212
213impl fmt::Debug for PublicKey {
214 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215 write!(f, "PublicKey({})", self.to_hex())
216 }
217}
218
219impl fmt::Display for PublicKey {
220 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221 f.write_str(&self.to_hex())
222 }
223}
224
225impl FromStr for PublicKey {
226 type Err = Error;
227 fn from_str(s: &str) -> Result<Self, Self::Err> {
228 Self::from_hex(s)
229 }
230}
231
232impl Signature {
235 pub fn recover_public_key(&self, data: &[u8]) -> Result<PublicKey, Error> {
238 let digest = eth_signed_message_digest(data);
239 let bytes = self.as_bytes();
240 let v = bytes[64];
241 let recovery_byte = if v >= 27 { v - 27 } else { v };
242 let recovery_id =
243 RecoveryId::from_byte(recovery_byte).ok_or_else(|| Error::crypto("invalid V byte"))?;
244 let sig = K256Signature::from_slice(&bytes[..64]).map_err(Error::crypto)?;
245 let vk = VerifyingKey::recover_from_prehash(&digest, &sig, recovery_id)
246 .map_err(Error::crypto)?;
247 let point = vk.to_encoded_point(false);
248 let raw = point.as_bytes();
249 let mut a = [0u8; PUBLIC_KEY_LENGTH];
250 a.copy_from_slice(&raw[1..]);
251 Ok(PublicKey(a))
252 }
253
254 pub fn is_valid(&self, data: &[u8], expected: EthAddress) -> bool {
257 match self.recover_public_key(data) {
258 Ok(pk) => pk.address() == expected,
259 Err(_) => false,
260 }
261 }
262}
263
264pub fn eth_signed_message_digest(data: &[u8]) -> [u8; 32] {
269 let mut h = Keccak256::new();
270 h.update(data);
271 let inner = h.finalize();
272
273 let mut h = Keccak256::new();
274 h.update(b"\x19Ethereum Signed Message:\n32");
275 h.update(inner);
276 let out = h.finalize();
277 let mut a = [0u8; 32];
278 a.copy_from_slice(&out);
279 a
280}
281
282#[cfg(test)]
283mod tests {
284 use super::*;
285
286 fn priv_repeat(byte: u8) -> PrivateKey {
287 PrivateKey::new(&[byte; PRIVATE_KEY_LENGTH]).unwrap()
288 }
289
290 #[test]
291 fn private_to_public_to_address_is_consistent() {
292 let pk = priv_repeat(0x11);
293 let pub_a = pk.public_key().unwrap();
294 let pub_b = pk.public_key().unwrap();
295 assert_eq!(pub_a.as_bytes(), pub_b.as_bytes());
296 assert_eq!(pub_a.address(), pub_b.address());
297 }
298
299 #[test]
300 fn public_key_compressed_round_trip() {
301 let pk = priv_repeat(0x22);
302 let pub_a = pk.public_key().unwrap();
303 let compressed = pub_a.compressed_bytes().unwrap();
304 assert_eq!(compressed.len(), 33);
305 let pub_b = PublicKey::new(&compressed).unwrap();
306 assert_eq!(pub_a.as_bytes(), pub_b.as_bytes());
307 }
308
309 #[test]
310 fn sign_recover_round_trip() {
311 let pk = priv_repeat(0x33);
312 let data = b"hello swarm";
313 let sig = pk.sign(data).unwrap();
314 let v = sig.as_bytes()[64];
316 assert!(v == 27 || v == 28, "V was {v}");
317 let recovered = sig.recover_public_key(data).unwrap();
318 assert_eq!(recovered.as_bytes(), pk.public_key().unwrap().as_bytes());
319 assert!(sig.is_valid(data, pk.public_key().unwrap().address()));
320 assert!(!sig.is_valid(b"tampered", pk.public_key().unwrap().address()));
321 }
322
323 #[test]
324 fn debug_does_not_leak_private_bytes() {
325 let pk = priv_repeat(0x44);
326 let s = format!("{pk:?}");
327 assert!(!s.contains("44"));
328 assert!(s.contains("redacted"));
329 }
330}