Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 45 additions & 3 deletions algorithm.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,56 @@ const (

// ECDSA w/ SHA-256 by RFC 8152.
// Requires an available crypto.SHA256.
//
// Note: use [AlgorithmESP256] only if you are certain that the recipient
// supports it. For maximum interoperability and backward compatibility,
// [AlgorithmES256] should remain the default choice.
AlgorithmES256 Algorithm = -7

// ECDSA using P-256 curve and SHA-256 by RFC 9864.
// Requires an available crypto.SHA256.
AlgorithmESP256 Algorithm = -9

// ECDSA w/ SHA-384 by RFC 8152.
// Requires an available crypto.SHA384.
//
// Note: use [AlgorithmESP384] only if you are certain that the recipient
// supports it. For maximum interoperability and backward compatibility,
// [AlgorithmES384] should remain the default choice.
AlgorithmES384 Algorithm = -35

// ECDSA using P-384 curve and SHA-384 by RFC 9864.
// Requires an available crypto.SHA384.
AlgorithmESP384 Algorithm = -51

// ECDSA w/ SHA-512 by RFC 8152.
// Requires an available crypto.SHA512.
//
// Note: use [AlgorithmESP512] only if you are certain that the recipient
// supports it. For maximum interoperability and backward compatibility,
// [AlgorithmES512] should remain the default choice.
AlgorithmES512 Algorithm = -36

// ECDSA using P-521 curve and SHA-512 by RFC 9864.
// Requires an available crypto.SHA512.
AlgorithmESP512 Algorithm = -52

// PureEdDSA by RFC 8152.
//
// Deprecated: use [AlgorithmEdDSA] instead, which has
// the same value but with a more accurate name.
AlgorithmEd25519 Algorithm = -8

// PureEdDSA by RFC 8152.
//
// Note: use [AlgorithmEd25519EdDSA] only if you are certain that the recipient
// supports it. For maximum interoperability and backward compatibility,
// AlgorithmEdDSA should remain the default choice.
AlgorithmEdDSA Algorithm = -8

// EdDSA restricted to the Ed25519 curve, as defined in RFC 9864.
// This algorithm is newer and may not be supported by all COSE libraries.
AlgorithmEd25519EdDSA Algorithm = -19
)

// Signature algorithms known, but not supported by this library.
Expand Down Expand Up @@ -104,14 +136,24 @@ func (a Algorithm) String() string {
return "RS512"
case AlgorithmES256:
return "ES256"
case AlgorithmESP256:
return "ESP256"
case AlgorithmES384:
return "ES384"
case AlgorithmESP384:
return "ESP384"
case AlgorithmES512:
return "ES512"
case AlgorithmESP512:
return "ESP512"
case AlgorithmEdDSA:
// As stated in RFC 8152 section 8.2, only the pure EdDSA version is
// used for COSE.
return "EdDSA"
case AlgorithmEd25519EdDSA:
// As stated in RFC 8152 section 8.2, only the pure EdDSA version is
// used for COSE.
return "Ed25519"
case AlgorithmReserved:
return "Reserved"
case AlgorithmSHA256:
Expand All @@ -129,11 +171,11 @@ func (a Algorithm) String() string {
// library.
func (a Algorithm) hashFunc() crypto.Hash {
switch a {
case AlgorithmPS256, AlgorithmES256, AlgorithmSHA256:
case AlgorithmPS256, AlgorithmES256, AlgorithmESP256, AlgorithmSHA256:
return crypto.SHA256
case AlgorithmPS384, AlgorithmES384, AlgorithmSHA384:
case AlgorithmPS384, AlgorithmES384, AlgorithmESP384, AlgorithmSHA384:
return crypto.SHA384
case AlgorithmPS512, AlgorithmES512, AlgorithmSHA512:
case AlgorithmPS512, AlgorithmES512, AlgorithmESP512, AlgorithmSHA512:
return crypto.SHA512
default:
return 0
Expand Down
128 changes: 82 additions & 46 deletions ecdsa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ func generateTestECDSAKey(t *testing.T) *ecdsa.PrivateKey {
func Test_ecdsaKeySigner(t *testing.T) {
key := generateTestECDSAKey(t)
testSignVerify(t, AlgorithmES256, key, false)
testSignVerify(t, AlgorithmESP256, key, false)
}

func Test_ecdsaCryptoSigner(t *testing.T) {
Expand All @@ -184,6 +185,7 @@ func Test_ecdsaCryptoSigner(t *testing.T) {
Signer: generateTestECDSAKey(t),
}
testSignVerify(t, AlgorithmES256, wrappedKey, true)
testSignVerify(t, AlgorithmESP256, wrappedKey, true)
}

func testSignVerify(t *testing.T, alg Algorithm, key crypto.Signer, isCryptoSigner bool) {
Expand Down Expand Up @@ -256,6 +258,7 @@ func Test_ecdsaBadCryptoSigner_SignFailure(t *testing.T) {
err: errors.New("sign failure"),
}
testSignFailure(t, AlgorithmES256, badSigner)
testSignFailure(t, AlgorithmESP256, badSigner)
}

func Test_ecdsaBadCryptoSigner_BadSignature(t *testing.T) {
Expand All @@ -267,6 +270,7 @@ func Test_ecdsaBadCryptoSigner_BadSignature(t *testing.T) {
signature: nil,
}
testSignFailure(t, AlgorithmES256, badSigner)
testSignFailure(t, AlgorithmESP256, badSigner)

// malformed signature: bad r
sig, err := asn1.Marshal(struct {
Expand All @@ -283,6 +287,7 @@ func Test_ecdsaBadCryptoSigner_BadSignature(t *testing.T) {
signature: sig,
}
testSignFailure(t, AlgorithmES256, badSigner)
testSignFailure(t, AlgorithmESP256, badSigner)

// malformed signature: bad s
sig, err = asn1.Marshal(struct {
Expand All @@ -299,6 +304,7 @@ func Test_ecdsaBadCryptoSigner_BadSignature(t *testing.T) {
signature: sig,
}
testSignFailure(t, AlgorithmES256, badSigner)
testSignFailure(t, AlgorithmESP256, badSigner)
}

func Test_ecdsaKeySigner_SignHashFailure(t *testing.T) {
Expand Down Expand Up @@ -332,69 +338,99 @@ func testSignFailure(t *testing.T, alg Algorithm, key crypto.Signer) {
}

func Test_ecdsaVerifier_Verify_Success(t *testing.T) {
// generate key
alg := AlgorithmES256
key := generateTestECDSAKey(t)
tests := []struct {
name string
alg Algorithm
}{
{"ES256", AlgorithmES256},
{"ESP256", AlgorithmESP256},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// generate key
key := generateTestECDSAKey(t)

// generate a valid signature
content, sig := signTestData(t, alg, key)
// generate a valid signature
content, sig := signTestData(t, tt.alg, key)

// set up verifier
verifier, err := NewVerifier(alg, key.Public())
if err != nil {
t.Fatalf("NewVerifier() error = %v", err)
}
if _, ok := verifier.(*ecdsaVerifier); !ok {
t.Fatalf("NewVerifier() type = %v, want *ecdsaVerifier", reflect.TypeOf(verifier))
}
if got := verifier.Algorithm(); got != alg {
t.Fatalf("Algorithm() = %v, want %v", got, alg)
}
// set up verifier
verifier, err := NewVerifier(tt.alg, key.Public())
if err != nil {
t.Fatalf("NewVerifier() error = %v", err)
}
if _, ok := verifier.(*ecdsaVerifier); !ok {
t.Fatalf("NewVerifier() type = %v, want *ecdsaVerifier", reflect.TypeOf(verifier))
}
if got := verifier.Algorithm(); got != tt.alg {
t.Fatalf("Algorithm() = %v, want %v", got, tt.alg)
}

// verify round trip
if err := verifier.Verify(content, sig); err != nil {
t.Fatalf("ecdsaVerifier.Verify() error = %v", err)
// verify round trip
if err := verifier.Verify(content, sig); err != nil {
t.Fatalf("ecdsaVerifier.Verify() error = %v", err)
}
})
}
}

func Test_ecdsaVerifier_Verify_AlgorithmMismatch(t *testing.T) {
// generate key
alg := AlgorithmES256
key := generateTestECDSAKey(t)
tests := []struct {
name string
alg Algorithm
}{
{"ES256", AlgorithmES256},
{"ESP256", AlgorithmESP256},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// generate key
key := generateTestECDSAKey(t)

// generate a valid signature
content, sig := signTestData(t, alg, key)
// generate a valid signature
content, sig := signTestData(t, tt.alg, key)

// set up verifier with a different algorithm
verifier := &ecdsaVerifier{
alg: AlgorithmES512,
key: &key.PublicKey,
}
// set up verifier with a different algorithm
verifier := &ecdsaVerifier{
alg: AlgorithmES512,
key: &key.PublicKey,
}

// verification should fail on algorithm mismatch
if err := verifier.Verify(content, sig); err != ErrVerification {
t.Fatalf("ecdsaVerifier.Verify() error = %v, wantErr %v", err, ErrVerification)
// verification should fail on algorithm mismatch
if err := verifier.Verify(content, sig); err != ErrVerification {
t.Fatalf("ecdsaVerifier.Verify() error = %v, wantErr %v", err, ErrVerification)
}
})
}
}

func Test_ecdsaVerifier_Verify_KeyMismatch(t *testing.T) {
// generate key
alg := AlgorithmES256
key := generateTestECDSAKey(t)
tests := []struct {
name string
alg Algorithm
}{
{"ES256", AlgorithmES256},
{"ESP256", AlgorithmESP256},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// generate key
key := generateTestECDSAKey(t)

// generate a valid signature
content, sig := signTestData(t, alg, key)
// generate a valid signature
content, sig := signTestData(t, tt.alg, key)

// set up verifier with a different key / new key
key = generateTestECDSAKey(t)
verifier := &ecdsaVerifier{
alg: alg,
key: &key.PublicKey,
}
// set up verifier with a different key / new key
key = generateTestECDSAKey(t)
verifier := &ecdsaVerifier{
alg: tt.alg,
key: &key.PublicKey,
}

// verification should fail on key mismatch
if err := verifier.Verify(content, sig); err != ErrVerification {
t.Fatalf("ecdsaVerifier.Verify() error = %v, wantErr %v", err, ErrVerification)
// verification should fail on key mismatch
if err := verifier.Verify(content, sig); err != ErrVerification {
t.Fatalf("ecdsaVerifier.Verify() error = %v, wantErr %v", err, ErrVerification)
}
})
}
}

Expand Down
6 changes: 4 additions & 2 deletions ed25519.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ import (

// ed25519Signer is a Pure EdDSA based signer with a generic crypto.Signer.
type ed25519Signer struct {
alg Algorithm
key crypto.Signer
}

// Algorithm returns the signing algorithm associated with the private key.
func (es *ed25519Signer) Algorithm() Algorithm {
return AlgorithmEdDSA
return es.alg
}

// Sign signs message content with the private key, possibly using entropy from
Expand All @@ -29,12 +30,13 @@ func (es *ed25519Signer) Sign(rand io.Reader, content []byte) ([]byte, error) {

// ed25519Verifier is a Pure EdDSA based verifier with golang built-in keys.
type ed25519Verifier struct {
alg Algorithm
key ed25519.PublicKey
}

// Algorithm returns the signing algorithm associated with the public key.
func (ev *ed25519Verifier) Algorithm() Algorithm {
return AlgorithmEdDSA
return ev.alg
}

// Verify verifies message content with the public key, returning nil for
Expand Down
Loading