1use ed25519_dalek::{Signer, SigningKey, Verifier, VerifyingKey, Signature as DalekSignature};
6use rand::rngs::OsRng;
7use sha2::{Sha256, Digest};
8use alloc::vec::Vec;
9use alloc::string::{String, ToString};
10use core::convert::TryInto;
11
12use crate::error::{Error, Result};
13use crate::format::Module;
14
15pub const PUBLIC_KEY_SIZE: usize = 32;
17
18pub const SECRET_KEY_SIZE: usize = 32;
20
21pub const SIGNATURE_SIZE: usize = 64;
23
24#[derive(Clone, Copy, Debug, PartialEq, Eq)]
26pub struct PublicKey {
27 inner: VerifyingKey,
28}
29
30impl PublicKey {
31 pub fn from_bytes(bytes: &[u8; PUBLIC_KEY_SIZE]) -> Result<Self> {
33 VerifyingKey::from_bytes(bytes)
34 .map(|inner| Self { inner })
35 .map_err(|e| Error::crypto(format!("invalid public key: {}", e)))
36 }
37
38 pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_SIZE] {
40 self.inner.to_bytes()
41 }
42
43 pub fn fingerprint(&self) -> [u8; 16] {
45 let mut hasher = Sha256::new();
46 hasher.update(self.to_bytes());
47 let result = hasher.finalize();
48 let mut fp = [0u8; 16];
49 fp.copy_from_slice(&result[..16]);
50 fp
51 }
52
53 pub fn fingerprint_hex(&self) -> String {
55 hex::encode(self.fingerprint())
56 }
57
58 pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<()> {
60 self.inner.verify(message, &signature.inner)
61 .map_err(|_| Error::VerificationFailed {
62 details: "signature verification failed".into(),
63 })
64 }
65}
66
67impl AsRef<VerifyingKey> for PublicKey {
68 fn as_ref(&self) -> &VerifyingKey {
69 &self.inner
70 }
71}
72
73#[derive(Clone, Copy, Debug, PartialEq, Eq)]
75pub struct Signature {
76 inner: DalekSignature,
77}
78
79impl Signature {
80 pub fn from_bytes(bytes: &[u8; SIGNATURE_SIZE]) -> Result<Self> {
82 Ok(Self {
83 inner: DalekSignature::from_bytes(bytes),
84 })
85 }
86
87 pub fn to_bytes(&self) -> [u8; SIGNATURE_SIZE] {
89 self.inner.to_bytes()
90 }
91}
92
93#[derive(Debug)]
95pub struct KeyPair {
96 signing: SigningKey,
97}
98
99impl KeyPair {
100 pub fn generate() -> Self {
102 let mut csprng = OsRng;
103 let signing = SigningKey::generate(&mut csprng);
104 Self { signing }
105 }
106
107 pub fn from_seed(seed: &[u8; SECRET_KEY_SIZE]) -> Self {
109 let signing = SigningKey::from_bytes(seed);
110 Self { signing }
111 }
112
113 pub fn to_seed(&self) -> [u8; SECRET_KEY_SIZE] {
115 self.signing.to_bytes()
116 }
117
118 pub fn public(&self) -> PublicKey {
120 PublicKey {
121 inner: self.signing.verifying_key(),
122 }
123 }
124
125 pub fn sign(&self, message: &[u8]) -> Signature {
127 Signature {
128 inner: self.signing.sign(message),
129 }
130 }
131
132 pub fn sign_module(&self, module: &mut Module) {
134 let signature = self.sign(&module.code);
135 module.signature = signature.to_bytes();
136 module.public_key = self.public().to_bytes();
137 module.header.flags |= format::FLAG_SIGNED;
138 }
139
140 pub fn verify_module(module: &Module) -> Result<()> {
142 if module.header.flags & format::FLAG_SIGNED == 0 {
143 return Err(Error::VerificationFailed {
144 details: "module is not signed".into(),
145 });
146 }
147
148 let public_key = PublicKey::from_bytes(&module.public_key)?;
149 let signature = Signature::from_bytes(&module.signature)?;
150
151 public_key.verify(&module.code, &signature)
152 }
153}
154
155impl Clone for KeyPair {
156 fn clone(&self) -> Self {
157 Self::from_seed(&self.to_seed())
158 }
159}
160
161use crate::format;
162
163#[cfg(feature = "std")]
165pub fn parse_key(source: &str) -> Result<KeyPair> {
166 use std::fs;
167 use std::path::Path;
168
169 if Path::new(source).exists() {
171 let contents = fs::read_to_string(source)
172 .map_err(|e| Error::crypto(format!("failed to read key file: {}", e)))?;
173 return parse_key_hex(&contents);
174 }
175
176 parse_key_hex(source)
178}
179
180fn parse_key_hex(hex_str: &str) -> Result<KeyPair> {
181 let cleaned: Vec<u8> = hex_str.chars()
182 .filter(|c| c.is_ascii_hexdigit())
183 .collect::<String>()
184 .as_bytes()
185 .chunks(2)
186 .map(|chunk| {
187 u8::from_str_radix(std::str::from_utf8(chunk).unwrap(), 16).unwrap()
188 })
189 .collect();
190
191 if cleaned.len() != SECRET_KEY_SIZE {
192 return Err(Error::crypto(format!(
193 "invalid key length: expected {} bytes, got {}",
194 SECRET_KEY_SIZE, cleaned.len()
195 )));
196 }
197
198 let mut seed = [0u8; SECRET_KEY_SIZE];
199 seed.copy_from_slice(&cleaned);
200 Ok(KeyPair::from_seed(&seed))
201}