use k256::ecdsa::{Signature, SigningKey, VerifyingKey};
use rand::rngs::OsRng;
use sha3::{Digest, Sha3_256};
pub fn gen_prvkey() -> String {
let signing_key = SigningKey::random(&mut OsRng);
hex::encode(signing_key.to_bytes())
}
pub fn gen_id(private_key: &str) -> String {
let private_key_bytes = hex::decode(private_key).expect("Invalid hex private key");
let signing_key = SigningKey::from_slice(&private_key_bytes).expect("Invalid private key");
let verifying_key = signing_key.verifying_key();
let public_key_point = verifying_key.to_encoded_point(false);
let public_key_bytes = public_key_point.as_bytes();
let public_key_hex = hex::encode(public_key_bytes);
let mut hasher = Sha3_256::new();
hasher.update(public_key_hex.as_bytes());
let hash = hasher.finalize();
hex::encode(hash)
}
pub fn gen_signature(message: &str, private_key: &str) -> String {
let private_key_bytes = hex::decode(private_key).expect("Invalid hex private key");
let signing_key = SigningKey::from_slice(&private_key_bytes).expect("Invalid private key");
let mut hasher = Sha3_256::new();
hasher.update(message.as_bytes());
let msg_hash = hasher.finalize();
let (signature, recovery_id) = signing_key
.sign_prehash_recoverable(&msg_hash)
.expect("Signing failed");
let r = signature.r().to_bytes();
let s = signature.s().to_bytes();
let v = recovery_id.to_byte();
let mut sig_bytes = [0u8; 65];
sig_bytes[..32].copy_from_slice(&r);
sig_bytes[32..64].copy_from_slice(&s);
sig_bytes[64] = v;
hex::encode(sig_bytes)
}
pub fn gen_hash(message: &str) -> String {
let mut hasher = Sha3_256::new();
hasher.update(message.as_bytes());
let hash = hasher.finalize();
hex::encode(hash)
}
pub fn recid(message: &str, signature: &str) -> String {
use k256::ecdsa::RecoveryId;
let sig_bytes = hex::decode(signature).expect("Invalid hex signature");
if sig_bytes.len() != 65 {
panic!("Invalid signature length");
}
let r = &sig_bytes[..32];
let s = &sig_bytes[32..64];
let v = sig_bytes[64];
let mut sig_bytes_rs = [0u8; 64];
sig_bytes_rs[..32].copy_from_slice(r);
sig_bytes_rs[32..].copy_from_slice(s);
let signature = Signature::from_slice(&sig_bytes_rs).expect("Invalid signature");
let recovery_id = RecoveryId::from_byte(v).expect("Invalid recovery id");
let mut hasher = Sha3_256::new();
hasher.update(message.as_bytes());
let msg_hash = hasher.finalize();
let verifying_key =
VerifyingKey::recover_from_prehash(&msg_hash, &signature, recovery_id).expect("Recovery failed");
let public_key_point = verifying_key.to_encoded_point(false);
let public_key_bytes = public_key_point.as_bytes();
let public_key_hex = hex::encode(public_key_bytes);
let mut hasher = Sha3_256::new();
hasher.update(public_key_hex.as_bytes());
let hash = hasher.finalize();
hex::encode(hash)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gen_prvkey() {
let p = gen_prvkey();
assert_eq!(64, p.len());
let p2 = gen_prvkey();
assert_ne!(p, p2);
}
#[test]
fn test_gen_id() {
let p = gen_prvkey();
let id = gen_id(&p);
assert_eq!(64, id.len());
}
#[test]
fn test_gen_id_deterministic() {
let prvkey = "ddf7f7791208083b6a9ed975a72684f6406a269cfa36f1b1c32045c0a71fff05";
let id1 = gen_id(prvkey);
let id2 = gen_id(prvkey);
assert_eq!(id1, id2);
}
#[test]
fn test_gen_signature() {
let p = gen_prvkey();
let msg = "test";
let s = gen_signature(msg, &p);
assert_eq!(130, s.len());
}
#[test]
fn test_gen_hash() {
let msg = "test";
let h = gen_hash(msg);
assert_eq!(64, h.len());
}
#[test]
fn test_gen_hash_deterministic() {
let msg = "hello world";
let h1 = gen_hash(msg);
let h2 = gen_hash(msg);
assert_eq!(h1, h2);
}
#[test]
fn test_recid() {
let p = gen_prvkey();
let id = gen_id(&p);
let msg = "hello";
let s = gen_signature(msg, &p);
let rid = recid(msg, &s);
assert_eq!(id, rid);
}
#[test]
fn test_known_key() {
let prvkey = "ddf7f7791208083b6a9ed975a72684f6406a269cfa36f1b1c32045c0a71fff05";
let expected_id = "3fc05cf3df4b494e95d6a3d297a34f19938f7daa7422ab0d4f794454133341ac";
let id = gen_id(prvkey);
assert_eq!(expected_id, id);
}
#[test]
fn test_colony_key() {
let colony_key = "ba949fa134981372d6da62b6a56f336ab4d843b22c02a4257dcf7d0d73097514";
let expected_id = "4787a5071856a4acf702b2ffcea422e3237a679c681314113d86139461290cf4";
let id = gen_id(colony_key);
println!("Colony ID: {}", id);
assert_eq!(expected_id, id);
}
#[test]
fn test_colony_key_signature() {
let colony_key = "ba949fa134981372d6da62b6a56f336ab4d843b22c02a4257dcf7d0d73097514";
let expected_id = "4787a5071856a4acf702b2ffcea422e3237a679c681314113d86139461290cf4";
let msg = r#"{"colonyname":"dev","executorname":"wasm-executor","msgtype":"approveexecutormsg"}"#;
let sig = gen_signature(msg, colony_key);
let recovered = recid(msg, &sig);
println!("Expected: {}", expected_id);
println!("Recovered: {}", recovered);
assert_eq!(expected_id, recovered, "Signature recovery should match colony owner ID");
}
}