32
32
33
33
from .secp256k1 import antiklepto_host_commit , antiklepto_verify
34
34
35
- from ..communication .generated import hww_pb2 as hww
36
- from ..communication .generated import eth_pb2 as eth
37
- from ..communication .generated import btc_pb2 as btc
38
- from ..communication .generated import cardano_pb2 as cardano
39
- from ..communication .generated import mnemonic_pb2 as mnemonic
40
- from ..communication .generated import bitbox02_system_pb2 as bitbox02_system
41
- from ..communication .generated import backup_commands_pb2 as backup
42
- from ..communication .generated import common_pb2 as common
43
- from ..communication .generated import keystore_pb2 as keystore
44
- from ..communication .generated import antiklepto_pb2 as antiklepto
45
-
46
- # pylint: disable=unused-import
47
- # We export it in __init__.py
48
- from ..communication .generated import system_pb2 as system
35
+ try :
36
+ from ..communication .generated import hww_pb2 as hww
37
+ from ..communication .generated import eth_pb2 as eth
38
+ from ..communication .generated import btc_pb2 as btc
39
+ from ..communication .generated import cardano_pb2 as cardano
40
+ from ..communication .generated import mnemonic_pb2 as mnemonic
41
+ from ..communication .generated import bitbox02_system_pb2 as bitbox02_system
42
+ from ..communication .generated import backup_commands_pb2 as backup
43
+ from ..communication .generated import common_pb2 as common
44
+ from ..communication .generated import keystore_pb2 as keystore
45
+ from ..communication .generated import antiklepto_pb2 as antiklepto
46
+ import google .protobuf .empty_pb2
47
+
48
+ # pylint: disable=unused-import
49
+ # We export it in __init__.py
50
+ from ..communication .generated import system_pb2 as system
51
+ except ModuleNotFoundError :
52
+ print ("Run `make py` to generate the protobuf messages" )
53
+ sys .exit ()
49
54
50
55
try :
51
56
# Optional rlp dependency only needed to sign ethereum transactions.
@@ -674,6 +679,40 @@ def electrum_encryption_key(self, keypath: Sequence[int]) -> str:
674
679
)
675
680
return self ._msg_query (request ).electrum_encryption_key .key
676
681
682
+ def bip85_bip39 (self ) -> None :
683
+ """Invokes the BIP85-BIP39 workflow on the device"""
684
+ self ._require_atleast (semver .VersionInfo (9 , 18 , 0 ))
685
+
686
+ # pylint: disable=no-member
687
+ request = hww .Request ()
688
+ request .bip85 .CopyFrom (
689
+ keystore .BIP85Request (
690
+ bip39 = google .protobuf .empty_pb2 .Empty (),
691
+ )
692
+ )
693
+ response = self ._msg_query (request , expected_response = "bip85" ).bip85
694
+ assert response .WhichOneof ("app" ) == "bip39"
695
+
696
+ def bip85_ln (self ) -> bytes :
697
+ """
698
+ Generates and returns a mnemonic for a hot Lightning wallet from the device using BIP-85.
699
+ """
700
+ self ._require_atleast (semver .VersionInfo (9 , 17 , 0 ))
701
+
702
+ # Only account_number=0 is allowed for now.
703
+ account_number = 0
704
+
705
+ # pylint: disable=no-member
706
+ request = hww .Request ()
707
+ request .bip85 .CopyFrom (
708
+ keystore .BIP85Request (
709
+ ln = keystore .BIP85Request .AppLn (account_number = account_number ),
710
+ )
711
+ )
712
+ response = self ._msg_query (request , expected_response = "bip85" ).bip85
713
+ assert response .WhichOneof ("app" ) == "ln"
714
+ return response .ln
715
+
677
716
def enable_mnemonic_passphrase (self ) -> None :
678
717
"""
679
718
Enable the bip39 passphrase.
@@ -753,28 +792,17 @@ def eth_sign(self, transaction: bytes, keypath: Sequence[int], chain_id: int = 1
753
792
"""
754
793
transaction should be given as a full rlp encoded eth transaction.
755
794
"""
756
- nonce , gas_price , gas_limit , recipient , value , data , _ , _ , _ = rlp .decode (transaction )
757
- request = eth .ETHRequest ()
758
- # pylint: disable=no-member
759
- request .sign .CopyFrom (
760
- eth .ETHSignRequest (
761
- coin = self ._eth_coin (chain_id ),
762
- chain_id = chain_id ,
763
- keypath = keypath ,
764
- nonce = nonce ,
765
- gas_price = gas_price ,
766
- gas_limit = gas_limit ,
767
- recipient = recipient ,
768
- value = value ,
769
- data = data ,
770
- )
771
- )
795
+ is_eip1559 = transaction .startswith (b"\x02 " )
772
796
773
- supports_antiklepto = self .version >= semver .VersionInfo (9 , 5 , 0 )
774
- if supports_antiklepto :
797
+ def handle_antiklepto (request : eth .ETHRequest ) -> bytes :
775
798
host_nonce = os .urandom (32 )
799
+ if is_eip1559 :
800
+ request .sign_eip1559 .host_nonce_commitment .commitment = antiklepto_host_commit (
801
+ host_nonce
802
+ )
803
+ else :
804
+ request .sign .host_nonce_commitment .commitment = antiklepto_host_commit (host_nonce )
776
805
777
- request .sign .host_nonce_commitment .commitment = antiklepto_host_commit (host_nonce )
778
806
signer_commitment = self ._eth_msg_query (
779
807
request , expected_response = "antiklepto_signer_commitment"
780
808
).antiklepto_signer_commitment .commitment
@@ -792,6 +820,64 @@ def eth_sign(self, transaction: bytes, keypath: Sequence[int], chain_id: int = 1
792
820
793
821
return signature
794
822
823
+ if is_eip1559 :
824
+ self ._require_atleast (semver .VersionInfo (9 , 16 , 0 ))
825
+ (
826
+ decoded_chain_id ,
827
+ nonce ,
828
+ priority_fee ,
829
+ max_fee ,
830
+ gas_limit ,
831
+ recipient ,
832
+ value ,
833
+ data ,
834
+ _ ,
835
+ _ ,
836
+ _ ,
837
+ ) = rlp .decode (transaction [1 :])
838
+ decoded_chain_id_int = int .from_bytes (decoded_chain_id , byteorder = "big" )
839
+ if decoded_chain_id_int != chain_id :
840
+ raise Exception (
841
+ f"chainID argument ({ chain_id } ) does not match chainID encoded in transaction ({ decoded_chain_id_int } )"
842
+ )
843
+ request = eth .ETHRequest ()
844
+ # pylint: disable=no-member
845
+ request .sign_eip1559 .CopyFrom (
846
+ eth .ETHSignEIP1559Request (
847
+ chain_id = chain_id ,
848
+ keypath = keypath ,
849
+ nonce = nonce ,
850
+ max_priority_fee_per_gas = priority_fee ,
851
+ max_fee_per_gas = max_fee ,
852
+ gas_limit = gas_limit ,
853
+ recipient = recipient ,
854
+ value = value ,
855
+ data = data ,
856
+ )
857
+ )
858
+ return handle_antiklepto (request )
859
+
860
+ nonce , gas_price , gas_limit , recipient , value , data , _ , _ , _ = rlp .decode (transaction )
861
+ request = eth .ETHRequest ()
862
+ # pylint: disable=no-member
863
+ request .sign .CopyFrom (
864
+ eth .ETHSignRequest (
865
+ coin = self ._eth_coin (chain_id ),
866
+ chain_id = chain_id ,
867
+ keypath = keypath ,
868
+ nonce = nonce ,
869
+ gas_price = gas_price ,
870
+ gas_limit = gas_limit ,
871
+ recipient = recipient ,
872
+ value = value ,
873
+ data = data ,
874
+ )
875
+ )
876
+
877
+ supports_antiklepto = self .version >= semver .VersionInfo (9 , 5 , 0 )
878
+ if supports_antiklepto :
879
+ return handle_antiklepto (request )
880
+
795
881
return self ._eth_msg_query (request , expected_response = "sign" ).sign .signature
796
882
797
883
def eth_sign_msg (self , msg : bytes , keypath : Sequence [int ], chain_id : int = 1 ) -> bytes :
@@ -945,7 +1031,10 @@ def get_value(
945
1031
return value
946
1032
if typ .type == eth .ETHSignTypedMessageRequest .DataType .UINT :
947
1033
if isinstance (value , str ):
948
- value = int (value )
1034
+ if value [:2 ].lower () == "0x" :
1035
+ value = int (value [2 :], 16 )
1036
+ else :
1037
+ value = int (value )
949
1038
assert isinstance (value , int )
950
1039
return value .to_bytes (typ .size , "big" )
951
1040
if typ .type == eth .ETHSignTypedMessageRequest .DataType .INT :
0 commit comments