Skip to content

Commit 5635f88

Browse files
committed
test: add property tests for secp functions
1 parent 5753d73 commit 5635f88

File tree

1 file changed

+283
-0
lines changed

1 file changed

+283
-0
lines changed

clarity/src/vm/tests/crypto.rs

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use proptest::prelude::*;
12
use stacks_common::types::chainstate::{StacksPrivateKey, StacksPublicKey};
23
use stacks_common::types::{PrivateKey, StacksEpochId};
34
use stacks_common::util::hash::{to_hex, Sha256Sum};
@@ -370,3 +371,285 @@ fn test_secp256k1_recover_invalid_signature_returns_err_code() {
370371
other => panic!("expected err response, found {other:?}"),
371372
}
372373
}
374+
375+
proptest! {
376+
#[test]
377+
fn prop_secp256k1_verify_accepts_valid_signatures(
378+
seed in any::<[u8; 32]>(),
379+
message in any::<[u8; 32]>()
380+
) {
381+
let privk = StacksPrivateKey::from_seed(&seed);
382+
let pubk = StacksPublicKey::from_private(&privk);
383+
let pubkey_bytes = pubk.to_bytes_compressed();
384+
let message = message.to_vec();
385+
let signature: Secp256k1Signature = privk.sign(&message).expect("secp256k1 signing should succeed");
386+
let signature_bytes = signature.to_rsv();
387+
let program = format!(
388+
"(secp256k1-verify {} {} {})",
389+
buff_literal(&message),
390+
buff_literal(&signature_bytes),
391+
buff_literal(&pubkey_bytes)
392+
);
393+
394+
let result = execute_with_parameters(
395+
program.as_str(),
396+
ClarityVersion::Clarity4,
397+
StacksEpochId::Epoch33,
398+
false,
399+
)
400+
.expect("execution should succeed")
401+
.expect("should return a value");
402+
403+
prop_assert_eq!(Value::Bool(true), result);
404+
}
405+
406+
#[test]
407+
fn prop_secp256k1_recover_matches_public_key(
408+
seed in any::<[u8; 32]>(),
409+
message in any::<[u8; 32]>()
410+
) {
411+
let privk = StacksPrivateKey::from_seed(&seed);
412+
let pubk = StacksPublicKey::from_private(&privk);
413+
let pubkey_bytes = pubk.to_bytes_compressed();
414+
let message = message.to_vec();
415+
let signature: Secp256k1Signature = privk.sign(&message).expect("secp256k1 signing should succeed");
416+
let signature_bytes = signature.to_rsv();
417+
let program = format!(
418+
"(is-eq (unwrap! (secp256k1-recover? {} {}) (err u1)) {})",
419+
buff_literal(&message),
420+
buff_literal(&signature_bytes),
421+
buff_literal(&pubkey_bytes)
422+
);
423+
424+
let result = execute_with_parameters(
425+
program.as_str(),
426+
ClarityVersion::Clarity4,
427+
StacksEpochId::Epoch33,
428+
false,
429+
)
430+
.expect("execution should succeed")
431+
.expect("should return a value");
432+
433+
prop_assert_eq!(Value::Bool(true), result);
434+
}
435+
436+
#[test]
437+
fn prop_secp256r1_verify_accepts_valid_signatures(
438+
seed in any::<[u8; 32]>(),
439+
message in any::<[u8; 32]>()
440+
) {
441+
let privk = Secp256r1PrivateKey::from_seed(&seed);
442+
let pubk = Secp256r1PublicKey::from_private(&privk);
443+
let pubkey_bytes = pubk.to_bytes_compressed();
444+
let message = message.to_vec();
445+
let signature = privk.sign(&message).expect("secp256r1 signing should succeed");
446+
let program = format!(
447+
"(secp256r1-verify {} {} {})",
448+
buff_literal(&message),
449+
buff_literal(&signature.0),
450+
buff_literal(&pubkey_bytes)
451+
);
452+
453+
let result = execute_with_parameters(
454+
program.as_str(),
455+
ClarityVersion::Clarity4,
456+
StacksEpochId::Epoch33,
457+
false,
458+
)
459+
.expect("execution should succeed")
460+
.expect("should return a value");
461+
462+
prop_assert_eq!(Value::Bool(true), result);
463+
}
464+
465+
#[test]
466+
fn prop_secp256k1_verify_rejects_tampered_msg(
467+
seed in any::<[u8; 32]>(),
468+
message in any::<[u8; 32]>(),
469+
bit in 0usize..32
470+
) {
471+
let privk = StacksPrivateKey::from_seed(&seed);
472+
let pubk = StacksPublicKey::from_private(&privk);
473+
let pubkey_bytes = pubk.to_bytes_compressed();
474+
let mut m = message.to_vec();
475+
let sig: Secp256k1Signature = privk.sign(&m).unwrap();
476+
let sig_bytes = sig.to_rsv();
477+
478+
// flip one bit
479+
m[bit] ^= 0x01;
480+
481+
let program = format!(
482+
"(secp256k1-verify {} {} {})",
483+
buff_literal(&m),
484+
buff_literal(&sig_bytes),
485+
buff_literal(&pubkey_bytes)
486+
);
487+
let result = execute_with_parameters(
488+
&program, ClarityVersion::Clarity4, StacksEpochId::Epoch33, false
489+
).unwrap().unwrap();
490+
491+
prop_assert_eq!(Value::Bool(false), result);
492+
}
493+
494+
#[test]
495+
fn prop_secp256r1_verify_rejects_tampered_msg(
496+
seed in any::<[u8; 32]>(),
497+
message in any::<[u8; 32]>(),
498+
bit in 0usize..32
499+
) {
500+
let privk = Secp256r1PrivateKey::from_seed(&seed);
501+
let pubk = Secp256r1PublicKey::from_private(&privk);
502+
let pubkey_bytes = pubk.to_bytes_compressed();
503+
let mut message = message.to_vec();
504+
let signature = privk.sign(&message).expect("secp256r1 signing should succeed");
505+
506+
// flip one bit
507+
message[bit] ^= 0x01;
508+
509+
let program = format!(
510+
"(secp256r1-verify {} {} {})",
511+
buff_literal(&message),
512+
buff_literal(&signature.0),
513+
buff_literal(&pubkey_bytes)
514+
);
515+
516+
let result = execute_with_parameters(
517+
program.as_str(),
518+
ClarityVersion::Clarity4,
519+
StacksEpochId::Epoch33,
520+
false,
521+
)
522+
.expect("execution should succeed")
523+
.expect("should return a value");
524+
525+
prop_assert_eq!(Value::Bool(false), result);
526+
}
527+
528+
#[test]
529+
fn prop_secp256k1_recover_fails_to_match_with_tampered_msg(
530+
seed in any::<[u8; 32]>(),
531+
message in any::<[u8; 32]>(),
532+
bit in 0usize..32
533+
) {
534+
let privk = StacksPrivateKey::from_seed(&seed);
535+
let pubk = StacksPublicKey::from_private(&privk);
536+
let pubkey_bytes = pubk.to_bytes_compressed();
537+
let mut message = message.to_vec();
538+
let signature: Secp256k1Signature = privk.sign(&message).expect("secp256k1 signing should succeed");
539+
let signature_bytes = signature.to_rsv();
540+
541+
// flip one bit
542+
message[bit] ^= 0x01;
543+
544+
let program = format!(
545+
"(is-eq (unwrap! (secp256k1-recover? {} {}) (err u1)) {})",
546+
buff_literal(&message),
547+
buff_literal(&signature_bytes),
548+
buff_literal(&pubkey_bytes)
549+
);
550+
551+
let result = execute_with_parameters(
552+
program.as_str(),
553+
ClarityVersion::Clarity4,
554+
StacksEpochId::Epoch33,
555+
false,
556+
)
557+
.expect("execution should succeed")
558+
.expect("should return a value");
559+
560+
prop_assert_eq!(Value::Bool(false), result);
561+
}
562+
563+
#[test]
564+
fn prop_secp256r1_verify_rejects_wrong_key(
565+
seed_a in any::<[u8; 32]>(),
566+
seed_b in any::<[u8; 32]>(),
567+
message in any::<[u8; 32]>()
568+
) {
569+
prop_assume!(seed_a != seed_b);
570+
571+
let priv_a = Secp256r1PrivateKey::from_seed(&seed_a);
572+
let pub_b = Secp256r1PublicKey::from_private(&Secp256r1PrivateKey::from_seed(&seed_b));
573+
let pub_b_bytes = pub_b.to_bytes_compressed();
574+
575+
let msg = message.to_vec();
576+
let signature = priv_a.sign(&msg).unwrap();
577+
578+
let program = format!(
579+
"(secp256r1-verify {} {} {})",
580+
buff_literal(&msg),
581+
buff_literal(&signature.0),
582+
buff_literal(&pub_b_bytes)
583+
);
584+
let result = execute_with_parameters(
585+
&program, ClarityVersion::Clarity4, StacksEpochId::Epoch33, false
586+
).unwrap().unwrap();
587+
588+
prop_assert_eq!(Value::Bool(false), result);
589+
}
590+
591+
#[test]
592+
fn prop_secp256k1_verify_rejects_wrong_key(
593+
seed_a in any::<[u8; 32]>(),
594+
seed_b in any::<[u8; 32]>(),
595+
message in any::<[u8; 32]>()
596+
) {
597+
prop_assume!(seed_a != seed_b);
598+
let priv_a = StacksPrivateKey::from_seed(&seed_a);
599+
let pub_b = StacksPublicKey::from_private(&StacksPrivateKey::from_seed(&seed_b));
600+
let pub_b_bytes = pub_b.to_bytes_compressed();
601+
602+
let message = message.to_vec();
603+
let signature: Secp256k1Signature = priv_a.sign(&message).expect("secp256k1 signing should succeed");
604+
let signature_bytes = signature.to_rsv();
605+
let program = format!(
606+
"(secp256k1-verify {} {} {})",
607+
buff_literal(&message),
608+
buff_literal(&signature_bytes),
609+
buff_literal(&pub_b_bytes)
610+
);
611+
612+
let result = execute_with_parameters(
613+
program.as_str(),
614+
ClarityVersion::Clarity4,
615+
StacksEpochId::Epoch33,
616+
false,
617+
)
618+
.expect("execution should succeed")
619+
.expect("should return a value");
620+
621+
prop_assert_eq!(Value::Bool(false), result);
622+
}
623+
624+
#[test]
625+
fn prop_secp256k1_recover_fails_to_match_with_wrong_key(
626+
seed_a in any::<[u8; 32]>(),
627+
seed_b in any::<[u8; 32]>(),
628+
message in any::<[u8; 32]>()
629+
) {
630+
let priv_a = StacksPrivateKey::from_seed(&seed_a);
631+
let pub_b = StacksPublicKey::from_private(&StacksPrivateKey::from_seed(&seed_b));
632+
let pub_b_bytes = pub_b.to_bytes_compressed();
633+
634+
let message = message.to_vec();
635+
let signature: Secp256k1Signature = priv_a.sign(&message).expect("secp256k1 signing should succeed");
636+
let signature_bytes = signature.to_rsv();
637+
let program = format!(
638+
"(is-eq (unwrap! (secp256k1-recover? {} {}) (err u1)) {})",
639+
buff_literal(&message),
640+
buff_literal(&signature_bytes),
641+
buff_literal(&pub_b_bytes)
642+
);
643+
644+
let result = execute_with_parameters(
645+
program.as_str(),
646+
ClarityVersion::Clarity4,
647+
StacksEpochId::Epoch33,
648+
false,
649+
)
650+
.expect("execution should succeed")
651+
.expect("should return a value");
652+
653+
prop_assert_eq!(Value::Bool(false), result);
654+
}
655+
}

0 commit comments

Comments
 (0)