flux_verify_api/
signing.rs1use ed25519_dalek::{SigningKey, VerifyingKey, Signer, Verifier, Signature as DalekSignature};
2use sha2::{Sha256, Digest};
3use serde::{Serialize, Deserialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct Signature {
8 #[serde(with = "hex_bytes_64")]
10 pub sig: [u8; 64],
11 #[serde(with = "hex_bytes_32")]
13 pub fingerprint: [u8; 32],
14 pub timestamp: u32,
16}
17
18mod hex_bytes_64 {
19 use serde::{self, Deserialize, Deserializer, Serializer};
20 pub fn serialize<S: Serializer>(data: &[u8; 64], s: S) -> Result<S::Ok, S::Error> {
21 s.serialize_str(&hex::encode(data))
22 }
23 pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<[u8; 64], D::Error> {
24 let s = String::deserialize(d)?;
25 let bytes = hex::decode(&s).map_err(serde::de::Error::custom)?;
26 let mut arr = [0u8; 64];
27 arr.copy_from_slice(&bytes);
28 Ok(arr)
29 }
30}
31
32mod hex_bytes_32 {
33 use serde::{self, Deserialize, Deserializer, Serializer};
34 pub fn serialize<S: Serializer>(data: &[u8; 32], s: S) -> Result<S::Ok, S::Error> {
35 s.serialize_str(&hex::encode(data))
36 }
37 pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<[u8; 32], D::Error> {
38 let s = String::deserialize(d)?;
39 let bytes = hex::decode(&s).map_err(serde::de::Error::custom)?;
40 let mut arr = [0u8; 32];
41 arr.copy_from_slice(&bytes);
42 Ok(arr)
43 }
44}
45
46#[derive(Debug, thiserror::Error)]
48pub enum SigningError {
49 #[error("invalid private key length: expected 32 bytes, got {0}")]
50 InvalidPrivateKey(usize),
51 #[error("invalid public key: {0}")]
52 InvalidPublicKey(String),
53 #[error("signature verification failed")]
54 VerificationFailed,
55 #[error("fingerprint mismatch — bytecode was tampered with")]
56 FingerprintMismatch,
57}
58
59impl Signature {
60 fn signed_message(fingerprint: &[u8; 32], timestamp: u32) -> [u8; 36] {
62 let mut msg = [0u8; 36];
63 msg[..32].copy_from_slice(fingerprint);
64 msg[32..].copy_from_slice(×tamp.to_le_bytes());
65 msg
66 }
67}
68
69pub fn fingerprint(bytecode: &[u8]) -> [u8; 32] {
71 let mut hasher = Sha256::new();
72 hasher.update(bytecode);
73 hasher.finalize().into()
74}
75
76pub fn sign_bytecode(bytecode: &[u8], private_key: &[u8; 32], timestamp: Option<u32>) -> Signature {
82 let signing_key = SigningKey::from_bytes(private_key);
83 let fp = fingerprint(bytecode);
84 let ts = timestamp.unwrap_or_else(|| {
85 std::time::SystemTime::now()
86 .duration_since(std::time::UNIX_EPOCH)
87 .expect("clock went backwards")
88 .as_secs() as u32
89 });
90 let msg = Signature::signed_message(&fp, ts);
91 let dalek_sig: DalekSignature = signing_key.sign(&msg);
92 let sig_bytes: [u8; 64] = dalek_sig.to_bytes();
93
94 Signature {
95 sig: sig_bytes,
96 fingerprint: fp,
97 timestamp: ts,
98 }
99}
100
101pub fn verify_bytecode(
105 bytecode: &[u8],
106 signature: &Signature,
107 public_key: &[u8; 32],
108) -> Result<(), SigningError> {
109 let fp = fingerprint(bytecode);
111 if fp != signature.fingerprint {
112 return Err(SigningError::FingerprintMismatch);
113 }
114
115 let msg = Signature::signed_message(&signature.fingerprint, signature.timestamp);
117
118 let verifying_key = VerifyingKey::from_bytes(public_key)
120 .map_err(|e| SigningError::InvalidPublicKey(e.to_string()))?;
121 let dalek_sig = DalekSignature::from_bytes(&signature.sig);
122 verifying_key
123 .verify(&msg, &dalek_sig)
124 .map_err(|_| SigningError::VerificationFailed)?;
125
126 Ok(())
127}
128
129#[cfg(test)]
130mod unit {
131 use super::*;
132
133 fn random_keypair() -> ([u8; 32], [u8; 32]) {
134 let signing_key = SigningKey::generate(&mut rand::rngs::OsRng);
135 let public_key = signing_key.verifying_key().to_bytes();
136 let private_key = signing_key.to_bytes();
137 (private_key, public_key)
138 }
139
140 #[test]
141 fn sign_and_verify_roundtrip() {
142 let (sk, pk) = random_keypair();
143 let bytecode = b"LOAD x 42.0; ASSERT_GT x 0;";
144 let sig = sign_bytecode(bytecode, &sk, Some(12345));
145 assert!(verify_bytecode(bytecode, &sig, &pk).is_ok());
146 }
147
148 #[test]
149 fn reject_tampered_bytecode() {
150 let (sk, pk) = random_keypair();
151 let bytecode = b"LOAD x 42.0; ASSERT_GT x 0;";
152 let sig = sign_bytecode(bytecode, &sk, Some(12345));
153 let mut tampered = bytecode.to_vec();
154 tampered[5] ^= 0xFF; assert!(verify_bytecode(&tampered, &sig, &pk).is_err());
156 }
157
158 #[test]
159 fn reject_wrong_public_key() {
160 let (sk, _) = random_keypair();
161 let (_, wrong_pk) = random_keypair();
162 let bytecode = b"LOAD x 42.0;";
163 let sig = sign_bytecode(bytecode, &sk, Some(12345));
164 assert!(verify_bytecode(bytecode, &sig, &wrong_pk).is_err());
165 }
166}