@@ -104,10 +104,11 @@ public class CardEdge extends javacard.framework.Applet {
104
104
// 0.12-0.4: add reset to factory support
105
105
// 0.12-0.5: add support for personalisation PKI
106
106
// 0.14-0.1: add Schnorr signature support (beta)
107
+ // 0.14-0.2: Schnorr signature - add option to bypass key tweaking (beta)
107
108
private final static byte PROTOCOL_MAJOR_VERSION = (byte ) 0 ;
108
109
private final static byte PROTOCOL_MINOR_VERSION = (byte ) 14 ;
109
110
private final static byte APPLET_MAJOR_VERSION = (byte ) 0 ;
110
- private final static byte APPLET_MINOR_VERSION = (byte ) 1 ;
111
+ private final static byte APPLET_MINOR_VERSION = (byte ) 2 ;
111
112
112
113
// Maximum number of keys handled by the Cardlet
113
114
private final static byte MAX_NUM_KEYS = (byte ) 16 ;
@@ -2475,14 +2476,14 @@ private short SignTransactionHash(APDU apdu, byte[] buffer){
2475
2476
* A private key must first be available, either from a keyslot or
2476
2477
* derived from a BIP32 seed using getBIP32ExtendedKey().
2477
2478
*
2478
- * The tweaked key is then stored in the same keyslot and available next for signing.
2479
+ * The tweaked key is then stored in a dedicated keyslot and available next for schnorr signing.
2479
2480
* The function returns the corresponding public key coordx, self-signed
2480
2481
*
2481
- * TODO: allows to bypass tweaking by providing empty tweak?
2482
+ * If P2 parameter is not 0x00, the tweaking is bypassed.
2482
2483
*
2483
2484
* ins: 0x7C
2484
2485
* p1: key number or 0xFF for the last derived Bip32 extended key
2485
- * p2: 0x00
2486
+ * p2: 0x00 for key tweak, 0x01 to bypass tweak
2486
2487
* data: [tweak_size (1b) | tweak data (32b)]
2487
2488
*
2488
2489
* return: [coordx_size(2b) | coordx | sig_size(2b) | authentikey_sig]
@@ -2498,17 +2499,13 @@ private short TaprootTweakPrivateKey(APDU apdu, byte[] buffer){
2498
2499
if ( (key_nb !=(byte )0xFF ) && ((key_nb < 0 ) || (key_nb >= MAX_NUM_KEYS )) )
2499
2500
ISOException .throwIt (SW_INCORRECT_P1 );
2500
2501
2502
+ // P2 can be used to bypass tweaking and copy private as is
2503
+ byte p2 = buffer [ISO7816 .OFFSET_P2 ];
2504
+
2501
2505
// check whether the seed is initialized
2502
2506
if (key_nb ==(byte )0xFF && !bip32_seeded )
2503
2507
ISOException .throwIt (SW_BIP32_UNINITIALIZED_SEED );
2504
2508
2505
- // tweak vector should be exactly 32bytes, thus 33 bytes in total
2506
- short bytesLeft = Util .makeShort ((byte ) 0x00 , buffer [ISO7816 .OFFSET_LC ]);
2507
- if (bytesLeft < (byte )33 )
2508
- ISOException .throwIt (ISO7816 .SW_WRONG_LENGTH );
2509
- if (buffer [ISO7816 .OFFSET_CDATA ] != (byte )32 )
2510
- ISOException .throwIt (ISO7816 .SW_WRONG_LENGTH );
2511
-
2512
2509
// get privkey from either bip32 derivation or single privkey
2513
2510
ECPrivateKey privkey ;
2514
2511
if (key_nb ==(byte )0xFF )
@@ -2524,47 +2521,65 @@ private short TaprootTweakPrivateKey(APDU apdu, byte[] buffer){
2524
2521
ISOException .throwIt (SW_INCORRECT_ALG );
2525
2522
}
2526
2523
2527
- // compute pubkey corresponding to private key
2528
- keyAgreement . init (( ECPrivateKey ) privkey ) ;
2529
- keyAgreement . generateSecret ( Secp256k1 . SECP256K1 , Secp256k1 . OFFSET_SECP256K1_G , ( short ) 65 , recvBuffer , ( short ) 0 ); //pubkey in uncompressed form (65bytes)
2524
+ short seckeyOffset ;
2525
+ short buffer_offset ;
2526
+ if ( p2 ==( byte ) 0x00 ){
2530
2527
2531
- // xor pubkey coordx with tweak vector
2532
- short recvBuffer_offset = (short )0x1 ;
2533
- short buffer_offset = (short )(ISO7816 .OFFSET_CDATA +1 );
2534
- short i ;
2535
- for (i = (short )0 ; i < (short )32 ; i ++){
2536
- recvBuffer [(short )(recvBuffer_offset +i )] = (byte ) (recvBuffer [(short )(recvBuffer_offset +i )] ^ buffer [(short )(buffer_offset +i )]);
2537
- }
2528
+ // tweak vector should be exactly 32bytes, thus 33 bytes in total
2529
+ short bytesLeft = Util .makeShort ((byte ) 0x00 , buffer [ISO7816 .OFFSET_LC ]);
2530
+ if (bytesLeft < (byte )33 )
2531
+ ISOException .throwIt (ISO7816 .SW_WRONG_LENGTH );
2532
+ if (buffer [ISO7816 .OFFSET_CDATA ] != (byte )32 )
2533
+ ISOException .throwIt (ISO7816 .SW_WRONG_LENGTH );
2538
2534
2539
- // compute tagged hash
2540
- // t = int_from_bytes(tagged_hash("TapTweak", bytes_from_int(x(P)) + h))
2541
- schnorr_hash_tag (tags , (short )41 , (short )8 , recvBuffer , recvBuffer_offset , (short )32 , recvBuffer , (short )65 );
2542
- // fails if t >= SECP256K1_ORDER (low probability)
2543
- if (!Biginteger .lessThan (recvBuffer , (short )65 , Secp256k1 .SECP256K1 , Secp256k1 .OFFSET_SECP256K1_R , (short )32 )){
2544
- ISOException .throwIt (SW_TAPROOT_TWEAK_ERROR );
2545
- }
2546
2535
2547
- //seckey = seckey0 if has_even_y(P) else SECP256K1_ORDER - seckey0
2548
- short seckeyOffset = (short ) 97 ;
2549
- if ( (recvBuffer [(byte )64 ] % 0x02 ) == 0x00 ) {
2550
- privkey .getS (recvBuffer , seckeyOffset );
2551
- } else {
2552
- privkey .getS (recvBuffer , (short )129 );
2553
- // copy SECP256K1_ORDER
2554
- Util .arrayCopyNonAtomic (Secp256k1 .SECP256K1 , Secp256k1 .OFFSET_SECP256K1_R , recvBuffer , seckeyOffset , (short )32 );
2555
- // SECP256K1_ORDER - seckey0
2556
- Biginteger .subtract (recvBuffer , seckeyOffset , recvBuffer , (short )129 , (short ) 32 );
2557
- }
2536
+ // compute pubkey corresponding to private key
2537
+ keyAgreement .init ((ECPrivateKey )privkey );
2538
+ keyAgreement .generateSecret (Secp256k1 .SECP256K1 , Secp256k1 .OFFSET_SECP256K1_G , (short ) 65 , recvBuffer , (short )0 ); //pubkey in uncompressed form (65bytes)
2558
2539
2559
- // compute ((seckey + t) % SECP256K1_ORDER)
2560
- if (Biginteger .add_carry (recvBuffer , seckeyOffset , recvBuffer , (short )65 , (short ) 32 )){
2561
- // in case of final carry, we must substract SECP256K1_R
2562
- Biginteger .subtract (recvBuffer , seckeyOffset , Secp256k1 .SECP256K1 , Secp256k1 .OFFSET_SECP256K1_R , (short )32 );
2563
- } else {
2564
- // in the unlikely case where SECP256K1_R <= (seckey + t) <2^256
2565
- if (!Biginteger .lessThan (recvBuffer , seckeyOffset , Secp256k1 .SECP256K1 , Secp256k1 .OFFSET_SECP256K1_R , (short )32 )){
2566
- Biginteger .subtract (recvBuffer , seckeyOffset , Secp256k1 .SECP256K1 , Secp256k1 .OFFSET_SECP256K1_R , (short )32 );
2540
+ // xor pubkey coordx with tweak vector
2541
+ short recvBuffer_offset = (short )0x1 ;
2542
+ buffer_offset = (short )(ISO7816 .OFFSET_CDATA +1 );
2543
+ short i ;
2544
+ for (i = (short )0 ; i < (short )32 ; i ++){
2545
+ recvBuffer [(short )(recvBuffer_offset +i )] = (byte ) (recvBuffer [(short )(recvBuffer_offset +i )] ^ buffer [(short )(buffer_offset +i )]);
2546
+ }
2547
+
2548
+ // compute tagged hash
2549
+ // t = int_from_bytes(tagged_hash("TapTweak", bytes_from_int(x(P)) + h))
2550
+ schnorr_hash_tag (tags , (short )41 , (short )8 , recvBuffer , recvBuffer_offset , (short )32 , recvBuffer , (short )65 );
2551
+ // fails if t >= SECP256K1_ORDER (low probability)
2552
+ if (!Biginteger .lessThan (recvBuffer , (short )65 , Secp256k1 .SECP256K1 , Secp256k1 .OFFSET_SECP256K1_R , (short )32 )){
2553
+ ISOException .throwIt (SW_TAPROOT_TWEAK_ERROR );
2554
+ }
2555
+
2556
+ //seckey = seckey0 if has_even_y(P) else SECP256K1_ORDER - seckey0
2557
+ seckeyOffset = (short ) 97 ;
2558
+ if ( (recvBuffer [(byte )64 ] % 0x02 ) == 0x00 ) {
2559
+ privkey .getS (recvBuffer , seckeyOffset );
2560
+ } else {
2561
+ privkey .getS (recvBuffer , (short )129 );
2562
+ // copy SECP256K1_ORDER
2563
+ Util .arrayCopyNonAtomic (Secp256k1 .SECP256K1 , Secp256k1 .OFFSET_SECP256K1_R , recvBuffer , seckeyOffset , (short )32 );
2564
+ // SECP256K1_ORDER - seckey0
2565
+ Biginteger .subtract (recvBuffer , seckeyOffset , recvBuffer , (short )129 , (short ) 32 );
2567
2566
}
2567
+
2568
+ // compute ((seckey + t) % SECP256K1_ORDER)
2569
+ if (Biginteger .add_carry (recvBuffer , seckeyOffset , recvBuffer , (short )65 , (short ) 32 )){
2570
+ // in case of final carry, we must substract SECP256K1_R
2571
+ Biginteger .subtract (recvBuffer , seckeyOffset , Secp256k1 .SECP256K1 , Secp256k1 .OFFSET_SECP256K1_R , (short )32 );
2572
+ } else {
2573
+ // in the unlikely case where SECP256K1_R <= (seckey + t) <2^256
2574
+ if (!Biginteger .lessThan (recvBuffer , seckeyOffset , Secp256k1 .SECP256K1 , Secp256k1 .OFFSET_SECP256K1_R , (short )32 )){
2575
+ Biginteger .subtract (recvBuffer , seckeyOffset , Secp256k1 .SECP256K1 , Secp256k1 .OFFSET_SECP256K1_R , (short )32 );
2576
+ }
2577
+ }
2578
+
2579
+ } else {
2580
+ // just copy privkey as is
2581
+ seckeyOffset = (short ) 97 ;
2582
+ privkey .getS (recvBuffer , seckeyOffset );
2568
2583
}
2569
2584
2570
2585
// save tweaked secret key
0 commit comments