mirror of
https://github.com/libp2p/go-libp2p.git
synced 2026-04-22 16:17:19 +08:00
refactor(webtransport): Use keygen package for deterministic ecdsa key
generation.
This commit is contained in:
committed by
Marco Munizaga
parent
93a9158968
commit
418cf8ff40
@@ -7,6 +7,7 @@ retract v0.26.1 // Tag was applied incorrectly due to a bug in the release workf
|
||||
retract v0.36.0 // Accidentally modified the tag.
|
||||
|
||||
require (
|
||||
filippo.io/keygen v0.0.0-20260114151900-8e2790ea4c5b
|
||||
github.com/benbjohnson/clock v1.3.5
|
||||
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0
|
||||
@@ -66,6 +67,7 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/bigmod v0.1.1-0.20260103110540-f8a47775ebe5 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
filippo.io/bigmod v0.1.1-0.20260103110540-f8a47775ebe5 h1:JA0fFr+kxpqTdxR9LOBiTWpGNchqmkcsgmdeJZRclZ0=
|
||||
filippo.io/bigmod v0.1.1-0.20260103110540-f8a47775ebe5/go.mod h1:OjOXDNlClLblvXdwgFFOQFJEocLhhtai8vGLy0JCZlI=
|
||||
filippo.io/keygen v0.0.0-20260114151900-8e2790ea4c5b h1:REI1FbdW71yO56Are4XAxD+OS/e+BQsB3gE4mZRQEXY=
|
||||
filippo.io/keygen v0.0.0-20260114151900-8e2790ea4c5b/go.mod h1:9nnw1SlYHYuPSo/3wjQzNjSbeHlq2NsKo5iEtfJPWP0=
|
||||
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
|
||||
github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/canonical/go-sp800.90a-drbg v0.0.0-20210314144037-6eeb1040d6c3 h1:oe6fCvaEpkhyW3qAicT0TnGtyht/UrgvOwMcEgLb7Aw=
|
||||
github.com/canonical/go-sp800.90a-drbg v0.0.0-20210314144037-6eeb1040d6c3/go.mod h1:qdP0gaj0QtgX2RUZhnlVrceJ+Qln8aSlDyJwelLLFeM=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
@@ -224,6 +230,8 @@ golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
|
||||
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -2,6 +2,7 @@ package libp2pwebtransport
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/sha256"
|
||||
@@ -17,6 +18,8 @@ import (
|
||||
|
||||
"golang.org/x/crypto/hkdf"
|
||||
|
||||
"filippo.io/keygen"
|
||||
|
||||
ic "github.com/libp2p/go-libp2p/core/crypto"
|
||||
|
||||
"github.com/multiformats/go-multihash"
|
||||
@@ -71,11 +74,16 @@ func generateCert(key ic.PrivKey, start, end time.Time) (*x509.Certificate, *ecd
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
caPrivateKey, err := ecdsa.GenerateKey(elliptic.P256(), deterministicHKDFReader)
|
||||
ecdsaSeed := make([]byte, 192/8) // 192 bits of entropy required for P256
|
||||
if _, err := io.ReadFull(deterministicHKDFReader, ecdsaSeed); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
caPrivateKey, err := keygen.ECDSA(elliptic.P256(), ecdsaSeed)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
caBytes, err := x509.CreateCertificate(deterministicHKDFReader, certTempl, certTempl, caPrivateKey.Public(), caPrivateKey)
|
||||
caBytes, err := x509.CreateCertificate(deterministicHKDFReader, certTempl, certTempl, caPrivateKey.Public(), deterministicSigner{caPrivateKey})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -136,29 +144,26 @@ func verifyRawCerts(rawCerts [][]byte, certHashes []multihash.DecodedMultihash)
|
||||
return nil
|
||||
}
|
||||
|
||||
// deterministicReader is a hack. It counter-acts the Go library's attempt at
|
||||
// making ECDSA signatures non-deterministic. Go adds non-determinism by
|
||||
// randomly dropping a singly byte from the reader stream. This counteracts this
|
||||
// by detecting when a read is a single byte and using a different reader
|
||||
// instead.
|
||||
type deterministicReader struct {
|
||||
reader io.Reader
|
||||
singleByteReader io.Reader
|
||||
}
|
||||
|
||||
func newDeterministicReader(seed []byte, salt []byte, info string) io.Reader {
|
||||
reader := hkdf.New(sha256.New, seed, salt, []byte(info))
|
||||
singleByteReader := hkdf.New(sha256.New, seed, salt, []byte(info+" single byte"))
|
||||
|
||||
return &deterministicReader{
|
||||
reader: reader,
|
||||
singleByteReader: singleByteReader,
|
||||
}
|
||||
return hkdf.New(sha256.New, seed, salt, []byte(info))
|
||||
}
|
||||
|
||||
func (r *deterministicReader) Read(p []byte) (n int, err error) {
|
||||
if len(p) == 1 {
|
||||
return r.singleByteReader.Read(p)
|
||||
}
|
||||
return r.reader.Read(p)
|
||||
// deterministicSigner wraps an ecdsa.PrivateKey and exposes a `Sign` method
|
||||
// that will produce deterministic signatures by ignoring the rand reader.
|
||||
// Go 1.24 produces deterministic ecdsa signatures when passed a nil random source.
|
||||
// See: https://go.dev/doc/go1.24#cryptoecdsapkgcryptoecdsa
|
||||
type deterministicSigner struct {
|
||||
priv *ecdsa.PrivateKey
|
||||
}
|
||||
|
||||
var _ crypto.Signer = deterministicSigner{}
|
||||
|
||||
func (ds deterministicSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
|
||||
// Ignore the rand reader to produce deterministic signatures.
|
||||
_ = rand
|
||||
return ds.priv.Sign(nil, digest, opts)
|
||||
}
|
||||
|
||||
func (ds deterministicSigner) Public() crypto.PublicKey {
|
||||
return ds.priv.Public()
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
mrand "math/rand"
|
||||
"testing"
|
||||
@@ -155,40 +154,3 @@ func TestDeterministicCertHashes(t *testing.T) {
|
||||
require.Equal(t, keyBytes, keyBytes2)
|
||||
}
|
||||
}
|
||||
|
||||
// TestDeterministicSig tests that our hack around making ECDSA signatures
|
||||
// deterministic works. If this fails, this means we need to try another
|
||||
// strategy to make deterministic signatures or try something else entirely.
|
||||
// See deterministicReader for more context.
|
||||
func TestDeterministicSig(t *testing.T) {
|
||||
// Run this test 1000 times since we want to make sure the signatures are deterministic
|
||||
runs := 1000
|
||||
for range runs {
|
||||
zeroSeed := [32]byte{}
|
||||
deterministicHKDFReader := newDeterministicReader(zeroSeed[:], nil, deterministicCertInfo)
|
||||
b := [1024]byte{}
|
||||
io.ReadFull(deterministicHKDFReader, b[:])
|
||||
caPrivateKey, err := ecdsa.GenerateKey(elliptic.P256(), deterministicHKDFReader)
|
||||
require.NoError(t, err)
|
||||
|
||||
sig, err := caPrivateKey.Sign(deterministicHKDFReader, b[:], crypto.SHA256)
|
||||
require.NoError(t, err)
|
||||
|
||||
deterministicHKDFReader = newDeterministicReader(zeroSeed[:], nil, deterministicCertInfo)
|
||||
b2 := [1024]byte{}
|
||||
io.ReadFull(deterministicHKDFReader, b2[:])
|
||||
caPrivateKey2, err := ecdsa.GenerateKey(elliptic.P256(), deterministicHKDFReader)
|
||||
require.NoError(t, err)
|
||||
|
||||
sig2, err := caPrivateKey2.Sign(deterministicHKDFReader, b2[:], crypto.SHA256)
|
||||
require.NoError(t, err)
|
||||
|
||||
keyBytes, err := x509.MarshalECPrivateKey(caPrivateKey)
|
||||
require.NoError(t, err)
|
||||
keyBytes2, err := x509.MarshalECPrivateKey(caPrivateKey2)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, sig, sig2)
|
||||
require.Equal(t, keyBytes, keyBytes2)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/bigmod v0.1.1-0.20260103110540-f8a47775ebe5 // indirect
|
||||
filippo.io/keygen v0.0.0-20260114151900-8e2790ea4c5b // indirect
|
||||
github.com/benbjohnson/clock v1.3.5 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
filippo.io/bigmod v0.1.1-0.20260103110540-f8a47775ebe5 h1:JA0fFr+kxpqTdxR9LOBiTWpGNchqmkcsgmdeJZRclZ0=
|
||||
filippo.io/bigmod v0.1.1-0.20260103110540-f8a47775ebe5/go.mod h1:OjOXDNlClLblvXdwgFFOQFJEocLhhtai8vGLy0JCZlI=
|
||||
filippo.io/keygen v0.0.0-20260114151900-8e2790ea4c5b h1:REI1FbdW71yO56Are4XAxD+OS/e+BQsB3gE4mZRQEXY=
|
||||
filippo.io/keygen v0.0.0-20260114151900-8e2790ea4c5b/go.mod h1:9nnw1SlYHYuPSo/3wjQzNjSbeHlq2NsKo5iEtfJPWP0=
|
||||
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
|
||||
github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/canonical/go-sp800.90a-drbg v0.0.0-20210314144037-6eeb1040d6c3 h1:oe6fCvaEpkhyW3qAicT0TnGtyht/UrgvOwMcEgLb7Aw=
|
||||
github.com/canonical/go-sp800.90a-drbg v0.0.0-20210314144037-6eeb1040d6c3/go.mod h1:qdP0gaj0QtgX2RUZhnlVrceJ+Qln8aSlDyJwelLLFeM=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
@@ -220,6 +226,8 @@ golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
|
||||
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
|
||||
Reference in New Issue
Block a user