diff --git a/cmd/publisher/auth/common.go b/cmd/publisher/auth/common.go index 053b543c2..f26ff9a19 100644 --- a/cmd/publisher/auth/common.go +++ b/cmd/publisher/auth/common.go @@ -127,13 +127,25 @@ func (c *InProcessSigner) GetSignedTimestamp(_ context.Context) (*string, []byte if err != nil { return nil, nil, fmt.Errorf("failed to sign message: %w", err) } - signature := append(r.Bytes(), s.Bytes()...) + signature := encodeECDSAP384Signature(r, s) return ×tamp, signature, nil default: return nil, nil, fmt.Errorf("unsupported crypto algorithm: %s", c.cryptoAlgorithm) } } +// encodeECDSAP384Signature encodes r and s as fixed-width R || S (48 bytes each, +// 96 bytes total). big.Int.Bytes drops leading zero bytes, so appending them +// directly can produce fewer than 96 bytes when r or s is small, which the +// registry rejects with "invalid signature size for ECDSA P-384". FillBytes +// left-pads each component to 48 bytes so verification always sees 96 bytes. +func encodeECDSAP384Signature(r, s *big.Int) []byte { + signature := make([]byte, 96) + r.FillBytes(signature[:48]) + s.FillBytes(signature[48:]) + return signature +} + // parseRawPrivateKey parses a raw ECDSA private key from bytes. // This mimics crypto/ecdsa.ParseRawPrivateKey from Go 1.25+ for compatibility with Go 1.24. func parseRawPrivateKey(curve elliptic.Curve, privateKeyBytes []byte) (*ecdsa.PrivateKey, error) { diff --git a/cmd/publisher/auth/common_internal_test.go b/cmd/publisher/auth/common_internal_test.go new file mode 100644 index 000000000..02fb85621 --- /dev/null +++ b/cmd/publisher/auth/common_internal_test.go @@ -0,0 +1,49 @@ +package auth + +import ( + "bytes" + "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/sha512" + "encoding/hex" + "math/big" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestEncodeECDSAP384Signature_FixedWidth(t *testing.T) { + // r is small enough that big.Int.Bytes() drops its leading zero bytes. The + // old append(r.Bytes(), s.Bytes()...) then produced fewer than 96 bytes, + // which the registry rejects with "invalid signature size for ECDSA P-384". + r := big.NewInt(1) + s := new(big.Int).SetBytes(bytes.Repeat([]byte{0xff}, 48)) + + sig := encodeECDSAP384Signature(r, s) + + require.Len(t, sig, 96) + // The verifier splits the signature at byte 48, so a fixed-width encoding + // must round-trip both components back exactly. + require.Zero(t, new(big.Int).SetBytes(sig[:48]).Cmp(r)) + require.Zero(t, new(big.Int).SetBytes(sig[48:]).Cmp(s)) +} + +func TestInProcessSigner_ECDSAP384_SignatureVerifies(t *testing.T) { + // Fixed, valid P-384 private scalar (well below the group order). + scalar := bytes.Repeat([]byte{0x11}, 48) + priv, err := parseRawPrivateKey(elliptic.P384(), scalar) + require.NoError(t, err) + + signer, err := NewInProcessSigner(hex.EncodeToString(scalar), AlgorithmECDSAP384) + require.NoError(t, err) + + timestamp, signature, err := signer.GetSignedTimestamp(context.Background()) + require.NoError(t, err) + require.Len(t, signature, 96) + + digest := sha512.Sum384([]byte(*timestamp)) + r := new(big.Int).SetBytes(signature[:48]) + s := new(big.Int).SetBytes(signature[48:]) + require.True(t, ecdsa.Verify(&priv.PublicKey, digest[:], r, s)) +}