-
Notifications
You must be signed in to change notification settings - Fork 523
ECDSA secp256k1 #2852
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
ECDSA secp256k1 #2852
Conversation
c46dd16 to
efb72e2
Compare
28019e7 to
1da24d5
Compare
|
Could you fork the eth secp256k1 to algorand/secp256k1 and use it from there ? ( and have the eth as upstream ). |
jasonpaulos
left a comment
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.
This is a great start. I have some comments on opcode details, but things are looking good in general.
1da24d5 to
688fbb9
Compare
Codecov Report
@@ Coverage Diff @@
## master #2852 +/- ##
==========================================
- Coverage 47.34% 47.29% -0.05%
==========================================
Files 351 355 +4
Lines 56523 56889 +366
==========================================
+ Hits 26758 26906 +148
- Misses 26758 26948 +190
- Partials 3007 3035 +28
Continue to review full report at Codecov.
|
Cost calculated relative to ed25519verify BenchmarkEcDsa/ecdsa_verify-8 17341 67335 ns/op BenchmarkEcDsa/ecdsa_pk_decompress-8 42854 25714 ns/op BenchmarkEcDsa/ecdsa_pk_recover-8 15337 78249 ns/op BenchmarkEd25519Verifyx1-8 15244 75253 ns/op
688fbb9 to
bb53759
Compare
Done. |
jannotti
left a comment
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 don't feel qualified to comment on the crypto itself, but I've commented where I could.
Thanks for adding work to document fields. I'll will need that to finish up tx_field spec.
data/transactions/logic/README.md
Outdated
| | `getbyte` | pop a byte-array A and integer B. Extract the Bth byte of A and push it as an integer | | ||
| | `setbyte` | pop a byte-array A, integer B, and small integer C (between 0..255). Set the Bth byte of A to C, and push the result | | ||
| | `concat` | pop two byte-arrays A and B and join them, push the result | | ||
| | `ecdsa_verify c` | for (data A, signature B, C and pubkey D, E) verify the signature of the data against the pubkey => {0 or 1} | |
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.
This is confusing because c is the name of the field (the immediate) and also (I think) the name you're using to describe the stack value. I'd pick a new name for the immediate.
Also need to say what the immediate is/does.
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.
done
data/transactions/logic/README.md
Outdated
| | `ecdsa_pk_recover c` | for (data A, recovery id B, signature C, D) recover a public compressed key => [*... stack*, X, Y] | | ||
| | `ecdsa_pk_decompress c` | decompress pubkey A into components X, Y => [*... stack*, X, Y] | |
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.
Earlier comment applies to these too.
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.
done
data/transactions/logic/doc.go
Outdated
| "ed25519verify": "The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack.", | ||
| "ecdsa_verify": "The 32 byte Y-component of a public key is the last element on the stack, preceded by X-component of a pubkey, preceded by S and R components of a signature, preceded by the data that is fifth element on the stack. The signed data must be 32 bytes long, and signatures in lower-S form are only accepted.", | ||
| "ecdsa_pk_decompress": "The 33 byte public key in a compressed form to be decompressed into X and Y (top) components.", | ||
| "ecdsa_pk_recover": "S (top) and R elements of a signature, recovery id and data (bottom) are expected on the stack and used to deriver a public key in compressed format. The signed data must be 32 bytes long.", |
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 think we should extend our convention for naming stack arguments. So for a five argument opcode, you can just refer to A,B,C,D,&E. (A is the deepest, E is top-of-stack). I think that will simplify the text.
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 agree but in this extended section we do not refer A-E at all, only in short description.
jasonpaulos
left a comment
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.
This is nearly there, I just have some minor suggestions.
Additionally, could you also add this test I made to verify compatibility with ethereum signatures? https://gist.github.com/jasonpaulos/47166d44c13e4efb18c7a1a09f4ac969
data/transactions/logic/doc.go
Outdated
| "ed25519verify": "for (data A, signature B, pubkey C) verify the signature of (\"ProgData\" || program_hash || data) against the pubkey => {0 or 1}", | ||
| "ecdsa_verify": "for (data A, signature B, C and pubkey D, E) verify the signature of the data against the pubkey => {0 or 1}", | ||
| "ecdsa_pk_decompress": "decompress pubkey A into components X, Y => [*... stack*, X, Y]", | ||
| "ecdsa_pk_recover": "for (data A, recovery id B, signature C, D) recover a public compressed key => [*... stack*, X, Y]", |
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.
This should not say compressed key
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.
done
data/transactions/logic/doc.go
Outdated
| "app_params_get": "params: Txn.ForeignApps offset or an app id that appears in Txn.ForeignApps. Return: did_exist flag (1 if exist and 0 otherwise), value.", | ||
| "log": "`log` can be called up to MaxLogCalls times in a program, and log up to a total of 1k bytes.", | ||
| "ed25519verify": "The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack.", | ||
| "ecdsa_verify": "The 32 byte Y-component of a public key is the last element on the stack, preceded by X-component of a pubkey, preceded by S and R components of a signature, preceded by the data that is fifth element on the stack. The signed data must be 32 bytes long, and signatures in lower-S form are only accepted.", |
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.
Could you mention that Y, X, S, and R are big-endian encoded?
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.
done
data/transactions/logic/doc.go
Outdated
| "ed25519verify": "The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack.", | ||
| "ecdsa_verify": "The 32 byte Y-component of a public key is the last element on the stack, preceded by X-component of a pubkey, preceded by S and R components of a signature, preceded by the data that is fifth element on the stack. The signed data must be 32 bytes long, and signatures in lower-S form are only accepted.", | ||
| "ecdsa_pk_decompress": "The 33 byte public key in a compressed form to be decompressed into X and Y (top) components.", | ||
| "ecdsa_pk_recover": "S (top) and R elements of a signature, recovery id and data (bottom) are expected on the stack and used to deriver a public key in compressed format. The signed data must be 32 bytes long.", |
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.
compressed should not be mentioned here either
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.
done
| pass bool | ||
| }{ | ||
| {r, true}, | ||
| {rTampered, false}, |
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 would also recommend altering the message to make sure verification fails
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.
done
|
Could we maybe have |
algorandskiy
left a comment
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.
All reviews addressed except A, B, C,... naming in extended doc.
Additionally, could you also add this test I made to verify compatibility with ethereum signatures? https://gist.github.com/jasonpaulos/47166d44c13e4efb18c7a1a09f4ac969
Added.
data/transactions/logic/README.md
Outdated
| | `getbyte` | pop a byte-array A and integer B. Extract the Bth byte of A and push it as an integer | | ||
| | `setbyte` | pop a byte-array A, integer B, and small integer C (between 0..255). Set the Bth byte of A to C, and push the result | | ||
| | `concat` | pop two byte-arrays A and B and join them, push the result | | ||
| | `ecdsa_verify c` | for (data A, signature B, C and pubkey D, E) verify the signature of the data against the pubkey => {0 or 1} | |
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.
done
data/transactions/logic/README.md
Outdated
| | `ecdsa_pk_recover c` | for (data A, recovery id B, signature C, D) recover a public compressed key => [*... stack*, X, Y] | | ||
| | `ecdsa_pk_decompress c` | decompress pubkey A into components X, Y => [*... stack*, X, Y] | |
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.
done
data/transactions/logic/doc.go
Outdated
| "ed25519verify": "for (data A, signature B, pubkey C) verify the signature of (\"ProgData\" || program_hash || data) against the pubkey => {0 or 1}", | ||
| "ecdsa_verify": "for (data A, signature B, C and pubkey D, E) verify the signature of the data against the pubkey => {0 or 1}", | ||
| "ecdsa_pk_decompress": "decompress pubkey A into components X, Y => [*... stack*, X, Y]", | ||
| "ecdsa_pk_recover": "for (data A, recovery id B, signature C, D) recover a public compressed key => [*... stack*, X, Y]", |
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.
done
data/transactions/logic/doc.go
Outdated
| "app_params_get": "params: Txn.ForeignApps offset or an app id that appears in Txn.ForeignApps. Return: did_exist flag (1 if exist and 0 otherwise), value.", | ||
| "log": "`log` can be called up to MaxLogCalls times in a program, and log up to a total of 1k bytes.", | ||
| "ed25519verify": "The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack.", | ||
| "ecdsa_verify": "The 32 byte Y-component of a public key is the last element on the stack, preceded by X-component of a pubkey, preceded by S and R components of a signature, preceded by the data that is fifth element on the stack. The signed data must be 32 bytes long, and signatures in lower-S form are only accepted.", |
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.
done
data/transactions/logic/doc.go
Outdated
| "ed25519verify": "The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack.", | ||
| "ecdsa_verify": "The 32 byte Y-component of a public key is the last element on the stack, preceded by X-component of a pubkey, preceded by S and R components of a signature, preceded by the data that is fifth element on the stack. The signed data must be 32 bytes long, and signatures in lower-S form are only accepted.", | ||
| "ecdsa_pk_decompress": "The 33 byte public key in a compressed form to be decompressed into X and Y (top) components.", | ||
| "ecdsa_pk_recover": "S (top) and R elements of a signature, recovery id and data (bottom) are expected on the stack and used to deriver a public key in compressed format. The signed data must be 32 bytes long.", |
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.
done
data/transactions/logic/doc.go
Outdated
| "ed25519verify": "The 32 byte public key is the last element on the stack, preceded by the 64 byte signature at the second-to-last element on the stack, preceded by the data which was signed at the third-to-last element on the stack.", | ||
| "ecdsa_verify": "The 32 byte Y-component of a public key is the last element on the stack, preceded by X-component of a pubkey, preceded by S and R components of a signature, preceded by the data that is fifth element on the stack. The signed data must be 32 bytes long, and signatures in lower-S form are only accepted.", | ||
| "ecdsa_pk_decompress": "The 33 byte public key in a compressed form to be decompressed into X and Y (top) components.", | ||
| "ecdsa_pk_recover": "S (top) and R elements of a signature, recovery id and data (bottom) are expected on the stack and used to deriver a public key in compressed format. The signed data must be 32 bytes long.", |
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 agree but in this extended section we do not refer A-E at all, only in short description.
| pass bool | ||
| }{ | ||
| {r, true}, | ||
| {rTampered, false}, |
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.
done
This is quite small and follows the approach of keeping crypto stuff under `crypto/' in go-algorand. |
Since the merge of #2852 go mod tidy and go mod vendor see paths in github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1 as a dependency, and go mod tidy adds it to go.mod.
* Add secp256k1 module from go-ethereum 1.10.7 * ECDSA secp256k1 implementation Cost calculated relative to ed25519verify BenchmarkEcDsa/ecdsa_verify-8 17341 67335 ns/op BenchmarkEcDsa/ecdsa_pk_decompress-8 42854 25714 ns/op BenchmarkEcDsa/ecdsa_pk_recover-8 15337 78249 ns/op BenchmarkEd25519Verifyx1-8 15244 75253 ns/op
Since the merge of algorand#2852 go mod tidy and go mod vendor see paths in github.com/ethereum/go-ethereum/crypto/secp256k1/libsecp256k1 as a dependency, and go mod tidy adds it to go.mod.
Summary
ecdsa_verify,ecdsa_pk_decompress,ecdsa_pk_recoveropcodes.ecdsa_verifyandecdsa_pk_recoverwork with two-component keys (two 32 bytes long numbers) and not a single 33-bytes compressed or 65-bytes uncompressed keys.Test Plan
Added new tests.
Cost calculation
Made as a ratio of
ed25519verifycostReviewer Notes
Most of the code is
crypto/secp256k1import from go-ethereum, please ignore.