@@ -398,6 +398,77 @@ void btc_segwit_init_cache(btc_txn_context_t *context) {
398
398
memzero (& sha_256_ctx , sizeof (sha_256_ctx ));
399
399
}
400
400
401
+ void btc_taproot_init_cache (btc_txn_context_t * context ) {
402
+ btc_taproot_cache_t * taproot_cache = & context -> taproot_cache ;
403
+
404
+ //sha_prevouts
405
+ uint8_t * prevouts_buf = malloc (36 * context -> metadata .input_count );
406
+ memzero (prevouts_buf , 36 * context -> metadata .input_count );
407
+
408
+ for (uint32_t idx = 0 , len = 0 ; idx < context -> metadata .input_count ; idx ++ ) {
409
+ memcpy (prevouts_buf + len , context -> inputs [idx ].prev_txn_hash , 32 );
410
+ len += 32 ;
411
+ memcpy (prevouts_buf + len , (uint8_t * )& context -> inputs [idx ].prev_output_index , 4 );
412
+ len + 4 ;
413
+ }
414
+ sha256_Raw (prevouts_buf , 36 * context -> metadata .input_count , taproot_cache -> sha_prevouts );
415
+ free (prevouts_buf );
416
+
417
+ //sha_amounts
418
+ SHA256_CTX sha_256_ctx = {0 };
419
+ memzero (& sha_256_ctx , sizeof (sha_256_ctx ));
420
+ sha256_Init (& sha_256_ctx );
421
+
422
+ for (uint32_t idx = 0 ; idx < context -> metadata .input_count ; idx ++ ) {
423
+ sha256_Update (& sha_256_ctx , (uint8_t * )& context -> inputs [idx ].value , 8 );
424
+ }
425
+ sha256_Final (& sha_256_ctx , taproot_cache -> sha_amounts );
426
+
427
+ //sha_scriptpubkeys
428
+ uint8_t * script_buf = malloc (35 * context -> metadata .input_count );
429
+ memzero (script_buf , 35 * context -> metadata .input_count );
430
+
431
+ for (uint32_t idx = 0 , len = 0 ; idx < context -> metadata .input_count ; idx ++ ) {
432
+ memcpy (script_buf + len , context -> inputs [idx ].script_pub_key .bytes , context -> inputs [idx ].script_pub_key .size );
433
+ len += context -> inputs [idx ].script_pub_key .size ;
434
+ }
435
+ sha256_Raw (script_buf , 35 * context -> metadata .input_count , taproot_cache -> sha_scriptpubkeys );
436
+ free (script_buf );
437
+
438
+ //sha_sequences
439
+ sha256_Init (& sha_256_ctx );
440
+ for (uint32_t idx = 0 ; idx < context -> metadata .input_count ; idx ++ ) {
441
+ sha256_Update (& sha_256_ctx , (uint8_t * )& context -> inputs [idx ].sequence , 4 );
442
+ }
443
+
444
+ sha256_Final (& sha_256_ctx , taproot_cache -> sha_sequences );
445
+
446
+ //sha_outputs
447
+ // calculate capacity
448
+ uint32_t capacity = 0 ;
449
+ uint32_t size = 0 ;
450
+ while (size < context -> metadata .output_count )
451
+ {
452
+ capacity += 8 ;
453
+ capacity += context -> outputs [size ].script_pub_key .size ;
454
+ size ++ ;
455
+ }
456
+
457
+ uint8_t * output_buf = malloc (capacity );
458
+ memzero (output_buf , capacity );
459
+
460
+ for (uint32_t idx = 0 , len = 0 ; idx < context -> metadata .output_count ; idx ++ ) {
461
+ memcpy ( output_buf + len , (uint8_t * )& context -> outputs [idx ].value , 8 );
462
+ len += 8 ;
463
+ memcpy ( output_buf + len , context -> outputs [idx ].script_pub_key .bytes , context -> outputs [idx ].script_pub_key .size );
464
+ len += context -> outputs [idx ].script_pub_key .size ;
465
+ }
466
+ sha256_Raw (output_buf , capacity , taproot_cache -> sha_outputs );
467
+ free (output_buf );
468
+
469
+ taproot_cache -> filled = true;
470
+ }
471
+
401
472
bool btc_digest_input (const btc_txn_context_t * context ,
402
473
const uint32_t index ,
403
474
uint8_t * digest ) {
@@ -412,8 +483,258 @@ bool btc_digest_input(const btc_txn_context_t *context,
412
483
} else if (SCRIPT_TYPE_P2PKH == type ) {
413
484
// p2pkh digest calculation; has not failure case
414
485
calculate_p2pkh_digest (context , index , digest );
486
+ } else if (SCRIPT_TYPE_P2SH == type ) {
487
+ status = calculate_p2wpkh_in_p2sh_digest (context , index , digest );
488
+ } else if (SCRIPT_TYPE_P2TR == type ) {
489
+ status = calculate_p2tr_digest (context , index , digest );
415
490
} else {
416
491
status = false;
417
492
}
418
493
return status ;
419
494
}
495
+
496
+ #include "ecdsa.h"
497
+ #include "secp256k1.h"
498
+ #include "memzero.h"
499
+
500
+ typedef struct {
501
+ uint8_t secret_key [32 ]; // Private key
502
+ uint8_t public_key [32 ]; // X-coordinate of public key (for BIP340)
503
+ uint8_t has_even_y ; // Whether Y-coordinate is even
504
+ } bip340_keypair_t ;
505
+
506
+ int keypair_create (bip340_keypair_t * keypair , const uint8_t * private_key_bytes ) {
507
+ // 1. Validate private key (must be in range [1, n-1])
508
+ bignum256 sk ;
509
+ bn_read_be (private_key_bytes , & sk );
510
+
511
+ // Check if sk is valid (not zero and less than curve order)
512
+ if (bn_is_zero (& sk )) {
513
+ return -1 ;
514
+ }
515
+
516
+ if (!bn_is_less (& sk , & secp256k1 .order )) {
517
+ return -1 ;
518
+ }
519
+
520
+ // 2. Copy private key
521
+ memcpy (keypair -> secret_key , private_key_bytes , 32 );
522
+
523
+ // 3. Calculate public key: P = sk * G
524
+ curve_point pub ;
525
+ scalar_multiply (& secp256k1 , & sk , & pub );
526
+
527
+ // 4. Convert to affine coordinates and get x-coordinate
528
+ bignum256 x , y ;
529
+ bn_inverse (& pub .z , & secp256k1 .prime );
530
+ bignum256 z_squared ;
531
+ bn_multiply (& pub .z , & pub .z , & secp256k1 .prime );
532
+ bn_multiply (& pub .x , & z_squared , & secp256k1 .prime );
533
+ bn_mod (& pub .x , & secp256k1 .prime );
534
+
535
+ // Store x-coordinate
536
+ bn_write_be (& pub .x , keypair -> public_key );
537
+
538
+ // 5. Check if Y is even (needed for BIP340)
539
+ bn_multiply (& pub .z , & z_squared , & secp256k1 .prime );
540
+ bn_multiply (& pub .y , & pub .z , & secp256k1 .prime );
541
+ bn_mod (& pub .y , & secp256k1 .prime );
542
+ keypair -> has_even_y = !bn_is_odd (& pub .y );
543
+
544
+ // Clear sensitive data
545
+ memzero (& sk , sizeof (sk ));
546
+ memzero (& pub , sizeof (pub ));
547
+
548
+ return 0 ;
549
+ }
550
+
551
+ #include "hasher.h"
552
+ #include "hmac.h"
553
+
554
+ // BIP340 tagged hash implementation
555
+ void bip340_tagged_hash (const char * tag , uint8_t * out ,
556
+ const uint8_t * data , size_t data_len ) {
557
+ uint8_t tag_hash [32 ];
558
+ Hasher hasher ;
559
+
560
+ // Hash the tag
561
+ hasher_Init (& hasher , HASHER_SHA2 );
562
+ hasher_Update (& hasher , (const uint8_t * )tag , strlen (tag ));
563
+ hasher_Final (& hasher , tag_hash );
564
+
565
+ // tagged_hash = SHA256(SHA256(tag) || SHA256(tag) || data)
566
+ hasher_Init (& hasher , HASHER_SHA2 );
567
+ hasher_Update (& hasher , tag_hash , 32 );
568
+ hasher_Update (& hasher , tag_hash , 32 );
569
+ hasher_Update (& hasher , data , data_len );
570
+ hasher_Final (& hasher , out );
571
+ }
572
+
573
+ int schnorrsig_sign32 (uint8_t * signature_bytes ,
574
+ const uint8_t * digest ,
575
+ const bip340_keypair_t * keypair ,
576
+ const uint8_t * auxiliary_data ) {
577
+ bignum256 sk , e , k , r_x ;
578
+ curve_point R ;
579
+ uint8_t aux [32 ];
580
+ uint8_t nonce_data [32 + 32 + 32 + 32 ]; // sk || P.x || msg || aux
581
+ uint8_t nonce_hash [32 ];
582
+
583
+ // 1. Load private key
584
+ bn_read_be (keypair -> secret_key , & sk );
585
+
586
+ // 2. Adjust private key if public key has odd Y
587
+ // BIP340 requires: if has_odd_y(P), then sk = n - sk
588
+ if (!keypair -> has_even_y ) {
589
+ bn_subtract (& secp256k1 .order , & sk , & sk );
590
+ bn_mod (& sk , & secp256k1 .order );
591
+ }
592
+
593
+ // 3. Prepare auxiliary data (use zeros if NULL)
594
+ if (auxiliary_data == NULL ) {
595
+ memzero (aux , 32 );
596
+ } else {
597
+ memcpy (aux , auxiliary_data , 32 );
598
+ }
599
+
600
+ // 4. Generate deterministic nonce k (BIP340)
601
+ // k = tagged_hash("BIP0340/nonce", sk || P.x || msg || aux)
602
+
603
+ // First, XOR auxiliary data with private key for additional randomness
604
+ uint8_t sk_bytes [32 ];
605
+ bn_write_be (& sk , sk_bytes );
606
+ for (int i = 0 ; i < 32 ; i ++ ) {
607
+ sk_bytes [i ] ^= aux [i ];
608
+ }
609
+
610
+ // Build nonce input: sk || P.x || msg || aux
611
+ memcpy (nonce_data , sk_bytes , 32 );
612
+ memcpy (nonce_data + 32 , keypair -> public_key , 32 );
613
+ memcpy (nonce_data + 64 , digest , 32 );
614
+ memcpy (nonce_data + 96 , aux , 32 );
615
+
616
+ // Generate nonce
617
+ bip340_tagged_hash ("BIP0340/nonce" , nonce_hash , nonce_data , 128 );
618
+ bn_read_be (nonce_hash , & k );
619
+ bn_mod (& k , & secp256k1 .order );
620
+
621
+ // Ensure k != 0
622
+ if (bn_is_zero (& k )) {
623
+ return -1 ;
624
+ }
625
+
626
+ // 5. Calculate R = k*G
627
+ scalar_multiply (& secp256k1 , & k , & R );
628
+
629
+ // 6. Convert R to affine coordinates and get x-coordinate
630
+ bignum256 z_inv , z_inv_squared ;
631
+ bn_inverse (& R .z , & secp256k1 .prime );
632
+ bn_multiply (& z_inv , & z_inv , & secp256k1 .prime );
633
+ bn_multiply (& R .x , & z_inv , & secp256k1 .prime );
634
+ bn_mod (& R .x , & secp256k1 .prime );
635
+
636
+ // Also get R.y for parity check
637
+ bn_multiply (& z_inv , & z_inv_squared , & secp256k1 .prime );
638
+ bn_multiply (& R .y , & z_inv , & secp256k1 .prime );
639
+ bn_mod (& R .y , & secp256k1 .prime );
640
+
641
+ // 7. If R.y is odd, negate k
642
+ if (bn_is_odd (& R .y )) {
643
+ bn_subtract (& secp256k1 .order , & k , & k );
644
+ bn_mod (& k , & secp256k1 .order );
645
+ }
646
+
647
+ // 8. Store R.x in signature (first 32 bytes)
648
+ bn_write_be (& R .x , signature_bytes );
649
+
650
+ // 9. Calculate e = tagged_hash("BIP0340/challenge", R.x || P.x || m)
651
+ uint8_t challenge_data [32 + 32 + 32 ]; // R.x || P.x || msg
652
+ uint8_t challenge_hash [32 ];
653
+
654
+ // Build challenge input: R.x || P.x || msg
655
+ memcpy (challenge_data , signature_bytes , 32 ); // R.x (already stored)
656
+ memcpy (challenge_data + 32 , keypair -> public_key , 32 ); // P.x
657
+ memcpy (challenge_data + 64 , digest , 32 ); // message
658
+
659
+ // Generate challenge e
660
+ bip340_tagged_hash ("BIP0340/challenge" , challenge_hash , challenge_data , 96 );
661
+ bn_read_be (challenge_hash , & e );
662
+ bn_mod (& e , & secp256k1 .order );
663
+
664
+ // 10. Calculate s = k + e*sk mod n
665
+ bignum256 s , temp ;
666
+ bn_multiply (& e , & sk , & secp256k1 .order ); // temp = e*sk mod n
667
+ bn_copy (& e , & temp );
668
+ bn_addmod (& k , & temp , & secp256k1 .order ); // s = k + e*sk mod n
669
+ bn_copy (& k , & s );
670
+
671
+ // 11. Store s in signature (second 32 bytes)
672
+ bn_write_be (& s , signature_bytes + 32 );
673
+
674
+ // 12. Verify signature is valid (optional but recommended)
675
+ // This catches any implementation errors
676
+ uint8_t verify_result [32 ];
677
+ if (manual_schnorrsig_verify (signature_bytes , digest , keypair -> public_key ) !=
678
+ 0 ) {
679
+ memzero (signature_bytes , 64 );
680
+ return -1 ;
681
+ }
682
+
683
+ // Clear all sensitive data
684
+ memzero (& sk , sizeof (sk ));
685
+ memzero (& k , sizeof (k ));
686
+ memzero (& e , sizeof (e ));
687
+ memzero (& s , sizeof (s ));
688
+ memzero (& R , sizeof (R ));
689
+ memzero (sk_bytes , sizeof (sk_bytes ));
690
+ memzero (nonce_data , sizeof (nonce_data ));
691
+ memzero (nonce_hash , sizeof (nonce_hash ));
692
+ memzero (challenge_data , sizeof (challenge_data ));
693
+ memzero (challenge_hash , sizeof (challenge_hash ));
694
+
695
+ return 0 ;
696
+ }
697
+
698
+ // Helper function for verification (optional)
699
+ int manual_schnorrsig_verify (const uint8_t * signature ,
700
+ const uint8_t * digest ,
701
+ const uint8_t * public_key_x ) {
702
+ bignum256 r , s , e , x ;
703
+ curve_point R , P , sG , eP ;
704
+
705
+ // Load r and s from signature
706
+ bn_read_be (signature , & r );
707
+ bn_read_be (signature + 32 , & s );
708
+
709
+ // Check r and s are in valid range
710
+ if (!bn_is_less (& r , & secp256k1 .prime ) || !bn_is_less (& s , & secp256k1 .order )) {
711
+ return -1 ;
712
+ }
713
+
714
+ // Load public key x-coordinate
715
+ bn_read_be (public_key_x , & x );
716
+
717
+ // Compute e = tagged_hash("BIP0340/challenge", r || P.x || m)
718
+ uint8_t challenge_data [96 ];
719
+ uint8_t challenge_hash [32 ];
720
+ memcpy (challenge_data , signature , 32 ); // r
721
+ memcpy (challenge_data + 32 , public_key_x , 32 ); // P.x
722
+ memcpy (challenge_data + 64 , digest , 32 ); // message
723
+
724
+ bip340_tagged_hash ("BIP0340/challenge" , challenge_hash , challenge_data , 96 );
725
+ bn_read_be (challenge_hash , & e );
726
+ bn_mod (& e , & secp256k1 .order );
727
+
728
+ // Recover P from x-coordinate (lift_x)
729
+ if (point_lift_x (& secp256k1 , & P , & x ) != 0 ) {
730
+ return -1 ;
731
+ }
732
+
733
+ // Compute R = s*G - e*P
734
+ scalar_multiply (& secp256k1 , & s , & sG ); // s*G
735
+ scalar_multiply (& secp256k1 , & e , & eP ); // e*P (using recovered P)
736
+ point_multiply (& secp256k1 , & e , & P , & eP );
737
+
738
+ // Negate eP
739
+ // bn_subtract(&secp256k1)
740
+ }
0 commit comments