use crate::{
asm::{Crypto, Op, Stack, Word},
crypto::{bytes_from_words, recover_secp256k1},
error::{ConstraintError, CryptoError, OpError},
eval_ops, exec_ops,
test_util::*,
types::{
convert::{bytes_from_word, word_4_from_u8_32, word_8_from_u8_64},
Hash,
},
};
use essential_types::convert::{u8_32_from_word_4, word_from_bytes_slice};
use sha2::Digest;
use super::pop_bytes;
fn exec_ops_sha256(ops: &[Op]) -> Hash {
let stack = exec_ops(ops, *test_access()).unwrap();
assert_eq!(stack.len(), 4);
let bytes: Vec<u8> = stack.iter().copied().flat_map(bytes_from_word).collect();
bytes.try_into().unwrap()
}
fn hash(bytes: &[u8]) -> [u8; 32] {
let mut hasher = sha2::Sha256::new();
hasher.update(bytes);
hasher.finalize().into()
}
#[test]
#[rustfmt::skip]
fn sha256_1_word() {
let ops = &[
Stack::Push(0x0000000000000000).into(), Stack::Push(8).into(), Crypto::Sha256.into(),
];
let hash = exec_ops_sha256(ops);
let expected = [
0xaf, 0x55, 0x70, 0xf5, 0xa1, 0x81, 0x0b, 0x7a,
0xf7, 0x8c, 0xaf, 0x4b, 0xc7, 0x0a, 0x66, 0x0f,
0x0d, 0xf5, 0x1e, 0x42, 0xba, 0xf9, 0x1d, 0x4d,
0xe5, 0xb2, 0x32, 0x8d, 0xe0, 0xe8, 0x3d, 0xfc,
];
assert_eq!(&hash[..], &expected);
}
#[test]
#[rustfmt::skip]
fn sha256_3_words() {
let ops = &[
Stack::Push(0x00000000000000FF).into(), Stack::Push(0x00000000000000FF).into(), Stack::Push(0x00000000000000FF).into(), Stack::Push(3 * 8).into(), Crypto::Sha256.into(),
];
let hash = exec_ops_sha256(ops);
let expected = [
0x58, 0x2d, 0xc8, 0xbd, 0xf8, 0xed, 0x36, 0x46,
0x65, 0xa2, 0xd4, 0x59, 0x13, 0xc4, 0x79, 0x9f,
0x38, 0x6e, 0xe0, 0xc2, 0x51, 0x96, 0x80, 0x81,
0x00, 0xe2, 0xfc, 0x2d, 0xae, 0x75, 0x00, 0xd6,
];
assert_eq!(&hash[..], &expected);
}
fn test_sha256_inner(bytes: &[u8], data_len: usize) -> crate::Stack {
let words: Vec<Word> = bytes.chunks(8).map(word_from_bytes_slice).collect();
let mut stack = crate::Stack::default();
for word in words {
stack.push(word).unwrap();
}
stack.push(data_len as Word).unwrap();
stack
}
#[test]
fn test_sha256_bytes() {
let bytes = [0x01; 33];
let mut stack = test_sha256_inner(&bytes, bytes.len());
super::sha256(&mut stack).unwrap();
let result: Vec<u8> = stack.iter().copied().flat_map(bytes_from_word).collect();
assert_eq!(result, hash(&bytes));
let bytes = [0x01; 32];
let mut stack = test_sha256_inner(&bytes, bytes.len());
super::sha256(&mut stack).unwrap();
let result: Vec<u8> = stack.iter().copied().flat_map(bytes_from_word).collect();
assert_eq!(result, hash(&bytes));
let mut stack = test_sha256_inner(&[], 0);
super::sha256(&mut stack).unwrap();
let result: Vec<u8> = stack.iter().copied().flat_map(bytes_from_word).collect();
assert_eq!(result, hash(&[]));
let mut stack = test_sha256_inner(&[1; 39], 40);
super::sha256(&mut stack).unwrap();
let result: Vec<u8> = stack.iter().copied().flat_map(bytes_from_word).collect();
let mut expected = vec![1; 39];
expected.push(0);
assert_eq!(result, hash(&expected));
let mut stack = test_sha256_inner(&[1; 39], 41);
super::sha256(&mut stack).unwrap_err();
}
fn test_ed25519_ops(num_bytes: usize) -> Vec<Op> {
use ed25519_dalek::{Signer, SigningKey};
use rand::{Rng, SeedableRng};
let data: &[Word] = &[7, 3, 5, 7];
let data_bytes: Vec<_> = bytes_from_words(data.iter().copied())
.take(num_bytes)
.collect();
let mut rng = rand::rngs::SmallRng::from_seed([0x00; 32]);
let key_bytes = rng.gen();
let privkey: SigningKey = SigningKey::from_bytes(&key_bytes);
let pubkey = privkey.verifying_key();
let pubkey_bytes = pubkey.to_bytes();
let signature = privkey.sign(&data_bytes);
pubkey.verify_strict(&data_bytes, &signature).unwrap();
let signature_bytes = signature.to_bytes();
data.iter()
.copied()
.chain(Some(Word::try_from(data_bytes.len()).unwrap()))
.chain(word_8_from_u8_64(signature_bytes))
.chain(word_4_from_u8_32(pubkey_bytes))
.map(Stack::Push)
.map(Op::from)
.chain(Some(Crypto::VerifyEd25519.into()))
.collect()
}
#[test]
fn verify_ed25519_true() {
let ops = test_ed25519_ops(8 * 4);
assert!(eval_ops(&ops, *test_access()).unwrap());
}
#[test]
fn verify_ed25519_bytes_true() {
let ops = test_ed25519_ops(8 * 3 + 2);
assert!(eval_ops(&ops, *test_access()).unwrap());
}
#[test]
fn verify_ed25519_false() {
let mut ops = test_ed25519_ops(8 * 4);
ops[0] = Stack::Push(0).into(); assert!(!eval_ops(&ops, *test_access()).unwrap());
}
#[test]
fn ed25519_error() {
let mut ops = test_ed25519_ops(8 * 4);
let key_ix = ops.len() - 5;
ops[key_ix] = Stack::Push(1).into();
ops[key_ix + 1] = Stack::Push(1).into();
ops[key_ix + 2] = Stack::Push(1).into();
ops[key_ix + 3] = Stack::Push(1).into();
let res = eval_ops(&ops, *test_access());
match res {
Err(ConstraintError::Op(_, OpError::Crypto(CryptoError::Ed25519(_err)))) => (),
_ => panic!("expected ed25519 error, got {res:?}"),
}
}
#[test]
fn test_secp256k1() {
use rand::SeedableRng;
use secp256k1::{Message, PublicKey, Secp256k1};
let secp = Secp256k1::new();
let mut rng = rand::rngs::SmallRng::from_seed([0x00; 32]);
let (secret_key, public_key) = secp.generate_keypair(&mut rng);
let message = Message::from_digest([0; 32]);
let secp = Secp256k1::new();
let sig = secp.sign_ecdsa_recoverable(&message, &secret_key);
let (rec_id, sig_bytes) = sig.serialize_compact();
let check = |sig_bytes, message: [u8; 32]| {
let mut stack = crate::Stack::default();
stack.extend(word_4_from_u8_32(message)).unwrap();
stack.extend(word_8_from_u8_64(sig_bytes)).unwrap();
let rec_id_word = Word::from(i32::from(rec_id));
stack.push(rec_id_word).unwrap();
recover_secp256k1(&mut stack).unwrap();
let end = stack.pop().unwrap();
let end = bytes_from_word(end);
let recovered = stack.pop4().unwrap();
let mut recovered = u8_32_from_word_4(recovered).to_vec();
recovered.push(end[7]);
let bytes: [u8; 33] = recovered.try_into().unwrap();
bytes
};
let result = PublicKey::from_slice(&check(sig_bytes, *message.as_ref())).unwrap();
assert_eq!(result, public_key);
let result = PublicKey::from_slice(&check(sig_bytes, [1; 32])).unwrap();
assert_ne!(result, public_key);
let result = check([0; 64], *message.as_ref());
assert_eq!(result, [0u8; 33]);
}
#[test]
fn test_pop_bytes() {
let mut stack = crate::Stack::default();
let word = 0xFF << 56;
stack.push(word).unwrap();
stack.push(1).unwrap();
let byte = pop_bytes(&mut stack).unwrap();
assert_eq!(stack.len(), 0);
assert_eq!(byte.as_slice(), &[0xFF]);
let bytes: Vec<u8> = (0..20).collect();
let words: Vec<Word> = bytes.chunks(8).map(word_from_bytes_slice).collect();
stack.extend(words).unwrap();
stack.push(bytes.len() as Word).unwrap();
let result = pop_bytes(&mut stack).unwrap();
assert_eq!(stack.len(), 0);
assert_eq!(result, bytes);
}