Skip to content

Commit

Permalink
adding tests and some other changes
Browse files Browse the repository at this point in the history
Signed-off-by: chaosinthecrd <[email protected]>
  • Loading branch information
ChaosInTheCRD committed Jan 24, 2024
1 parent eda08d5 commit d7d9b74
Show file tree
Hide file tree
Showing 10 changed files with 656 additions and 277 deletions.
23 changes: 20 additions & 3 deletions policy/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,14 @@ import (
"bytes"
"context"
"crypto/x509"
"fmt"
"strings"
"time"

"github.com/in-toto/go-witness/attestation"
"github.com/in-toto/go-witness/cryptoutil"
"github.com/in-toto/go-witness/log"
"github.com/in-toto/go-witness/signer/kms"
"github.com/in-toto/go-witness/source"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -54,10 +57,24 @@ type PublicKey struct {
// PublicKeyVerifiers returns verifiers for each of the policy's embedded public keys grouped by the key's ID
func (p Policy) PublicKeyVerifiers() (map[string]cryptoutil.Verifier, error) {
verifiers := make(map[string]cryptoutil.Verifier)
var verifier cryptoutil.Verifier
var err error

for _, key := range p.PublicKeys {
verifier, err := cryptoutil.NewVerifierFromReader(bytes.NewReader(key.Key))
if err != nil {
return nil, err
for _, prefix := range kms.SupportedProviders() {
if strings.HasPrefix(key.KeyID, prefix) {
verifier, err = kms.New(kms.WithRef(key.KeyID), kms.WithHash("SHA256")).Verifier(context.TODO())
if err != nil {
return nil, fmt.Errorf("KMS Key ID recognized but not valid: %w", err)
}
}
}

if verifier == nil {
verifier, err = cryptoutil.NewVerifierFromReader(bytes.NewReader(key.Key))
if err != nil {
return nil, err
}
}

keyID, err := verifier.KeyID()
Expand Down
142 changes: 54 additions & 88 deletions signer/kms/aws/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,17 @@ import (
ttlcache "github.com/jellydator/ttlcache/v3"
)

type client interface {
sign(ctx context.Context, digest []byte, _ crypto.Hash) ([]byte, error)
verifyRemotely(ctx context.Context, sig, digest []byte) error
verify(ctx context.Context, sig, message io.Reader) error
fetchCMK(ctx context.Context) (*cmk, error)
getHashFunc(ctx context.Context) (crypto.Hash, error)
setupClient(ctx context.Context, ksp *kms.KMSSignerProvider) (err error)
fetchKeyMetadata(ctx context.Context) (*types.KeyMetadata, error)
fetchPublicKey(ctx context.Context) (crypto.PublicKey, error)
}

func init() {
kms.AddProvider(ReferenceScheme, func(ctx context.Context, ksp *kms.KMSSignerProvider) (cryptoutil.Signer, error) {
return LoadSignerVerifier(ctx, ksp)
Expand Down Expand Up @@ -159,49 +170,6 @@ func (a *awsClient) setupClient(ctx context.Context, ksp *kms.KMSSignerProvider)
return
}

type cmk struct {
KeyMetadata *types.KeyMetadata
PublicKey crypto.PublicKey
}

func (c *cmk) HashFunc() crypto.Hash {
switch c.KeyMetadata.SigningAlgorithms[0] {
case types.SigningAlgorithmSpecRsassaPssSha256, types.SigningAlgorithmSpecRsassaPkcs1V15Sha256, types.SigningAlgorithmSpecEcdsaSha256:
return crypto.SHA256
case types.SigningAlgorithmSpecRsassaPssSha384, types.SigningAlgorithmSpecRsassaPkcs1V15Sha384, types.SigningAlgorithmSpecEcdsaSha384:
return crypto.SHA384
case types.SigningAlgorithmSpecRsassaPssSha512, types.SigningAlgorithmSpecRsassaPkcs1V15Sha512, types.SigningAlgorithmSpecEcdsaSha512:
return crypto.SHA512
default:
return 0
}
}

func (c *cmk) Verifier() (cryptoutil.Verifier, error) {
switch c.KeyMetadata.SigningAlgorithms[0] {
case types.SigningAlgorithmSpecRsassaPssSha256, types.SigningAlgorithmSpecRsassaPssSha384, types.SigningAlgorithmSpecRsassaPssSha512:
pub, ok := c.PublicKey.(*rsa.PublicKey)
if !ok {
return nil, fmt.Errorf("public key is not rsa")
}
return cryptoutil.NewRSAVerifier(pub, c.HashFunc()), nil
case types.SigningAlgorithmSpecRsassaPkcs1V15Sha256, types.SigningAlgorithmSpecRsassaPkcs1V15Sha384, types.SigningAlgorithmSpecRsassaPkcs1V15Sha512:
pub, ok := c.PublicKey.(*rsa.PublicKey)
if !ok {
return nil, fmt.Errorf("public key is not rsa")
}
return cryptoutil.NewRSAVerifier(pub, c.HashFunc()), nil
case types.SigningAlgorithmSpecEcdsaSha256, types.SigningAlgorithmSpecEcdsaSha384, types.SigningAlgorithmSpecEcdsaSha512:
pub, ok := c.PublicKey.(*ecdsa.PublicKey)
if !ok {
return nil, fmt.Errorf("public key is not ecdsa")
}
return cryptoutil.NewECDSAVerifier(pub, c.HashFunc()), nil
default:
return nil, fmt.Errorf("signing algorithm unsupported")
}
}

func (a *awsClient) fetchCMK(ctx context.Context) (*cmk, error) {
var err error
cmk := &cmk{}
Expand Down Expand Up @@ -245,51 +213,6 @@ func (a *awsClient) getCMK(ctx context.Context) (*cmk, error) {
return nil, lerr
}

func (a *awsClient) createKey(ctx context.Context, algorithm string) (crypto.PublicKey, error) {
if a.alias == "" {
return nil, errors.New("must use alias key format")
}

// look for existing key first
cmk, err := a.getCMK(ctx)
if err == nil {
out := cmk.PublicKey
return out, nil
}

// return error if not *kms.NotFoundException
var errNotFound *types.NotFoundException
if !errors.As(err, &errNotFound) {
return nil, fmt.Errorf("looking up key: %w", err)
}

usage := types.KeyUsageTypeSignVerify
description := "Created by Witness"
key, err := a.client.CreateKey(ctx, &akms.CreateKeyInput{
CustomerMasterKeySpec: types.CustomerMasterKeySpec(algorithm),
KeyUsage: usage,
Description: &description,
})
if err != nil {
return nil, fmt.Errorf("creating key: %w", err)
}

_, err = a.client.CreateAlias(ctx, &akms.CreateAliasInput{
AliasName: &a.alias,
TargetKeyId: key.KeyMetadata.KeyId,
})
if err != nil {
return nil, fmt.Errorf("creating alias %q: %w", a.alias, err)
}

cmk, err = a.getCMK(ctx)
if err != nil {
return nil, fmt.Errorf("retrieving PublicKey from cache: %w", err)
}

return cmk.PublicKey, err
}

// At the moment this function lies unused, but it is here for future if necessary
func (a *awsClient) verify(ctx context.Context, sig, message io.Reader) error {
cmk, err := a.getCMK(ctx)
Expand Down Expand Up @@ -374,3 +297,46 @@ func (a *awsClient) fetchKeyMetadata(ctx context.Context) (*types.KeyMetadata, e
}
return out.KeyMetadata, nil
}

type cmk struct {
KeyMetadata *types.KeyMetadata
PublicKey crypto.PublicKey
}

func (c *cmk) HashFunc() crypto.Hash {
switch c.KeyMetadata.SigningAlgorithms[0] {
case types.SigningAlgorithmSpecRsassaPssSha256, types.SigningAlgorithmSpecRsassaPkcs1V15Sha256, types.SigningAlgorithmSpecEcdsaSha256:
return crypto.SHA256
case types.SigningAlgorithmSpecRsassaPssSha384, types.SigningAlgorithmSpecRsassaPkcs1V15Sha384, types.SigningAlgorithmSpecEcdsaSha384:
return crypto.SHA384
case types.SigningAlgorithmSpecRsassaPssSha512, types.SigningAlgorithmSpecRsassaPkcs1V15Sha512, types.SigningAlgorithmSpecEcdsaSha512:
return crypto.SHA512
default:
return 0
}
}

func (c *cmk) Verifier() (cryptoutil.Verifier, error) {
switch c.KeyMetadata.SigningAlgorithms[0] {
case types.SigningAlgorithmSpecRsassaPssSha256, types.SigningAlgorithmSpecRsassaPssSha384, types.SigningAlgorithmSpecRsassaPssSha512:
pub, ok := c.PublicKey.(*rsa.PublicKey)
if !ok {
return nil, fmt.Errorf("public key is not rsa")
}
return cryptoutil.NewRSAVerifier(pub, c.HashFunc()), nil
case types.SigningAlgorithmSpecRsassaPkcs1V15Sha256, types.SigningAlgorithmSpecRsassaPkcs1V15Sha384, types.SigningAlgorithmSpecRsassaPkcs1V15Sha512:
pub, ok := c.PublicKey.(*rsa.PublicKey)
if !ok {
return nil, fmt.Errorf("public key is not rsa")
}
return cryptoutil.NewRSAVerifier(pub, c.HashFunc()), nil
case types.SigningAlgorithmSpecEcdsaSha256, types.SigningAlgorithmSpecEcdsaSha384, types.SigningAlgorithmSpecEcdsaSha512:
pub, ok := c.PublicKey.(*ecdsa.PublicKey)
if !ok {
return nil, fmt.Errorf("public key is not ecdsa")
}
return cryptoutil.NewECDSAVerifier(pub, c.HashFunc()), nil
default:
return nil, fmt.Errorf("signing algorithm unsupported")
}
}
182 changes: 182 additions & 0 deletions signer/kms/aws/fakeclient.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
// Copyright 2023 The Witness Contributors
//
// 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 aws implement the interface with amazon aws kms service
package aws

import (
"bytes"
"context"
"crypto"
"crypto/rand"
"crypto/rsa"
"time"

akms "github.com/aws/aws-sdk-go-v2/service/kms"
"github.com/aws/aws-sdk-go-v2/service/kms/types"
"github.com/in-toto/go-witness/cryptoutil"
"github.com/in-toto/go-witness/signer/kms"
ttlcache "github.com/jellydator/ttlcache/v3"
)

var (
aid = "012345678901"
arn = "arn:aws:kms:us-west-2:012345678901:key/12345678-1234-1234-1234-123456789012"
)

func createRsaKey() (*rsa.PrivateKey, *rsa.PublicKey, error) {
privKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, nil, err
}

return privKey, &privKey.PublicKey, nil
}

func createTestKey() (cryptoutil.Signer, cryptoutil.Verifier, error) {
privKey, pubKey, err := createRsaKey()
if err != nil {
return nil, nil, err
}

signer := cryptoutil.NewRSASigner(privKey, crypto.SHA256)
verifier := cryptoutil.NewRSAVerifier(pubKey, crypto.SHA256)
return signer, verifier, nil
}

type fakeAWSClient struct {
client *akms.Client
endpoint string
keyID string
alias string
keyCache *ttlcache.Cache[string, cmk]
privateKey *rsa.PrivateKey
hash crypto.Hash
}

func newFakeAWSClient(ctx context.Context, ksp *kms.KMSSignerProvider) (*fakeAWSClient, error) {
a, err := newAWSClient(ctx, ksp)
if err != nil {
return nil, err
}

c := &fakeAWSClient{
client: a.client,
endpoint: a.endpoint,
keyID: a.keyID,
alias: a.alias,
keyCache: a.keyCache,
hash: ksp.HashFunc,
}

return c, nil
}

func (a *fakeAWSClient) fetchCMK(ctx context.Context) (*cmk, error) {
var err error
cmk := &cmk{}
cmk.PublicKey, err = a.fetchPublicKey(ctx)
if err != nil {
return nil, err
}
cmk.KeyMetadata, err = a.fetchKeyMetadata(ctx)
if err != nil {
return nil, err
}
return cmk, nil
}

func (a *fakeAWSClient) getHashFunc(ctx context.Context) (crypto.Hash, error) {
cmk, err := a.getCMK(ctx)
if err != nil {
return 0, err
}
return cmk.HashFunc(), nil
}

func (a *fakeAWSClient) getCMK(ctx context.Context) (*cmk, error) {
var lerr error
loader := ttlcache.LoaderFunc[string, cmk](
func(c *ttlcache.Cache[string, cmk], key string) *ttlcache.Item[string, cmk] {
var k *cmk
k, lerr = a.fetchCMK(ctx)
if lerr == nil {
return c.Set(cacheKey, *k, time.Second*300)
}
return nil
},
)

item := a.keyCache.Get(cacheKey, ttlcache.WithLoader[string, cmk](loader))
if lerr == nil {
cmk := item.Value()
return &cmk, nil
}
return nil, lerr
}

// At the moment this function lies unused, but it is here for future if necessary

func (a *fakeAWSClient) verifyRemotely(ctx context.Context, sig, digest []byte) error {
c, err := a.getCMK(ctx)
if err != nil {
return err
}

v, err := cryptoutil.NewVerifier(c.PublicKey, cryptoutil.VerifyWithHash(a.hash))
if err != nil {
return err
}

return v.Verify(bytes.NewReader(digest), sig)
}

func (a *fakeAWSClient) sign(ctx context.Context, digest []byte, _ crypto.Hash) ([]byte, error) {
_, err := a.getCMK(ctx)
if err != nil {
return nil, err
}

signer, err := cryptoutil.NewSigner(a.privateKey, cryptoutil.SignWithHash(a.hash))
if err != nil {
return nil, err
}

s, err := signer.Sign(bytes.NewReader(digest))
if err != nil {
return nil, err
}

return s, nil
}

func (a *fakeAWSClient) fetchPublicKey(ctx context.Context) (crypto.PublicKey, error) {
k, p, err := createRsaKey()
if err != nil {
return nil, err
}
a.privateKey = k

return p, nil
}

func (a *fakeAWSClient) fetchKeyMetadata(ctx context.Context) (*types.KeyMetadata, error) {
km := &types.KeyMetadata{
KeyId: &a.keyID,
AWSAccountId: &aid,
Arn: &arn,
}

return km, nil
}
Loading

0 comments on commit d7d9b74

Please sign in to comment.