diff --git a/api/firmware/btc.go b/api/firmware/btc.go index 729492a..327623d 100644 --- a/api/firmware/btc.go +++ b/api/firmware/btc.go @@ -353,6 +353,9 @@ func (device *Device) nonAtomicBTCSign( formatUnit messages.BTCSignInitRequest_FormatUnit, ) (*BTCSignResult, error) { generatedOutputs := map[int][]byte{} + if !device.version.AtLeast(semver.NewSemVer(9, 4, 0)) { + return nil, UnsupportedError("9.4.0") + } if !device.version.AtLeast(semver.NewSemVer(9, 10, 0)) { for _, sc := range scriptConfigs { if isTaproot(sc) { @@ -369,8 +372,6 @@ func (device *Device) nonAtomicBTCSign( } } - supportsAntiklepto := device.version.AtLeast(semver.NewSemVer(9, 4, 0)) - containsSilentPaymentOutputs := false for _, output := range tx.Outputs { if output.SilentPayment != nil { @@ -415,7 +416,7 @@ func (device *Device) nonAtomicBTCSign( inputIsSchnorr := isTaproot(scriptConfigs[input.ScriptConfigIndex]) // Anti-Klepto protocol not supported yet for Schnorr signatures. - performAntiklepto := supportsAntiklepto && isInputsPass2 && !inputIsSchnorr + performAntiklepto := isInputsPass2 && !inputIsSchnorr var hostNonce []byte if performAntiklepto { @@ -555,8 +556,9 @@ func (device *Device) nonAtomicBTCSign( } } -// BTCSign signs a bitcoin or bitcoin-like transaction. The previous transactions of the inputs -// need to be provided if `BTCSignNeedsPrevTxs()` returns true. +// BTCSign signs a bitcoin or bitcoin-like transaction. Firmware v9.4.0 or newer is required to +// ensure anti-klepto protection for ECDSA signatures. The previous transactions of the inputs need +// to be provided if `BTCSignNeedsPrevTxs()` returns true. func (device *Device) BTCSign( coin messages.BTCCoin, scriptConfigs []*messages.BTCScriptConfigWithKeypath, @@ -655,23 +657,16 @@ func (device *Device) nonAtomicBTCSignMessage( if isTaproot(scriptConfig) { return nil, errp.New("taproot not supported") } - if !device.version.AtLeast(semver.NewSemVer(9, 2, 0)) { - return nil, UnsupportedError("9.2.0") + if !device.version.AtLeast(semver.NewSemVer(9, 5, 0)) { + return nil, UnsupportedError("9.5.0") } - supportsAntiklepto := device.version.AtLeast(semver.NewSemVer(9, 5, 0)) - var hostNonceCommitment *messages.AntiKleptoHostNonceCommitment - var hostNonce []byte - - if supportsAntiklepto { - var err error - hostNonce, err = generateHostNonce() - if err != nil { - return nil, err - } - hostNonceCommitment = &messages.AntiKleptoHostNonceCommitment{ - Commitment: antikleptoHostCommit(hostNonce), - } + hostNonce, err := generateHostNonce() + if err != nil { + return nil, err + } + hostNonceCommitment := &messages.AntiKleptoHostNonceCommitment{ + Commitment: antikleptoHostCommit(hostNonce), } request := &messages.BTCRequest{ @@ -689,42 +684,33 @@ func (device *Device) nonAtomicBTCSignMessage( return nil, err } - var signature []byte - if supportsAntiklepto { - signerCommitment, ok := response.Response.(*messages.BTCResponse_AntikleptoSignerCommitment) - if !ok { - return nil, errp.New("unexpected response") - } - response, err := device.nonAtomicQueryBTC(&messages.BTCRequest{ - Request: &messages.BTCRequest_AntikleptoSignature{ - AntikleptoSignature: &messages.AntiKleptoSignatureRequest{ - HostNonce: hostNonce, - }, + signerCommitment, ok := response.Response.(*messages.BTCResponse_AntikleptoSignerCommitment) + if !ok { + return nil, errp.New("unexpected response") + } + response, err = device.nonAtomicQueryBTC(&messages.BTCRequest{ + Request: &messages.BTCRequest_AntikleptoSignature{ + AntikleptoSignature: &messages.AntiKleptoSignatureRequest{ + HostNonce: hostNonce, }, - }) - if err != nil { - return nil, err - } + }, + }) + if err != nil { + return nil, err + } - signResponse, ok := response.Response.(*messages.BTCResponse_SignMessage) - if !ok { - return nil, errp.New("unexpected response") - } - signature = signResponse.SignMessage.Signature - err = antikleptoVerify( - hostNonce, - signerCommitment.AntikleptoSignerCommitment.Commitment, - signature[:64], - ) - if err != nil { - return nil, err - } - } else { - signResponse, ok := response.Response.(*messages.BTCResponse_SignMessage) - if !ok { - return nil, errp.New("unexpected response") - } - signature = signResponse.SignMessage.Signature + signResponse, ok := response.Response.(*messages.BTCResponse_SignMessage) + if !ok { + return nil, errp.New("unexpected response") + } + signature := signResponse.SignMessage.Signature + err = antikleptoVerify( + hostNonce, + signerCommitment.AntikleptoSignerCommitment.Commitment, + signature[:64], + ) + if err != nil { + return nil, err } sig, recID := signature[:64], signature[64] @@ -738,7 +724,8 @@ func (device *Device) nonAtomicBTCSignMessage( }, nil } -// BTCSignMessage signs a Bitcoin message. +// BTCSignMessage signs a Bitcoin message. Firmware v9.5.0 or newer is required to ensure +// anti-klepto protection. func (device *Device) BTCSignMessage( coin messages.BTCCoin, scriptConfig *messages.BTCScriptConfigWithKeypath, diff --git a/api/firmware/btc_test.go b/api/firmware/btc_test.go index 9babce3..9126560 100644 --- a/api/firmware/btc_test.go +++ b/api/firmware/btc_test.go @@ -474,13 +474,13 @@ func TestBTCSignMessage(t *testing.T) { }, []byte("message"), ) - if env.version.AtLeast(semver.NewSemVer(9, 2, 0)) { + if env.version.AtLeast(semver.NewSemVer(9, 5, 0)) { require.NoError(t, err) require.Equal(t, expectedSig[:64], result.Signature) require.Equal(t, byte(0), result.RecID) require.Equal(t, result.ElectrumSig65, append([]byte{31}, expectedSig[:64]...)) } else { - require.EqualError(t, err, UnsupportedError("9.2.0").Error()) + require.EqualError(t, err, UnsupportedError("9.5.0").Error()) } }) } diff --git a/api/firmware/eth.go b/api/firmware/eth.go index 5c96dc9..6d80361 100644 --- a/api/firmware/eth.go +++ b/api/firmware/eth.go @@ -191,8 +191,8 @@ func ETHIdentifyCase(recipientAddress string) messages.ETHAddressCase { } // ETHSign signs an ethereum transaction. It returns a 65 byte signature (R, S, -// and 1 byte recID). If len(data) > 6144, firmware v9.26.0 or newer is -// required. +// and 1 byte recID). Firmware v9.5.0 or newer is required to ensure anti-klepto protection. If +// len(data) > 6144, firmware v9.26.0 or newer is required. func (device *Device) ETHSign( chainID uint64, keypath []uint32, @@ -229,21 +229,17 @@ func (device *Device) nonAtomicETHSign( data []byte, recipientAddressCase messages.ETHAddressCase, ) ([]byte, error) { - supportsAntiklepto := device.version.AtLeast(semver.NewSemVer(9, 5, 0)) + if !device.version.AtLeast(semver.NewSemVer(9, 5, 0)) { + return nil, UnsupportedError("9.5.0") + } useStreaming := len(data) > ethStreamingThreshold if useStreaming && !device.version.AtLeast(semver.NewSemVer(9, 26, 0)) { return nil, UnsupportedError("9.26.0") } - var hostNonceCommitment *messages.AntiKleptoHostNonceCommitment - var err error - var hostNonce []byte - - if supportsAntiklepto { - hostNonceCommitment, hostNonce, err = handleHostNonceCommitment() - if err != nil { - return nil, err - } + hostNonceCommitment, hostNonce, err := handleHostNonceCommitment() + if err != nil { + return nil, err } coin, err := device.ethCoin(chainID) @@ -285,14 +281,7 @@ func (device *Device) nonAtomicETHSign( } } - if supportsAntiklepto { - return device.nonAtomicHandleSignerNonceCommitment(response, hostNonce) - } - signResponse, ok := response.Response.(*messages.ETHResponse_Sign) - if !ok { - return nil, errp.New("unexpected response") - } - return signResponse.Sign.Signature, nil + return device.nonAtomicHandleSignerNonceCommitment(response, hostNonce) } // ETHSignEIP1559 signs an ethereum EIP1559 transaction. It returns a 65 byte @@ -395,7 +384,8 @@ func (device *Device) nonAtomicETHSignEIP1559( // ETHSignMessage signs an Ethereum message. The provided msg will be prefixed with "\x19Ethereum // Signed Message\n" + len(msg) in the hardware, e.g. "\x19Ethereum Signed dMessage\n5hello" (yes, the len prefix is the // ascii representation with no fixed size or delimiter, WTF). -// 27 is added to the recID to denote an uncompressed pubkey. +// 27 is added to the recID to denote an uncompressed pubkey. Firmware v9.5.0 or newer is required +// to ensure anti-klepto protection. func (device *Device) ETHSignMessage( chainID uint64, keypath []uint32, @@ -414,20 +404,16 @@ func (device *Device) nonAtomicETHSignMessage( if len(msg) > 1024 { return nil, errp.New("message too large") } + if !device.version.AtLeast(semver.NewSemVer(9, 5, 0)) { + return nil, UnsupportedError("9.5.0") + } - supportsAntiklepto := device.version.AtLeast(semver.NewSemVer(9, 5, 0)) - var hostNonceCommitment *messages.AntiKleptoHostNonceCommitment - var hostNonce []byte - - if supportsAntiklepto { - var err error - hostNonce, err = generateHostNonce() - if err != nil { - return nil, err - } - hostNonceCommitment = &messages.AntiKleptoHostNonceCommitment{ - Commitment: antikleptoHostCommit(hostNonce), - } + hostNonce, err := generateHostNonce() + if err != nil { + return nil, err + } + hostNonceCommitment := &messages.AntiKleptoHostNonceCommitment{ + Commitment: antikleptoHostCommit(hostNonce), } coin, err := device.ethCoin(chainID) @@ -450,24 +436,12 @@ func (device *Device) nonAtomicETHSignMessage( return nil, err } - if supportsAntiklepto { - signature, err := device.nonAtomicHandleSignerNonceCommitment(response, hostNonce) - if err != nil { - return nil, err - } - // 27 is the magic constant to add to the recoverable ID to denote an uncompressed pubkey. - signature[64] += 27 - return signature, nil - } - - signResponse, ok := response.Response.(*messages.ETHResponse_Sign) - if !ok { - return nil, errp.New("unexpected response") + signature, err := device.nonAtomicHandleSignerNonceCommitment(response, hostNonce) + if err != nil { + return nil, err } - signature := signResponse.Sign.Signature // 27 is the magic constant to add to the recoverable ID to denote an uncompressed pubkey. signature[64] += 27 - return signature, nil }