1#![cfg_attr(target_arch = "riscv32", no_std, no_main)] 2 3extern crate alloc; 4 5use alloc::{ 6 string::{String, ToString}, 7 vec::Vec, 8}; 9use base58::ToBase58; 10use common::message::{PlcOperation, Request, Response}; 11use sdk::{ 12 App, 13 curve::{Curve, EcfpPrivateKey, EcfpPublicKey, Secp256k1, ToPublicKey}, 14 hash::Hasher, 15}; 16 17sdk::bootstrap!(); 18 19const HARDENED_PATH: u32 = '🦋' as u32 | (1 << 31); 20 21struct PublicDidKey(Vec<u8>); 22 23impl PublicDidKey { 24 fn new(pubkey: &EcfpPublicKey<Secp256k1, 32>) -> Self { 25 let bytes = pubkey.as_ref().to_bytes(); 26 let mut key = Vec::with_capacity(34); 27 key.push(0xE7); 28 key.push(0x01); 29 key.push(bytes[64] % 2 + 0x02); 30 key.extend_from_slice(&bytes[1..33]); 31 PublicDidKey(key) 32 } 33} 34 35impl ToString for PublicDidKey { 36 fn to_string(&self) -> String { 37 alloc::format!("did:key:z{}", self.0.to_base58()) 38 } 39} 40 41#[cfg(not(test))] 42fn display_key(pubkey: &PublicDidKey, index: u32) -> bool { 43 use sdk::ux::TagValue; 44 45 sdk::ux::review_pairs( 46 "Verify Atproto DID key", 47 "", 48 &[ 49 TagValue { 50 tag: "Key index".into(), 51 value: index.to_string(), 52 }, 53 TagValue { 54 tag: "Path".into(), 55 value: alloc::format!("m/{}'/{}", '🦋' as u32, index), 56 }, 57 ], 58 &pubkey.to_string(), 59 "Confirm", 60 false, 61 ) 62} 63 64#[cfg(test)] 65fn display_key(pubkey: &PublicDidKey, index: u32) -> bool { 66 true 67} 68 69pub fn get_did_key(index: u32, display: bool) -> Result<Response, &'static str> { 70 if index > 256 { 71 return Err("Index is too long"); 72 } 73 74 let hd_node = sdk::curve::Secp256k1::derive_hd_node(&[HARDENED_PATH, index])?; 75 let privkey: EcfpPrivateKey<Secp256k1, 32> = EcfpPrivateKey::new(*hd_node.privkey); 76 let pubkey = privkey.to_public_key(); 77 let key = PublicDidKey::new(&pubkey); 78 79 if display && !display_key(&key, index) { 80 return Err("Rejected by the user"); 81 } 82 83 Ok(Response::DidKey(key.0)) 84} 85 86#[cfg(not(test))] 87fn operation_tags(operation: &PlcOperation) -> Vec<sdk::ux::TagValue> { 88 use sdk::ux::TagValue; 89 let mut tags: Vec<TagValue> = operation 90 .rotation_keys 91 .iter() 92 .map(|value| TagValue { 93 tag: "Rotation key:".into(), 94 value: value.to_string(), 95 }) 96 .collect(); 97 98 tags.push(TagValue { 99 tag: "Verification method (atproto):".into(), 100 value: operation.verification_methods.atproto.to_string(), 101 }); 102 103 for tag in operation.also_known_as.iter().map(|value| TagValue { 104 tag: "Known as:".into(), 105 value: value.to_string(), 106 }) { 107 tags.push(tag); 108 } 109 110 tags.push(TagValue { 111 tag: "Service (atproto pds):".into(), 112 value: operation.services.atproto_pds.endpoint.to_string(), 113 }); 114 115 tags 116} 117 118#[cfg(not(test))] 119fn display_full_operation(pubkey: &PublicDidKey, index: u32, operation: &PlcOperation) -> bool { 120 sdk::ux::review_pairs( 121 "Sign plc operation", 122 "", 123 &operation_tags(operation), 124 &alloc::format!("with key #{} {} ", index, pubkey.to_string()), 125 "Confirm", 126 false, 127 ) 128} 129 130#[cfg(test)] 131fn display_full_operation(pubkey: &PublicDidKey, index: u32, operation: &PlcOperation) -> bool { 132 true 133} 134 135pub fn sign_plc_operation( 136 key_index: u32, 137 operation: PlcOperation, 138 previous: Option<PlcOperation>, 139) -> Result<Response, &'static str> { 140 if key_index > 256 { 141 return Err("Index is too long"); 142 } 143 144 if operation.r#type != "plc_operation" { 145 return Err("Wrong payload type"); 146 } 147 148 let hd_node = sdk::curve::Secp256k1::derive_hd_node(&[HARDENED_PATH, key_index])?; 149 let privkey: EcfpPrivateKey<Secp256k1, 32> = EcfpPrivateKey::new(*hd_node.privkey); 150 let did_key = PublicDidKey::new(&privkey.to_public_key()); 151 152 if let Some(operation) = previous { 153 if !operation.rotation_keys.contains(&did_key.to_string()) { 154 return Err("Key is not part of the previous operation rotation_keys"); 155 } 156 // todo: check previous 157 } 158 159 if !display_full_operation(&did_key, key_index, &operation) { 160 return Err("Rejected by the user"); 161 } 162 163 let msg = 164 serde_ipld_dagcbor::to_vec(&operation).map_err(|_| "Failed to serialize operation")?; 165 let mut hasher = sdk::hash::Sha256::new(); 166 hasher.update(&msg); 167 let mut digest = [0u8; 32]; 168 hasher.digest(&mut digest); 169 170 let sig = privkey.ecdsa_sign_hash(&digest).unwrap(); 171 172 Ok(Response::Signature(sig)) 173} 174 175fn process(_app: &mut App, request: &[u8]) -> Vec<u8> { 176 let Ok(request) = postcard::from_bytes::<Request>(request) else { 177 return postcard::to_allocvec(&Response::Error("Invalid request".to_string())).unwrap(); 178 }; 179 let response = match request { 180 Request::Exit => sdk::exit(0), 181 Request::GetDidKey { index, display } => { 182 get_did_key(index, display).unwrap_or_else(|e| Response::Error(e.to_string())) 183 } 184 Request::SignPlcOperation { 185 key_index, 186 previous, 187 operation, 188 } => sign_plc_operation(key_index, operation, previous) 189 .unwrap_or_else(|e| Response::Error(e.to_string())), 190 }; 191 postcard::to_allocvec(&response).unwrap() 192} 193 194pub fn main() { 195 App::new(process).run() 196}