@@ -401,6 +401,68 @@ mod tests {
401
401
. is_ok( ) ) ;
402
402
}
403
403
404
+ #[ test]
405
+ fn test_secp256k1_schnorr_sign ( ) {
406
+ mock_unlocked_using_mnemonic ( "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" , "" ) ;
407
+ let keypath = [ 86 + HARDENED , 0 + HARDENED , 0 + HARDENED , 0 , 0 ] ;
408
+ let msg = [ 0x88u8 ; 32 ] ;
409
+
410
+ let expected_pubkey = {
411
+ let pubkey =
412
+ hex:: decode ( "cc8a4bc64d897bddc5fbc2f670f7a8ba0b386779106cf1223c6fc5d7cd6fc115" )
413
+ . unwrap ( ) ;
414
+ secp256k1:: XOnlyPublicKey :: from_slice ( & pubkey) . unwrap ( )
415
+ } ;
416
+
417
+ // Test without tweak
418
+ crate :: random:: mock_reset ( ) ;
419
+ let sig = secp256k1_schnorr_sign ( & keypath, & msg, None ) . unwrap ( ) ;
420
+ let secp = secp256k1:: Secp256k1 :: new ( ) ;
421
+ assert ! ( secp
422
+ . verify_schnorr(
423
+ & secp256k1:: schnorr:: Signature :: from_slice( & sig) . unwrap( ) ,
424
+ & secp256k1:: Message :: from_digest_slice( & msg) . unwrap( ) ,
425
+ & expected_pubkey
426
+ )
427
+ . is_ok( ) ) ;
428
+
429
+ // Test with tweak
430
+ crate :: random:: mock_reset ( ) ;
431
+ let tweak = {
432
+ let tweak =
433
+ hex:: decode ( "a39fb163dbd9b5e0840af3cc1ee41d5b31245c5dd8d6bdc3d026d09b8964997c" )
434
+ . unwrap ( ) ;
435
+ secp256k1:: Scalar :: from_be_bytes ( tweak. try_into ( ) . unwrap ( ) ) . unwrap ( )
436
+ } ;
437
+ let ( tweaked_pubkey, _) = expected_pubkey. add_tweak ( & secp, & tweak) . unwrap ( ) ;
438
+ let sig = secp256k1_schnorr_sign ( & keypath, & msg, Some ( & tweak. to_be_bytes ( ) ) ) . unwrap ( ) ;
439
+ assert ! ( secp
440
+ . verify_schnorr(
441
+ & secp256k1:: schnorr:: Signature :: from_slice( & sig) . unwrap( ) ,
442
+ & secp256k1:: Message :: from_digest_slice( & msg) . unwrap( ) ,
443
+ & tweaked_pubkey
444
+ )
445
+ . is_ok( ) ) ;
446
+ }
447
+
448
+ #[ test]
449
+ fn test_secp256k1_nonce_commit ( ) {
450
+ lock ( ) ;
451
+ let keypath = [ 44 + HARDENED , 0 + HARDENED , 0 + HARDENED , 0 , 5 ] ;
452
+ let msg = [ 0x88u8 ; 32 ] ;
453
+ let host_commitment = [ 0xabu8 ; 32 ] ;
454
+
455
+ // Fails because keystore is locked.
456
+ assert ! ( secp256k1_nonce_commit( & keypath, & msg, & host_commitment) . is_err( ) ) ;
457
+
458
+ mock_unlocked ( ) ;
459
+ let client_commitment = secp256k1_nonce_commit ( & keypath, & msg, & host_commitment) . unwrap ( ) ;
460
+ assert_eq ! (
461
+ hex:: encode( client_commitment) ,
462
+ "0381e4136251c87f2947b735159c6dd644a7b58d35b437e20c878e5129f1320e5e" ,
463
+ ) ;
464
+ }
465
+
404
466
#[ test]
405
467
fn test_bip39_mnemonic_to_seed ( ) {
406
468
assert ! ( bip39_mnemonic_to_seed( "invalid" ) . is_err( ) ) ;
@@ -550,6 +612,22 @@ mod tests {
550
612
assert ! ( bip39_mnemonic_from_seed( b"foo" ) . is_err( ) ) ;
551
613
}
552
614
615
+ #[ test]
616
+ fn test_lock ( ) {
617
+ lock ( ) ;
618
+ assert ! ( is_locked( ) ) ;
619
+
620
+ let seed = hex:: decode ( "cb33c20cea62a5c277527e2002da82e6e2b37450a755143a540a54cea8da9044" )
621
+ . unwrap ( ) ;
622
+ assert ! ( encrypt_and_store_seed( & seed, "password" ) . is_ok( ) ) ;
623
+ assert ! ( unlock( "password" ) . is_ok( ) ) ;
624
+ assert ! ( is_locked( ) ) ; // still locked, it is only unlocked after unlock_bip39.
625
+ assert ! ( unlock_bip39( "foo" ) . is_ok( ) ) ;
626
+ assert ! ( !is_locked( ) ) ;
627
+ lock ( ) ;
628
+ assert ! ( is_locked( ) ) ;
629
+ }
630
+
553
631
#[ test]
554
632
fn test_unlock ( ) {
555
633
mock_memory ( ) ;
@@ -624,7 +702,6 @@ mod tests {
624
702
625
703
assert ! ( encrypt_and_store_seed( & seed, "password" ) . is_ok( ) ) ;
626
704
assert ! ( unlock( "password" ) . is_ok( ) ) ;
627
- assert ! ( is_locked( ) ) ; // still locked, it is only unlocked after unlock_bip39.
628
705
assert ! ( unlock_bip39( "foo" ) . is_ok( ) ) ;
629
706
630
707
// Check that the retained bip39 seed was encrypted with the expected encryption key.
@@ -647,6 +724,68 @@ mod tests {
647
724
assert_eq ! ( decrypted. as_slice( ) , expected_bip39_seed. as_slice( ) ) ;
648
725
}
649
726
727
+ #[ test]
728
+ fn test_create_and_store_seed ( ) {
729
+ let mock_salt_root =
730
+ hex:: decode ( "3333333333333333444444444444444411111111111111112222222222222222" )
731
+ . unwrap ( ) ;
732
+
733
+ let host_entropy =
734
+ hex:: decode ( "25569b9a11f9db6560459e8e48b4727a4c935300143d978989ed55db1d1b9cbe25569b9a11f9db6560459e8e48b4727a4c935300143d978989ed55db1d1b9cbe" )
735
+ . unwrap ( ) ;
736
+
737
+ // Invalid seed lengths
738
+ for size in [ 8 , 24 , 40 ] {
739
+ assert ! ( matches!(
740
+ create_and_store_seed( "password" , & host_entropy[ ..size] ) ,
741
+ Err ( Error :: SeedSize )
742
+ ) ) ;
743
+ }
744
+
745
+ // Hack to get the random bytes that will be used.
746
+ let seed_random = {
747
+ crate :: random:: mock_reset ( ) ;
748
+ crate :: random:: random_32_bytes ( )
749
+ } ;
750
+
751
+ // Derived from mock_salt_root and "password".
752
+ let password_salted_hashed =
753
+ hex:: decode ( "e8c70a20d9108fbb9454b1b8e2d7373e78cbaf9de025ab2d4f4d3c7a6711694c" )
754
+ . unwrap ( ) ;
755
+
756
+ // expected_seed = seed_random ^ host_entropy ^ password_salted_hashed
757
+ let expected_seed: Vec < u8 > = seed_random
758
+ . into_iter ( )
759
+ . zip ( host_entropy. iter ( ) )
760
+ . zip ( password_salted_hashed)
761
+ . map ( |( ( a, & b) , c) | a ^ b ^ c)
762
+ . collect ( ) ;
763
+
764
+ for size in [ 16 , 32 ] {
765
+ mock_memory ( ) ;
766
+ crate :: random:: mock_reset ( ) ;
767
+ crate :: memory:: set_salt_root ( mock_salt_root. as_slice ( ) . try_into ( ) . unwrap ( ) ) . unwrap ( ) ;
768
+ lock ( ) ;
769
+
770
+ assert ! ( create_and_store_seed( "password" , & host_entropy[ ..size] ) . is_ok( ) ) ;
771
+ assert ! ( unlock( "password" ) . is_ok( ) ) ;
772
+ assert_eq ! ( copy_seed( ) . unwrap( ) . as_slice( ) , & expected_seed[ ..size] ) ;
773
+ // Check the seed has been stored encrypted with the expected encryption key.
774
+ // Decrypt and check seed.
775
+ let cipher = crate :: memory:: get_encrypted_seed_and_hmac ( ) . unwrap ( ) ;
776
+
777
+ // Same as Python:
778
+ // import hmac, hashlib; hmac.digest(b"unit-test", b"password", hashlib.sha256).hex()
779
+ // See also: mock_securechip.c
780
+ let expected_encryption_key =
781
+ hex:: decode ( "e56de448f5f1d29cdcc0e0099007309afe4d5a3ef2349e99dcc41840ad98409e" )
782
+ . unwrap ( ) ;
783
+ let decrypted =
784
+ bitbox_aes:: decrypt_with_hmac ( & expected_encryption_key, & cipher) . unwrap ( ) ;
785
+ assert_eq ! ( decrypted. as_slice( ) , & expected_seed[ ..size] ) ;
786
+ }
787
+ }
788
+
650
789
// This tests that you can create a keystore, unlock it, and then do this again. This is an
651
790
// expected workflow for when the wallet setup process is restarted after seeding and unlocking,
652
791
// but before creating a backup, in which case a new seed is created.
0 commit comments