2024-10-17 13:40:17 -05:00
|
|
|
/*
|
2024-10-18 09:25:31 -05:00
|
|
|
Copyright Docker attest authors
|
2024-10-17 13:40:17 -05:00
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
*/
|
2024-10-18 09:25:31 -05:00
|
|
|
|
2024-04-22 12:22:15 -05:00
|
|
|
package attestation
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
2024-09-18 13:34:10 +01:00
|
|
|
"crypto"
|
2024-04-22 12:22:15 -05:00
|
|
|
"encoding/base64"
|
|
|
|
|
"fmt"
|
|
|
|
|
|
2024-09-02 16:17:50 +01:00
|
|
|
"github.com/docker/attest/signerverifier"
|
2024-04-22 12:22:15 -05:00
|
|
|
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
|
|
|
|
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
|
|
|
|
"github.com/secure-systems-lab/go-securesystemslib/dsse"
|
|
|
|
|
)
|
|
|
|
|
|
2024-09-18 13:34:10 +01:00
|
|
|
func VerifyDSSE(ctx context.Context, verifier Verifier, env *Envelope, opts *VerifyOptions) ([]byte, error) {
|
2024-04-22 12:22:15 -05:00
|
|
|
// enforce payload type
|
|
|
|
|
if !ValidPayloadType(env.PayloadType) {
|
|
|
|
|
return nil, fmt.Errorf("unsupported payload type %s", env.PayloadType)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(env.Signatures) == 0 {
|
|
|
|
|
return nil, fmt.Errorf("no signatures found")
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-01 15:35:15 +01:00
|
|
|
keys := make(map[string]*KeyMetadata, len(opts.Keys))
|
2024-06-06 09:59:32 +01:00
|
|
|
for _, key := range opts.Keys {
|
|
|
|
|
keys[key.ID] = key
|
2024-04-22 12:22:15 -05:00
|
|
|
}
|
|
|
|
|
|
2024-09-18 13:34:10 +01:00
|
|
|
payload, err := base64Encoding.DecodeString(env.Payload)
|
2024-04-22 12:22:15 -05:00
|
|
|
if err != nil {
|
2024-09-18 13:34:10 +01:00
|
|
|
return nil, fmt.Errorf("error failed to decode payload: %w", err)
|
2024-04-22 12:22:15 -05:00
|
|
|
}
|
|
|
|
|
|
2024-09-18 13:34:10 +01:00
|
|
|
encPayload := dsse.PAE(env.PayloadType, payload)
|
|
|
|
|
// verify signatures and transparency log entry
|
|
|
|
|
for _, sig := range env.Signatures {
|
|
|
|
|
// resolve public key used to sign
|
|
|
|
|
keyMeta, ok := keys[sig.KeyID]
|
|
|
|
|
if !ok {
|
|
|
|
|
return nil, fmt.Errorf("error key not found: %s", sig.KeyID)
|
2024-06-06 09:59:32 +01:00
|
|
|
}
|
|
|
|
|
|
2024-09-18 13:34:10 +01:00
|
|
|
if keyMeta.Distrust {
|
|
|
|
|
return nil, fmt.Errorf("key %s is distrusted", keyMeta.ID)
|
2024-06-06 09:59:32 +01:00
|
|
|
}
|
2024-09-18 13:34:10 +01:00
|
|
|
publicKey, err := keyMeta.ParsedKey()
|
2024-06-06 09:59:32 +01:00
|
|
|
if err != nil {
|
2024-09-18 13:34:10 +01:00
|
|
|
return nil, fmt.Errorf("failed to parse public key: %w", err)
|
2024-06-06 09:59:32 +01:00
|
|
|
}
|
2024-09-18 13:34:10 +01:00
|
|
|
// decode signature
|
|
|
|
|
signature, err := base64.StdEncoding.Strict().DecodeString(sig.Sig)
|
2024-06-06 09:59:32 +01:00
|
|
|
if err != nil {
|
2024-09-18 13:34:10 +01:00
|
|
|
return nil, fmt.Errorf("error failed to decode signature: %w", err)
|
2024-06-06 09:59:32 +01:00
|
|
|
}
|
2024-09-18 13:34:10 +01:00
|
|
|
|
|
|
|
|
err = verifier.VerifySignature(ctx, publicKey, encPayload, signature, opts)
|
2024-06-06 09:59:32 +01:00
|
|
|
if err != nil {
|
2024-09-18 13:34:10 +01:00
|
|
|
return nil, fmt.Errorf("error failed to verify signature: %w", err)
|
2024-06-06 09:59:32 +01:00
|
|
|
}
|
2024-09-18 13:34:10 +01:00
|
|
|
if err := verifier.VerifyLog(ctx, keyMeta, encPayload, sig, opts); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("error failed to verify transparency log entry: %w", err)
|
2024-06-06 09:59:32 +01:00
|
|
|
}
|
2024-04-22 12:22:15 -05:00
|
|
|
}
|
2024-06-06 09:59:32 +01:00
|
|
|
|
2024-09-18 13:34:10 +01:00
|
|
|
return payload, nil
|
2024-04-22 12:22:15 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ValidPayloadType(payloadType string) bool {
|
|
|
|
|
return payloadType == intoto.PayloadType || payloadType == ociv1.MediaTypeDescriptor
|
|
|
|
|
}
|
2024-09-18 13:34:10 +01:00
|
|
|
|
|
|
|
|
func (km *KeyMetadata) ParsedKey() (crypto.PublicKey, error) {
|
|
|
|
|
if km.publicKey != nil {
|
|
|
|
|
return km.publicKey, nil
|
|
|
|
|
}
|
|
|
|
|
publicKey, err := signerverifier.ParsePublicKey([]byte(km.PEM))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to parse public key: %w", err)
|
|
|
|
|
}
|
|
|
|
|
km.publicKey = publicKey
|
|
|
|
|
return publicKey, nil
|
|
|
|
|
}
|