1use crate::{Base64urlUInt, OctetParams, Params, JWK};
12use thiserror::Error;
13
14use blake2::Blake2s;
15use snarkvm_algorithms::{
16 commitment::{PedersenCommitmentParameters, PedersenCompressedCommitment},
17 encryption::{GroupEncryption, GroupEncryptionParameters},
18 signature::{Schnorr, SchnorrParameters, SchnorrSignature},
19};
20use snarkvm_curves::edwards_bls12::{EdwardsAffine, EdwardsProjective};
21use snarkvm_dpc::{
22 account::{Address, PrivateKey, ViewKey},
23 testnet1::instantiated::Components,
24};
25use snarkvm_parameters::{
26 global::{
27 AccountCommitmentParameters, AccountEncryptionParameters, AccountSignatureParameters,
28 },
29 Parameter,
30};
31use snarkvm_utilities::{FromBytes, ToBytes};
32use std::str::FromStr;
33
34#[derive(Error, Debug)]
36pub enum AleoSignError {
37 #[error("Unable to convert JWK to Aleo private key: {0}")]
38 JWKToPrivateKey(#[source] ParsePrivateKeyError),
39 #[error("Unable to convert Aleo private key to view key: {0}")]
40 ViewKeyFromPrivateKey(#[source] snarkvm_dpc::AccountError),
41 #[error("Unable to sign with view key: {0}")]
42 Sign(#[source] snarkvm_dpc::AccountError),
43 #[error("Unable to write signture as bytes: {0}")]
44 WriteSignature(#[source] std::io::Error),
45}
46
47#[derive(Error, Debug)]
49pub enum AleoVerifyError {
50 #[error("Invalid signature over message")]
51 InvalidSignature,
52 #[error("Unable to verify signature: {0}")]
53 VerifySignature(#[source] snarkvm_dpc::AccountError),
54 #[error("Unable to deserialize account address: {0}")]
55 AddressFromStr(#[source] snarkvm_dpc::AccountError),
56 #[error("Unable to read signature bytes: {0}")]
57 ReadSignature(#[source] std::io::Error),
58}
59
60#[derive(Error, Debug)]
62pub enum AleoGeneratePrivateKeyError {
63 #[error("Unable to generate new key: {0}")]
64 NewKey(#[source] snarkvm_dpc::AccountError),
65 #[error("Unable to base58-decode new key: {0}")]
66 DecodePrivateKey(#[source] bs58::decode::Error),
67 #[error("Unable to convert private key to account address: {0}")]
68 PrivateKeyToAddress(#[source] snarkvm_dpc::AccountError),
69 #[error("Unable to write account address as bytes: {0}")]
70 WriteAddress(#[source] std::io::Error),
71}
72
73#[derive(Error, Debug)]
77pub enum ParsePrivateKeyError {
78 #[error("Unexpected JWK OKP curve: {0}")]
79 UnexpectedCurve(String),
80 #[error("Unexpected JWK key type. Expected \"OKP\"")]
81 ExpectedOKP,
82 #[error("Missing private key (\"d\") OKP JWK parameter")]
83 MissingPrivateKey,
84 #[error("Unable to deserialize private key: {0}")]
85 PrivateKeyFromStr(#[source] snarkvm_dpc::AccountError),
86 #[error("Unable to convert JWK to account address: {0}")]
87 JWKToAddress(#[source] ParseAddressError),
88 #[error("Unable to convert private key to account address: {0}")]
89 PrivateKeyToAddress(#[source] snarkvm_dpc::AccountError),
90 #[error("Address mismatch. Computed: {}, expected: {}", .computed, .expected)]
91 AddressMismatch {
92 computed: Box<Address<Components>>,
93 expected: Box<Address<Components>>,
94 },
95}
96
97#[derive(Error, Debug)]
101pub enum ParseAddressError {
102 #[error("Unexpected JWK OKP curve: {0}")]
103 UnexpectedCurve(String),
104 #[error("Unexpected JWK key type. Expected \"OKP\"")]
105 ExpectedOKP,
106 #[error("Unable to read address from bytes: {0}")]
107 ReadAddress(#[source] std::io::Error),
108}
109
110lazy_static::lazy_static! {
111 pub static ref SIG_PARAMS: Schnorr<EdwardsAffine, Blake2s> = {
113 SchnorrParameters::read_le(AccountSignatureParameters::load_bytes().unwrap().as_slice())
114 .unwrap()
115 .into()
116 };
117
118 pub static ref COM_PARAMS: PedersenCompressedCommitment<EdwardsProjective, 8, 192> = {
120 let com_params_bytes = AccountCommitmentParameters::load_bytes().unwrap();
121 PedersenCommitmentParameters::read_le(com_params_bytes.as_slice())
122 .unwrap()
123 .into()
124 };
125
126 pub static ref ENC_PARAMS: GroupEncryption<EdwardsProjective, EdwardsAffine, Blake2s> = {
128 let enc_params_bytes = AccountEncryptionParameters::load_bytes()
129 .unwrap();
130 GroupEncryptionParameters::read_le(
131 enc_params_bytes
132 .as_slice(),
133 )
134 .unwrap()
135 .into()
136 };
137}
138
139pub const OKP_CURVE: &str = "AleoTestnet1Key";
159
160pub fn generate_private_key_jwk() -> Result<JWK, AleoGeneratePrivateKeyError> {
164 let mut rng = rand::rngs::OsRng {};
165 let sig_params = SIG_PARAMS.clone();
166 let com_params = COM_PARAMS.clone();
167 let enc_params = ENC_PARAMS.clone();
168 let private_key = PrivateKey::<Components>::new(&sig_params, &com_params, &mut rng)
169 .map_err(AleoGeneratePrivateKeyError::NewKey)?;
170 let private_key_bytes = bs58::decode(private_key.to_string())
171 .into_vec()
172 .map_err(AleoGeneratePrivateKeyError::DecodePrivateKey)?;
173 let address = Address::from_private_key(&sig_params, &com_params, &enc_params, &private_key)
174 .map_err(AleoGeneratePrivateKeyError::PrivateKeyToAddress)?;
175 let mut public_key_bytes = Vec::new();
176 address
177 .write_le(&mut public_key_bytes)
178 .map_err(AleoGeneratePrivateKeyError::WriteAddress)?;
179 Ok(JWK::from(Params::OKP(OctetParams {
180 curve: OKP_CURVE.to_string(),
181 public_key: Base64urlUInt(public_key_bytes),
182 private_key: Some(Base64urlUInt(private_key_bytes)),
183 })))
184}
185
186fn aleo_jwk_to_private_key(jwk: &JWK) -> Result<PrivateKey<Components>, ParsePrivateKeyError> {
190 let params = match &jwk.params {
191 Params::OKP(ref okp_params) => {
192 if okp_params.curve != OKP_CURVE {
193 return Err(ParsePrivateKeyError::UnexpectedCurve(
194 okp_params.curve.to_string(),
195 ));
196 }
197 okp_params
198 }
199 _ => return Err(ParsePrivateKeyError::ExpectedOKP),
200 };
201 let private_key_bytes = params
202 .private_key
203 .as_ref()
204 .ok_or(ParsePrivateKeyError::MissingPrivateKey)?;
205 let private_key_base58 = bs58::encode(&private_key_bytes.0).into_string();
206 let address = aleo_jwk_to_address(jwk).map_err(ParsePrivateKeyError::JWKToAddress)?;
207 let private_key = PrivateKey::<Components>::from_str(&private_key_base58)
208 .map_err(ParsePrivateKeyError::PrivateKeyFromStr)?;
209 let address_computed = Address::from_private_key(
210 &SIG_PARAMS.clone(),
211 &COM_PARAMS.clone(),
212 &ENC_PARAMS.clone(),
213 &private_key,
214 )
215 .map_err(ParsePrivateKeyError::PrivateKeyToAddress)?;
216 if address_computed != address {
217 return Err(ParsePrivateKeyError::AddressMismatch {
218 computed: Box::new(address_computed),
219 expected: Box::new(address),
220 });
221 }
222 Ok(private_key)
223}
224
225fn aleo_jwk_to_address(jwk: &JWK) -> Result<Address<Components>, ParseAddressError> {
226 let params = match &jwk.params {
227 Params::OKP(ref okp_params) => {
228 if okp_params.curve != OKP_CURVE {
229 return Err(ParseAddressError::UnexpectedCurve(
230 okp_params.curve.to_string(),
231 ));
232 }
233 okp_params
234 }
235 _ => return Err(ParseAddressError::ExpectedOKP),
236 };
237 let public_key_bytes = ¶ms.public_key.0;
238 let address = Address::<Components>::read_le(&**public_key_bytes)
239 .map_err(ParseAddressError::ReadAddress)?;
240 Ok(address)
241}
242
243pub fn sign(msg: &[u8], key: &JWK) -> Result<Vec<u8>, AleoSignError> {
250 let private_key = aleo_jwk_to_private_key(key).map_err(AleoSignError::JWKToPrivateKey)?;
251 let enc_params = ENC_PARAMS.clone();
252 let sig_params = SIG_PARAMS.clone();
253 let com_params = COM_PARAMS.clone();
254 let view_key = ViewKey::<Components>::from_private_key(&sig_params, &com_params, &private_key)
255 .map_err(AleoSignError::ViewKeyFromPrivateKey)?;
256 let mut rng = rand::rngs::OsRng {};
257 let sig = view_key
258 .sign(&enc_params, msg, &mut rng)
259 .map_err(AleoSignError::Sign)?;
260 let mut sig_bytes = Vec::new();
261 sig.write_le(&mut sig_bytes)
262 .map_err(AleoSignError::WriteSignature)?;
263 Ok(sig_bytes)
264}
265
266pub fn verify(msg: &[u8], address: &str, sig: &[u8]) -> Result<(), AleoVerifyError> {
270 let address =
271 Address::<Components>::from_str(address).map_err(AleoVerifyError::AddressFromStr)?;
272 let sig =
273 SchnorrSignature::<EdwardsAffine>::read_le(sig).map_err(AleoVerifyError::ReadSignature)?;
274 let enc_params = ENC_PARAMS.clone();
275 let valid = address
276 .verify_signature(&enc_params, msg, &sig)
277 .map_err(AleoVerifyError::VerifySignature)?;
278 if !valid {
279 return Err(AleoVerifyError::InvalidSignature);
280 }
281 Ok(())
282}
283
284#[cfg(test)]
285mod tests {
286 use super::*;
287
288 #[test]
289 fn parse_private_key_jwk() {
290 let key: JWK =
291 serde_json::from_str(include_str!("../../../tests/aleotestnet1-2021-11-22.json"))
292 .unwrap();
293 let private_key = aleo_jwk_to_private_key(&key).unwrap();
294 let private_key_str = private_key.to_string();
295 assert_eq!(
296 private_key_str,
297 "APrivateKey1w7oJWmo86D26Efs6hBfz8xK7M4ww2jmA5WT3QdmYefVnZdS"
298 );
299 let address = Address::from_private_key(
300 &SIG_PARAMS.clone(),
301 &COM_PARAMS.clone(),
302 &ENC_PARAMS.clone(),
303 &private_key,
304 )
305 .unwrap();
306 assert_eq!(
307 address.to_string(),
308 "aleo1al8unplh8vtsuwna0h6u2t6g0hvr7t0tnfkem2we5gj7t70aeuxsd94hsy"
309 );
310 }
311
312 #[test]
313 fn aleo_jwk_sign_verify() {
314 let private_key: JWK =
315 serde_json::from_str(include_str!("../../../tests/aleotestnet1-2021-11-22.json"))
316 .unwrap();
317
318 let public_key = private_key.to_public();
319 let msg1 = b"asdf";
320 let msg2 = b"asdfg";
321 let sig = sign(msg1, &private_key).unwrap();
322 let address = aleo_jwk_to_address(&public_key).unwrap();
323 let address_string = format!("{}", &address);
324 verify(msg1, &address_string, &sig).unwrap();
325 verify(msg2, &address_string, &sig).unwrap_err();
326 }
327}