good morning!!!!
Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
B
bor
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Iterations
Wiki
Requirements
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Test cases
Artifacts
Deploy
Releases
Package registry
Container registry
Harbor Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Issue analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
open
bor
Commits
aab35600
Unverified
Commit
aab35600
authored
Mar 22, 2021
by
MrChico
Committed by
GitHub
Mar 22, 2021
Browse files
Options
Downloads
Patches
Plain Diff
accounts: eip-712 signing for ledger (#22378)
* accounts: eip-712 signing for ledger * address review comments
parent
eaccdba4
Branches
Branches containing commit
Tags
Tags containing commit
No related merge requests found
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
accounts/usbwallet/ledger.go
+82
-0
82 additions, 0 deletions
accounts/usbwallet/ledger.go
accounts/usbwallet/trezor.go
+4
-0
4 additions, 0 deletions
accounts/usbwallet/trezor.go
accounts/usbwallet/wallet.go
+42
-1
42 additions, 1 deletion
accounts/usbwallet/wallet.go
with
128 additions
and
1 deletion
accounts/usbwallet/ledger.go
+
82
−
0
View file @
aab35600
...
...
@@ -52,8 +52,10 @@ const (
ledgerOpRetrieveAddress
ledgerOpcode
=
0x02
// Returns the public key and Ethereum address for a given BIP 32 path
ledgerOpSignTransaction
ledgerOpcode
=
0x04
// Signs an Ethereum transaction after having the user validate the parameters
ledgerOpGetConfiguration
ledgerOpcode
=
0x06
// Returns specific wallet application configuration
ledgerOpSignTypedMessage
ledgerOpcode
=
0x0c
// Signs an Ethereum message following the EIP 712 specification
ledgerP1DirectlyFetchAddress
ledgerParam1
=
0x00
// Return address directly from the wallet
ledgerP1InitTypedMessageData
ledgerParam1
=
0x00
// First chunk of Typed Message data
ledgerP1InitTransactionData
ledgerParam1
=
0x00
// First transaction data block for signing
ledgerP1ContTransactionData
ledgerParam1
=
0x80
// Subsequent transaction data block for signing
ledgerP2DiscardAddressChainCode
ledgerParam2
=
0x00
// Do not return the chain code along with the address
...
...
@@ -170,6 +172,24 @@ func (w *ledgerDriver) SignTx(path accounts.DerivationPath, tx *types.Transactio
return
w
.
ledgerSign
(
path
,
tx
,
chainID
)
}
// SignTypedMessage implements usbwallet.driver, sending the message to the Ledger and
// waiting for the user to sign or deny the transaction.
//
// Note: this was introduced in the ledger 1.5.0 firmware
func
(
w
*
ledgerDriver
)
SignTypedMessage
(
path
accounts
.
DerivationPath
,
domainHash
[]
byte
,
messageHash
[]
byte
)
([]
byte
,
error
)
{
// If the Ethereum app doesn't run, abort
if
w
.
offline
()
{
return
nil
,
accounts
.
ErrWalletClosed
}
// Ensure the wallet is capable of signing the given transaction
if
w
.
version
[
0
]
<
1
&&
w
.
version
[
1
]
<
5
{
//lint:ignore ST1005 brand name displayed on the console
return
nil
,
fmt
.
Errorf
(
"Ledger version >= 1.5.0 required for EIP-712 signing (found version v%d.%d.%d)"
,
w
.
version
[
0
],
w
.
version
[
1
],
w
.
version
[
2
])
}
// All infos gathered and metadata checks out, request signing
return
w
.
ledgerSignTypedMessage
(
path
,
domainHash
,
messageHash
)
}
// ledgerVersion retrieves the current version of the Ethereum wallet app running
// on the Ledger wallet.
//
...
...
@@ -367,6 +387,68 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
return
sender
,
signed
,
nil
}
// ledgerSignTypedMessage sends the transaction to the Ledger wallet, and waits for the user
// to confirm or deny the transaction.
//
// The signing protocol is defined as follows:
//
// CLA | INS | P1 | P2 | Lc | Le
// ----+-----+----+-----------------------------+-----+---
// E0 | 0C | 00 | implementation version : 00 | variable | variable
//
// Where the input is:
//
// Description | Length
// -------------------------------------------------+----------
// Number of BIP 32 derivations to perform (max 10) | 1 byte
// First derivation index (big endian) | 4 bytes
// ... | 4 bytes
// Last derivation index (big endian) | 4 bytes
// domain hash | 32 bytes
// message hash | 32 bytes
//
//
//
// And the output data is:
//
// Description | Length
// ------------+---------
// signature V | 1 byte
// signature R | 32 bytes
// signature S | 32 bytes
func
(
w
*
ledgerDriver
)
ledgerSignTypedMessage
(
derivationPath
[]
uint32
,
domainHash
[]
byte
,
messageHash
[]
byte
)
([]
byte
,
error
)
{
// Flatten the derivation path into the Ledger request
path
:=
make
([]
byte
,
1
+
4
*
len
(
derivationPath
))
path
[
0
]
=
byte
(
len
(
derivationPath
))
for
i
,
component
:=
range
derivationPath
{
binary
.
BigEndian
.
PutUint32
(
path
[
1
+
4
*
i
:
],
component
)
}
// Create the 712 message
payload
:=
append
(
path
,
domainHash
...
)
payload
=
append
(
payload
,
messageHash
...
)
// Send the request and wait for the response
var
(
op
=
ledgerP1InitTypedMessageData
reply
[]
byte
err
error
)
// Send the message over, ensuring it's processed correctly
reply
,
err
=
w
.
ledgerExchange
(
ledgerOpSignTypedMessage
,
op
,
0
,
payload
)
if
err
!=
nil
{
return
nil
,
err
}
// Extract the Ethereum signature and do a sanity validation
if
len
(
reply
)
!=
crypto
.
SignatureLength
{
return
nil
,
errors
.
New
(
"reply lacks signature"
)
}
signature
:=
append
(
reply
[
1
:
],
reply
[
0
])
return
signature
,
nil
}
// ledgerExchange performs a data exchange with the Ledger wallet, sending it a
// message and retrieving the response.
//
...
...
This diff is collapsed.
Click to expand it.
accounts/usbwallet/trezor.go
+
4
−
0
View file @
aab35600
...
...
@@ -185,6 +185,10 @@ func (w *trezorDriver) SignTx(path accounts.DerivationPath, tx *types.Transactio
return
w
.
trezorSign
(
path
,
tx
,
chainID
)
}
func
(
w
*
trezorDriver
)
SignTypedMessage
(
path
accounts
.
DerivationPath
,
domainHash
[]
byte
,
messageHash
[]
byte
)
([]
byte
,
error
)
{
return
nil
,
accounts
.
ErrNotSupported
}
// trezorDerive sends a derivation request to the Trezor device and returns the
// Ethereum address located on that path.
func
(
w
*
trezorDriver
)
trezorDerive
(
derivationPath
[]
uint32
)
(
common
.
Address
,
error
)
{
...
...
This diff is collapsed.
Click to expand it.
accounts/usbwallet/wallet.go
+
42
−
1
View file @
aab35600
...
...
@@ -67,6 +67,8 @@ type driver interface {
// SignTx sends the transaction to the USB device and waits for the user to confirm
// or deny the transaction.
SignTx
(
path
accounts
.
DerivationPath
,
tx
*
types
.
Transaction
,
chainID
*
big
.
Int
)
(
common
.
Address
,
*
types
.
Transaction
,
error
)
SignTypedMessage
(
path
accounts
.
DerivationPath
,
messageHash
[]
byte
,
domainHash
[]
byte
)
([]
byte
,
error
)
}
// wallet represents the common functionality shared by all USB hardware
...
...
@@ -524,9 +526,48 @@ func (w *wallet) signHash(account accounts.Account, hash []byte) ([]byte, error)
// SignData signs keccak256(data). The mimetype parameter describes the type of data being signed
func
(
w
*
wallet
)
SignData
(
account
accounts
.
Account
,
mimeType
string
,
data
[]
byte
)
([]
byte
,
error
)
{
// Unless we are doing 712 signing, simply dispatch to signHash
if
!
(
mimeType
==
accounts
.
MimetypeTypedData
&&
len
(
data
)
==
66
&&
data
[
0
]
==
0x19
&&
data
[
1
]
==
0x01
)
{
return
w
.
signHash
(
account
,
crypto
.
Keccak256
(
data
))
}
// dispatch to 712 signing if the mimetype is TypedData and the format matches
w
.
stateLock
.
RLock
()
// Comms have own mutex, this is for the state fields
defer
w
.
stateLock
.
RUnlock
()
// If the wallet is closed, abort
if
w
.
device
==
nil
{
return
nil
,
accounts
.
ErrWalletClosed
}
// Make sure the requested account is contained within
path
,
ok
:=
w
.
paths
[
account
.
Address
]
if
!
ok
{
return
nil
,
accounts
.
ErrUnknownAccount
}
// All infos gathered and metadata checks out, request signing
<-
w
.
commsLock
defer
func
()
{
w
.
commsLock
<-
struct
{}{}
}()
// Ensure the device isn't screwed with while user confirmation is pending
// TODO(karalabe): remove if hotplug lands on Windows
w
.
hub
.
commsLock
.
Lock
()
w
.
hub
.
commsPend
++
w
.
hub
.
commsLock
.
Unlock
()
defer
func
()
{
w
.
hub
.
commsLock
.
Lock
()
w
.
hub
.
commsPend
--
w
.
hub
.
commsLock
.
Unlock
()
}()
// Sign the transaction
signature
,
err
:=
w
.
driver
.
SignTypedMessage
(
path
,
data
[
2
:
34
],
data
[
34
:
66
])
if
err
!=
nil
{
return
nil
,
err
}
return
signature
,
nil
}
// SignDataWithPassphrase implements accounts.Wallet, attempting to sign the given
// data with the given account using passphrase as extra authentication.
// Since USB wallets don't rely on passphrases, these are silently ignored.
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment