x: private key
X: public key
m: message to sign
n: nonce extra
H: cryptographically secure hash committment
q = H(x,m,n)
Q = q·G
k = q + H(Q,m,n)
R = k·G
e = H(R,X,m)
Schnorr signature
s = k + x·e
ECDSA signature
r = R.x
s = k⁻¹·(m + r·x)
Q, s
R = Q + H(Q,m,n)·G
R, s
Schnorr verify
e = H(R,X,m)
s·G ?= R + e·X
ECDSA verify
r = R.x
s⁻¹·m·G + s⁻¹·r·X ?= R
Typically an opensource "Companion App" or view only wallet software would provide the n
nonce extra.
The PSBT standard can either be extended with a new field, or n
may be included as an invalid but recognizable "signature" that the SD replaces with (Q,s)
.
m1 = "hello ", n1 = "world!"
m2 = "hello", n2 = " world!"
q = hash(x|"hello world!")
Q = q·G
k = q + hash(Q|"hello world!")
Would generate the exact same nonce, but the SD would sign a different message ("hello "
and "hello"
), allowing malicious software to extract the private key from the device.
s1 = k + x·e1
s2 = k + x·e2
s1 - s2 = (k - k) + x(e1 - e2)
With k
eliminated, the private key x
is revealed to the malicious software.
With fixed sized fields/values hash(v1|v2|..|vn)
can be safely used.
With variable size binary strings:
hash(serialize(v1, v2, .., vn))
hash(hash(v1)|hash(v2)|..|hash(vn))
merkle_root(v1, v2, .., vn)
Any reversible serialization may also be used.
For example: hash(6|"hello "|6|"world!")
!= hash(5|"hello"|7|" world!")
Let's take a look at what happens, when we sign the same message twice, with different nonce extras!
q = H(x,m)
Q = q·G
s1 = q + H(Q,m,n1) x·e1
s2 = q + H(Q,m,n2) x·e2
s1 - s2 = (q - q) + H(Q,m,n1) - H(Q,m,n2) + x(e1 - e2)
With q
eliminated, the private key x
is revealed to the malicious software.
Yes, theoretically the SD with malicious firmware could churn the final R point to leak information using for example FEC codes (Pieter Wuille on delving) with 2n rounds of churning SD can leak the seed phrase by creating bits(seed)/n
signatures. For example if the attacker chooses to leak 4 bits each time it takes 32 signatures to leak a 128 bit (12 word) seed phrase. Due to the public transaction graph and plainly observable wallet characteristics the attacker can make good guesses at which signatures could belong to the same seed.
- The attacker can just generate a random
q
, normally this can not be detected - The verifier needs to know the private key to verify generation of
q
(same as RFC6979) - The attacker can encrypt and obfuscate his own channel
- The attacker decides his channel bandwidth making it impossible to estimate "seed health"
- Transactions may have multiple inputs making signing them not deterministic in time
- Said attack is likely to be "always on" and would be caught by verifying generation of
q
- Evil maid scenario is still problematic, factory and user acceptance tests are already passed
- Tamper evident storage of signing devices is still heavily recommended
- This is not a huge concern for cold storage scenarios with very infrequent signing
- This protocol offers protection from immediate catastrophic leaks via chosen nonce
- 24 words are better in this scheme than 12 words
- https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2020-March/017667.html (Pieter Wuille 2020)
- https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2020-February/017649.html
- https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2020-February/017655.html
- https://blog.eternitywall.com/2018/04/13/sign-to-contract/
- https://bitcointalk.org/index.php?topic=893898.msg9861102#msg9861102
- https://bitcointalk.org/index.php?topic=893898.msg9841502#msg9841502
- https://medium.com/cryptoadvance/hardware-wallets-can-be-hacked-but-this-is-fine-a6156bbd199
- https://youtu.be/j9Wvz7zI_Ac?t=640
- https://diyhpl.us/wiki/transcripts/sf-bitcoin-meetup/2019-02-04-threshold-signatures-and-accountability/
- bitcoin-core/secp256k1#590
- bitcoin-core/secp256k1#669
- https://eprint.iacr.org/2017/985
- https://moderncrypto.org/mail-archive/curves/2017/000925.html
Thank you very much. You cleared up a lot.
misunderstood that
above python library is just convenience wrapper around
libsecp256k1.so
so it uses it "directly"BIP340 uses https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki#cite_ref-11-0 but they also mention that mod should be ok on secp256k1 https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki#cite_note-13
we should also take Low R values grinding into consideration, iirc currently we use that extra data to pass counter to
nonce_function_rfc6979
for sig grinding (ECDSA only)So, my understanding is that libsecp (in its current form) only allows us to provide custom
noncefp
withnoncedata
forecdsa_sign
andextraparams
(with bothnoncefp
andndata
) forsecp256k1_schnorrsig_sign_custom
. It is possible to generate custom noncek
according to the spec, we just cannot getQ
out ofnoncefp
?I do not understand this. Looking at your schnorr signing part:
it seems to be exactly same as in
secp256k1_schnorrsig_sign_internal
(BIP340).Seems to me we could just use those
extraparams
, define our customnoncefp
as in spec:and when we need
Q
we would just recalc it again, outside ofnoncefp
context. We do not needs
for companion app as it is part of the signature already. What am I missing ?