Skip to content

Commit

Permalink
Merge branch 'main' into feat/delta_crl
Browse files Browse the repository at this point in the history
  • Loading branch information
JeyJeyGao authored Dec 9, 2024
2 parents 934698a + 2cd55de commit c506b40
Show file tree
Hide file tree
Showing 14 changed files with 749 additions and 284 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.22
require (
github.com/fxamacker/cbor/v2 v2.7.0
github.com/golang-jwt/jwt/v4 v4.5.1
github.com/notaryproject/tspclient-go v0.2.1-0.20241030015323-90a141e7525c
github.com/notaryproject/tspclient-go v1.0.0-rc.1
github.com/veraison/go-cose v1.3.0
golang.org/x/crypto v0.29.0
)
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/notaryproject/tspclient-go v0.2.1-0.20241030015323-90a141e7525c h1:bX6gGxFw9+DShmYTgbD+vr6neF1SoXIMUU2fDgdLsfA=
github.com/notaryproject/tspclient-go v0.2.1-0.20241030015323-90a141e7525c/go.mod h1:LGyA/6Kwd2FlM0uk8Vc5il3j0CddbWSHBj/4kxQDbjs=
github.com/notaryproject/tspclient-go v1.0.0-rc.1 h1:KcHxlqg6Adt4kzGLw012i0YMLlwGwToiR129c6IQ7Ys=
github.com/notaryproject/tspclient-go v1.0.0-rc.1/go.mod h1:LGyA/6Kwd2FlM0uk8Vc5il3j0CddbWSHBj/4kxQDbjs=
github.com/veraison/go-cose v1.3.0 h1:2/H5w8kdSpQJyVtIhx8gmwPJ2uSz1PkyWFx0idbd7rk=
github.com/veraison/go-cose v1.3.0/go.mod h1:df09OV91aHoQWLmy1KsDdYiagtXgyAwAl8vFeFn1gMc=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
Expand Down
121 changes: 121 additions & 0 deletions internal/algorithm/algorithm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package algorithm includes signature algorithms accepted by Notary Project
package algorithm

import (
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"crypto/x509"
"errors"
"fmt"
)

// Algorithm defines the signature algorithm.
type Algorithm int

// Signature algorithms supported by this library.
//
// Reference: https://github.com/notaryproject/notaryproject/blob/main/specs/signature-specification.md#algorithm-selection
const (
AlgorithmPS256 Algorithm = 1 + iota // RSASSA-PSS with SHA-256
AlgorithmPS384 // RSASSA-PSS with SHA-384
AlgorithmPS512 // RSASSA-PSS with SHA-512
AlgorithmES256 // ECDSA on secp256r1 with SHA-256
AlgorithmES384 // ECDSA on secp384r1 with SHA-384
AlgorithmES512 // ECDSA on secp521r1 with SHA-512
)

// Hash returns the hash function of the algorithm.
func (alg Algorithm) Hash() crypto.Hash {
switch alg {
case AlgorithmPS256, AlgorithmES256:
return crypto.SHA256
case AlgorithmPS384, AlgorithmES384:
return crypto.SHA384
case AlgorithmPS512, AlgorithmES512:
return crypto.SHA512
}
return 0
}

// KeyType defines the key type.
type KeyType int

const (
KeyTypeRSA KeyType = 1 + iota // KeyType RSA
KeyTypeEC // KeyType EC
)

// KeySpec defines a key type and size.
type KeySpec struct {
// KeyType is the type of the key.
Type KeyType

// KeySize is the size of the key in bits.
Size int
}

// SignatureAlgorithm returns the signing algorithm associated with the KeySpec.
func (k KeySpec) SignatureAlgorithm() Algorithm {
switch k.Type {
case KeyTypeEC:
switch k.Size {
case 256:
return AlgorithmES256
case 384:
return AlgorithmES384
case 521:
return AlgorithmES512
}
case KeyTypeRSA:
switch k.Size {
case 2048:
return AlgorithmPS256
case 3072:
return AlgorithmPS384
case 4096:
return AlgorithmPS512
}
}
return 0
}

// ExtractKeySpec extracts KeySpec from the signing certificate.
func ExtractKeySpec(signingCert *x509.Certificate) (KeySpec, error) {
switch key := signingCert.PublicKey.(type) {
case *rsa.PublicKey:
switch bitSize := key.Size() << 3; bitSize {
case 2048, 3072, 4096:
return KeySpec{
Type: KeyTypeRSA,
Size: bitSize,
}, nil
default:
return KeySpec{}, fmt.Errorf("rsa key size %d bits is not supported", bitSize)
}
case *ecdsa.PublicKey:
switch bitSize := key.Curve.Params().BitSize; bitSize {
case 256, 384, 521:
return KeySpec{
Type: KeyTypeEC,
Size: bitSize,
}, nil
default:
return KeySpec{}, fmt.Errorf("ecdsa key size %d bits is not supported", bitSize)
}
}
return KeySpec{}, errors.New("unsupported public key type")
}
244 changes: 244 additions & 0 deletions internal/algorithm/algorithm_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package algorithm

import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"reflect"
"strconv"
"testing"

"github.com/notaryproject/notation-core-go/testhelper"
)

func TestHash(t *testing.T) {
tests := []struct {
name string
alg Algorithm
expect crypto.Hash
}{
{
name: "PS256",
alg: AlgorithmPS256,
expect: crypto.SHA256,
},
{
name: "ES256",
alg: AlgorithmES256,
expect: crypto.SHA256,
},
{
name: "PS384",
alg: AlgorithmPS384,
expect: crypto.SHA384,
},
{
name: "ES384",
alg: AlgorithmES384,
expect: crypto.SHA384,
},
{
name: "PS512",
alg: AlgorithmPS512,
expect: crypto.SHA512,
},
{
name: "ES512",
alg: AlgorithmES512,
expect: crypto.SHA512,
},
{
name: "UnsupportedAlgorithm",
alg: 0,
expect: 0,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
hash := tt.alg.Hash()
if hash != tt.expect {
t.Fatalf("Expected %v, got %v", tt.expect, hash)
}
})
}
}

func TestSignatureAlgorithm(t *testing.T) {
tests := []struct {
name string
keySpec KeySpec
expect Algorithm
}{
{
name: "EC 256",
keySpec: KeySpec{
Type: KeyTypeEC,
Size: 256,
},
expect: AlgorithmES256,
},
{
name: "EC 384",
keySpec: KeySpec{
Type: KeyTypeEC,
Size: 384,
},
expect: AlgorithmES384,
},
{
name: "EC 521",
keySpec: KeySpec{
Type: KeyTypeEC,
Size: 521,
},
expect: AlgorithmES512,
},
{
name: "RSA 2048",
keySpec: KeySpec{
Type: KeyTypeRSA,
Size: 2048,
},
expect: AlgorithmPS256,
},
{
name: "RSA 3072",
keySpec: KeySpec{
Type: KeyTypeRSA,
Size: 3072,
},
expect: AlgorithmPS384,
},
{
name: "RSA 4096",
keySpec: KeySpec{
Type: KeyTypeRSA,
Size: 4096,
},
expect: AlgorithmPS512,
},
{
name: "Unsupported key spec",
keySpec: KeySpec{
Type: 0,
Size: 0,
},
expect: 0,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
alg := tt.keySpec.SignatureAlgorithm()
if alg != tt.expect {
t.Errorf("unexpected signature algorithm: %v, expect: %v", alg, tt.expect)
}
})
}
}

func TestExtractKeySpec(t *testing.T) {
type testCase struct {
name string
cert *x509.Certificate
expect KeySpec
expectErr bool
}
// invalid cases
tests := []testCase{
{
name: "RSA wrong size",
cert: testhelper.GetUnsupportedRSACert().Cert,
expect: KeySpec{},
expectErr: true,
},
{
name: "ECDSA wrong size",
cert: testhelper.GetUnsupportedECCert().Cert,
expect: KeySpec{},
expectErr: true,
},
{
name: "Unsupported type",
cert: &x509.Certificate{
PublicKey: ed25519.PublicKey{},
},
expect: KeySpec{},
expectErr: true,
},
}

// append valid RSA cases
for _, k := range []int{2048, 3072, 4096} {
rsaRoot := testhelper.GetRSARootCertificate()
priv, _ := rsa.GenerateKey(rand.Reader, k)

certTuple := testhelper.GetRSACertTupleWithPK(
priv,
"Test RSA_"+strconv.Itoa(priv.Size()),
&rsaRoot,
)
tests = append(tests, testCase{
name: "RSA " + strconv.Itoa(k),
cert: certTuple.Cert,
expect: KeySpec{
Type: KeyTypeRSA,
Size: k,
},
expectErr: false,
})
}

// append valid EDCSA cases
for _, curve := range []elliptic.Curve{elliptic.P256(), elliptic.P384(), elliptic.P521()} {
ecdsaRoot := testhelper.GetECRootCertificate()
priv, _ := ecdsa.GenerateKey(curve, rand.Reader)
bitSize := priv.Params().BitSize

certTuple := testhelper.GetECDSACertTupleWithPK(
priv,
"Test EC_"+strconv.Itoa(bitSize),
&ecdsaRoot,
)
tests = append(tests, testCase{
name: "EC " + strconv.Itoa(bitSize),
cert: certTuple.Cert,
expect: KeySpec{
Type: KeyTypeEC,
Size: bitSize,
},
expectErr: false,
})
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
keySpec, err := ExtractKeySpec(tt.cert)

if (err != nil) != tt.expectErr {
t.Errorf("error = %v, expectErr = %v", err, tt.expectErr)
}
if !reflect.DeepEqual(keySpec, tt.expect) {
t.Errorf("expect %+v, got %+v", tt.expect, keySpec)
}
})
}
}
Binary file added internal/timestamp/testdata/tsaRootCert.cer
Binary file not shown.
Binary file removed internal/timestamp/testdata/tsaRootCert.crt
Binary file not shown.
Loading

0 comments on commit c506b40

Please sign in to comment.