-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: p-460 bitcore message signature issue #2553
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for finding the root cause! 👍
Not sure if it's related to nonce tho (I haven't checked the lib impl and am not sure if the k
is random or not) - maybe it's a sole consequence of having random btc key pairs each time.
@@ -145,7 +145,11 @@ export class BitcoinSigner implements Signer { | |||
return new Promise((resolve, reject) => { | |||
if (isString(message)) { | |||
const sig = new bitcore.Message(message).sign(this.keypair); | |||
resolve(bufferToU8a(Buffer.from(sig, 'base64'))); | |||
let buf = Buffer.from(sig, 'base64'); | |||
if (buf.length === 64) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to have more idiomatic code by using e.g. a fixed length buffer? Something like [u8; 65]
in rust to make sure it's always 65 bytes 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After testing, it is not just the error case where buffer.length === 64
. In fact, there will also be cases where it equals 63 (not excluding other cases, 63 is the result of 1000 loops). Therefore, change to using do while to ensure getting u8aArray 65 sig.
1) Test Identity (bitcoin direct invocation)
check idGraph from sidechain storage before linking:
Error: createType(LitentryMultiSignature):: Enum(bitcoin):: Expected input with 65 bytes (520 bits), found 63 bytes
The way of creating a buffer seems to be impractical, as it will change the content of the signature, such as this way:
let buf = Buffer.from(sig, 'base64');
Buffer.alloc(65)
buf.copy(resultBuf, 65 - buf.length);
Will get an invalid error:
Got response: {"value":"TGetterIsNotAuthorized","do_watch":false,"status":"Error"}
So my initial approach was also incorrect. The reason why the test passed is that when I console.log here, actually called the sign method. Due to the uncertainty of the signature result, after two console calls in front, the third call here happened to match the sig we used, so the test passed strangely.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for finding the root cause.
|
d7efdf0
to
7877432
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Btw what about bitcore.Message
in identity-helper.ts
?
// Here we must use the format [u8,65], | ||
// so we can only accept a signature that has been decoded from base64 and has a length of 65. | ||
// Therefore, using a do -while loop to resign unnecessary signatures.We should not encounter an infinite loop situation, so no condition is set to break out of the loop. | ||
do { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I somehow feel this isn't the right way, still.
A btc message signature should be 65 bytes - the encoded base64 array should be 88 bytes. This should be firm. If we get less than 65 bytes in decoding, I can only think of padding?
Let it run again and hope to get 65 bytes isn't the good practice IMO, as in reality you should be able to verify each btc signature the user generated, with arbitrary k
cc @kziemianek - maybe you know it as you've done some btc sig related impl
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my opinion it should always produce 65 bytes signature, even bitcore-lib
developers assumes that.
If you try to use bitcode-lib
api to recreate signature from Buffer
it will fail:
const buffer = Buffer.from(sig, 'base64')
Signature.fromCompact(buffer)
Invalid Argument: Error: s must be 32 bytes
Error
at new NodeError (/home/kziemianek/projects/kziemianek/sig-test/node_modules/bitcore-lib/lib/errors/index.js:20:41)
at Object.checkArgument (/home/kziemianek/projects/kziemianek/sig-test/node_modules/bitcore-lib/lib/util/preconditions.js:14:13)
at Signature.fromCompact (/home/kziemianek/projects/kziemianek/sig-test/node_modules/bitcore-lib/lib/crypto/signature.js:53:5)
I debugged it for a while and it turns out the r
value is sometimes 31 bytes long, which results in 64 (1+31+32) bytes signature (instead of 1+32+32). Above error says s must be 32 bytes
but the root cause is still r
being 31 bytes long.
It happens ocassionally so it's really low probability of happeining twice in the row.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So to sum up, I see two options:
- switch to other js bitcoin lib (https://www.npmjs.com/package/bitcoinjs-lib maybe ?)
- implement a workaround (try to generate new signature untill it has 65 bytes) and wait for the potential fix in
bitcore-lib
- but before that happening it needs to be reported, confirmed and released.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your summary @kziemianek , I will switch to bitcoinjs for implementation. Btw, I have looked at the SDK of unisat-wallet and switched to the version they use, but there is still the same problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @jonalvarezz - in case you have some experience when implementing SDK..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new bitcoinjs-lib was tested twice in 2000 loops with no signature decoding issues. I also tested identity-helper several times, and there was no problem:
Got response: {"value":"0x010400","do_watch":false,"status":"Ok"}
✔ check idGraph from sidechain storage before linking (2012011ms)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
jumping in late, but I hope it helps.
Here is what we do on the SDK for Bitcoin in a nutshell:
- The hex challenge code should be signed without the
0x
prefix. Only relevant if signing in hex/string format. We were using raw uint signing before, but I remember we had to change this because of bitcoin/ unisat.
- The produced signature is base64 decoded and the
0x
prefix put back. Otherwise,LitentryValidationData
fails at encoding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks @jonalvarezz I actually looked at the code on idehub, and in the Bitcoin signature part, mainly use unisat-wallet. This wallet uses their own integrated unisat-sdk, and you will find that they also use bitcore-lib and bitcoinjs-lib.So, essentially there is no difference, I guess.:p
de78d82
to
50da585
Compare
tee-worker/ts-tests/integration-tests/common/utils/vc-helper.ts
Outdated
Show resolved
Hide resolved
tee-worker/ts-tests/integration-tests/di_bitcoin_identity.test.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For me it's ready to be merged
Context
resolves:p-460
Root cause: bitcore uses the ECDSA signature algorithm, which uses a nonce to sign messages. This causes the signature (base64) to not be consistent. When decoding, we might need to manually pad it with a 0 byte.
For example, under normal the signature is:
IJyax3CRyGOCceIyoBDpH9a03dwi3SoRJ4N1RZh36FxARkDCpEmUgrHqIhE5H8THYF8TcE5HuH/9BkovbYmMSbk=
But sometimes:
H6FuCBQt/fjshZIym0BBgGjbykkqBWO6CisWA9tBDDMD/5iODJehfyUXX3MrM21l5dHSuG46KqXiFEA7X94apw==
This is a base64 encoding issue.
I tested it and adding a0
can make it work normally:Updates:
The solution of adding 0 was wrong, will change the content of sig, causing it to be invalid when used. I updated it with a new way.
After 2000 cycles of testing, there were no issues with the do-while sign method: