From a82a8dc547dca7e0f4e30c4d6f6adaaa124babbc Mon Sep 17 00:00:00 2001 From: Jack Doan Date: Wed, 6 May 2026 17:00:07 -0500 Subject: [PATCH] don't panic on bad ed25519 key lengths (#1601) * don't panic on bad ed25519 key lengths * don't allow mismatched curves * add test --- cert/ca_pool.go | 4 ++++ cert/ca_pool_test.go | 28 ++++++++++++++++++++++++++++ cert/cert_v1.go | 3 +++ cert/cert_v2.go | 3 +++ cert/errors.go | 1 + 5 files changed, 39 insertions(+) diff --git a/cert/ca_pool.go b/cert/ca_pool.go index 792f8e66..966f78e3 100644 --- a/cert/ca_pool.go +++ b/cert/ca_pool.go @@ -217,6 +217,10 @@ func (ncp *CAPool) verify(c Certificate, now time.Time, certFp string, signerFp return nil, err } + if signer.Certificate.Curve() != c.Curve() { + return nil, ErrCurveMismatch + } + if signer.Certificate.Expired(now) { return nil, ErrRootExpired } diff --git a/cert/ca_pool_test.go b/cert/ca_pool_test.go index ab173228..c246e770 100644 --- a/cert/ca_pool_test.go +++ b/cert/ca_pool_test.go @@ -654,3 +654,31 @@ func TestCertificateV2_Verify_Subnets(t *testing.T) { _, err = caPool.VerifyCertificate(time.Now(), c) require.NoError(t, err) } + +func TestCertificateV2_CurveMismatch(t *testing.T) { + caIp1 := mustParsePrefixUnmapped("10.0.0.0/16") + caIp2 := mustParsePrefixUnmapped("192.168.0.0/24") + ca, _, caKey, _ := NewTestCaCert(Version2, Curve_P256, time.Now(), time.Now().Add(10*time.Minute), []netip.Prefix{caIp1, caIp2}, nil, []string{"test"}) + + caPem, err := ca.MarshalPEM() + require.NoError(t, err) + + caPool := NewCAPool() + b, err := caPool.AddCAFromPEM(caPem) + require.NoError(t, err) + assert.Empty(t, b) + + // ip is outside the network + cIp1 := mustParsePrefixUnmapped("10.0.0.1/24") + c, _, _, _ := NewTestCert(Version2, Curve_P256, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), []netip.Prefix{cIp1}, nil, []string{"test"}) + + fp, _ := c.Fingerprint() + _, err = caPool.verify(c, time.Now(), fp, c.Issuer()) + require.NoError(t, err) + // + c2 := c.(*certificateV2) + c2.curve = Curve_CURVE25519 + fp, _ = c.Fingerprint() + _, err = caPool.verify(c, time.Now(), fp, c.Issuer()) + require.Error(t, err) +} diff --git a/cert/cert_v1.go b/cert/cert_v1.go index c32f409a..4df30032 100644 --- a/cert/cert_v1.go +++ b/cert/cert_v1.go @@ -112,6 +112,9 @@ func (c *certificateV1) CheckSignature(key []byte) bool { } switch c.details.curve { case Curve_CURVE25519: + if len(key) != ed25519.PublicKeySize { + return false //avoids a panic internal to ed25519 + } return ed25519.Verify(key, b, c.signature) case Curve_P256: pubKey, err := ecdsa.ParseUncompressedPublicKey(elliptic.P256(), key) diff --git a/cert/cert_v2.go b/cert/cert_v2.go index 4648c496..c2b43a69 100644 --- a/cert/cert_v2.go +++ b/cert/cert_v2.go @@ -151,6 +151,9 @@ func (c *certificateV2) CheckSignature(key []byte) bool { switch c.curve { case Curve_CURVE25519: + if len(key) != ed25519.PublicKeySize { + return false //avoids a panic internal to ed25519 + } return ed25519.Verify(key, b, c.signature) case Curve_P256: pubKey, err := ecdsa.ParseUncompressedPublicKey(elliptic.P256(), key) diff --git a/cert/errors.go b/cert/errors.go index 8c480a14..596cfe19 100644 --- a/cert/errors.go +++ b/cert/errors.go @@ -22,6 +22,7 @@ var ( ErrCaNotFound = errors.New("could not find ca for the certificate") ErrUnknownVersion = errors.New("certificate version unrecognized") ErrCertPubkeyPresent = errors.New("certificate has unexpected pubkey present") + ErrCurveMismatch = errors.New("certificate curve does not match CA") ErrInvalidPEMBlock = errors.New("input did not contain a valid PEM encoded block") ErrInvalidPEMCertificateBanner = errors.New("bytes did not contain a proper certificate banner")