Skip to content

crypto: add DecompressPubkey, VerifySignature#15615

Merged
fjl merged 6 commits into
ethereum:masterfrom
fjl:crypto-pubkey-ops
Dec 6, 2017
Merged

crypto: add DecompressPubkey, VerifySignature#15615
fjl merged 6 commits into
ethereum:masterfrom
fjl:crypto-pubkey-ops

Conversation

@fjl
Copy link
Copy Markdown
Contributor

@fjl fjl commented Dec 6, 2017

We need those operations for ENR.

@fjl fjl requested a review from holiman December 6, 2017 10:40
// VerifySignature checks that the given pubkey created signature over message.
// The signature should be in [R || S] format.
func VerifySignature(pubkey, msg, signature []byte) bool {
if len(msg) != 32 || len(signature) != 64 || len(pubkey) == 0 {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't len(pubkey) be one of two values ? So either compressed or uncompressed at 65 bytes, or compressed at 33 bytes. If so, I'd prefer to check against one of those. Better to be strict whenever possible.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to leave it to libsecp256k1 to check for the length. If you disagree strongly we can add the check here too. The check for zero is needed to prevent a crash for the &pubkey[0] construction.

Copy link
Copy Markdown
Contributor Author

@fjl fjl Dec 6, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The length check is done here.

Comment thread crypto/signature_cgo.go
return secp256k1.RecoverPubkey(hash, sig)
}

// SigToPub returns the public key that created the given signature.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"SigToPub returns the public key" -> "SigToPub returns the uncompressed public key"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Compressed/uncompressed doesn't matter for SigToPub because the return value isn't encoded.

Comment thread crypto/signature_cgo.go Outdated
"github.com/ethereum/go-ethereum/crypto/secp256k1"
)

// Ecrecover returns the public key that created the given signature.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Ecrecover returns the public" -> "Ecrecover returns the uncompressed public"

Comment thread crypto/signature_cgo.go
// DecompressPubkey parses a public key in the 33-byte compressed format.
func DecompressPubkey(pubkey []byte) (*ecdsa.PublicKey, error) {
x, y := secp256k1.DecompressPubkey(pubkey)
if x == nil {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Design question: Why does the underlying method return nil, and we create an error here, instead of returning error from the lower layer?

Copy link
Copy Markdown
Contributor Author

@fjl fjl Dec 6, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided this way to simplify the interface. There is only one possible error anyway: "invalid". elliptic.Unmarshal uses the same convention.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right though, maybe this should be changed.

Comment thread crypto/signature_nocgo.go
sig := &btcec.Signature{R: new(big.Int).SetBytes(signature[:32]), S: new(big.Int).SetBytes(signature[32:])}
key, err := btcec.ParsePubKey(pubkey, btcec.S256())
if err != nil {
return false
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another design question: we're throwing away error-info here, is that really needed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btcec is the fallback for platforms that can't link C code. It provides better errors because it's Go code. There is no good way to get those errors from the C version. IMHO it doesn't matter much: the signature is invalid if any of the inputs are.

Copy link
Copy Markdown
Contributor Author

@fjl fjl Dec 6, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe more context: I added this because I want a function that does decompression and verification in one call without any pubkey/signature decoding. We get the compressed pubkey in ENR and need to verify that it created the signature. The fastest and simplest way is to just pass these values into libsecp256k1.

We could go ahead and make it so secp256k1_ecdsa_verify_enc returns different ints for 'invalid signature encoding', 'invalid pubkey encoding' and 'signature mismatch', but it'll just make the code more complicated. Callers won't care: all they want to know is whether the signature checks out.

Comment thread crypto/signature_test.go
}
if VerifySignature(testpubkey, testmsg, nil) {
t.Errorf("nil signature valid")
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More potential testcases:

  • VerifySignature(testpubkey, testmsg, sig+extra_bytes)
  • VerifySignature(testpubkey, testmsg, testsig[:len(testsig)-2])

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added

Comment thread crypto/signature_test.go
}

func TestDecompressPubkey(t *testing.T) {
key, err := DecompressPubkey(testpubkeyc)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More potential testcases:

  • DecompressPubkey with nil, slice with < 32 bytes, slice with > 32 bytes

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added

@fjl
Copy link
Copy Markdown
Contributor Author

fjl commented Dec 6, 2017

@holiman PTAL

@fjl fjl merged commit e85b68e into ethereum:master Dec 6, 2017
@karalabe karalabe added this to the 1.8.0 milestone Dec 14, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants