You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
147 lines
3.5 KiB
Go
147 lines
3.5 KiB
Go
2 years ago
|
package dsse
|
||
|
|
||
|
import (
|
||
|
"crypto"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
|
||
|
"golang.org/x/crypto/ssh"
|
||
|
)
|
||
|
|
||
|
/*
|
||
|
Verifier verifies a complete message against a signature and key.
|
||
|
If the message was hashed prior to signature generation, the verifier
|
||
|
must perform the same steps.
|
||
|
If KeyID returns successfully, only signature matching the key ID will be verified.
|
||
|
*/
|
||
|
type Verifier interface {
|
||
|
Verify(data, sig []byte) error
|
||
|
KeyID() (string, error)
|
||
|
Public() crypto.PublicKey
|
||
|
}
|
||
|
|
||
|
type EnvelopeVerifier struct {
|
||
|
providers []Verifier
|
||
|
threshold int
|
||
|
}
|
||
|
|
||
|
type AcceptedKey struct {
|
||
|
Public crypto.PublicKey
|
||
|
KeyID string
|
||
|
Sig Signature
|
||
|
}
|
||
|
|
||
|
func (ev *EnvelopeVerifier) Verify(e *Envelope) ([]AcceptedKey, error) {
|
||
|
if e == nil {
|
||
|
return nil, errors.New("cannot verify a nil envelope")
|
||
|
}
|
||
|
|
||
|
if len(e.Signatures) == 0 {
|
||
|
return nil, ErrNoSignature
|
||
|
}
|
||
|
|
||
|
// Decode payload (i.e serialized body)
|
||
|
body, err := e.DecodeB64Payload()
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
// Generate PAE(payloadtype, serialized body)
|
||
|
paeEnc := PAE(e.PayloadType, body)
|
||
|
|
||
|
// If *any* signature is found to be incorrect, it is skipped
|
||
|
var acceptedKeys []AcceptedKey
|
||
|
usedKeyids := make(map[string]string)
|
||
|
unverified_providers := ev.providers
|
||
|
for _, s := range e.Signatures {
|
||
|
sig, err := b64Decode(s.Sig)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
// Loop over the providers.
|
||
|
// If provider and signature include key IDs but do not match skip.
|
||
|
// If a provider recognizes the key, we exit
|
||
|
// the loop and use the result.
|
||
|
providers := unverified_providers
|
||
|
for i, v := range providers {
|
||
|
keyID, err := v.KeyID()
|
||
|
|
||
|
// Verifiers that do not provide a keyid will be generated one using public.
|
||
|
if err != nil || keyID == "" {
|
||
|
keyID, err = SHA256KeyID(v.Public())
|
||
|
if err != nil {
|
||
|
keyID = ""
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if s.KeyID != "" && keyID != "" && err == nil && s.KeyID != keyID {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
err = v.Verify(paeEnc, sig)
|
||
|
if err != nil {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
acceptedKey := AcceptedKey{
|
||
|
Public: v.Public(),
|
||
|
KeyID: keyID,
|
||
|
Sig: s,
|
||
|
}
|
||
|
unverified_providers = removeIndex(providers, i)
|
||
|
|
||
|
// See https://github.com/in-toto/in-toto/pull/251
|
||
|
if _, ok := usedKeyids[keyID]; ok {
|
||
|
fmt.Printf("Found envelope signed by different subkeys of the same main key, Only one of them is counted towards the step threshold, KeyID=%s\n", keyID)
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
usedKeyids[keyID] = ""
|
||
|
acceptedKeys = append(acceptedKeys, acceptedKey)
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Sanity if with some reflect magic this happens.
|
||
|
if ev.threshold <= 0 || ev.threshold > len(ev.providers) {
|
||
|
return nil, errors.New("Invalid threshold")
|
||
|
}
|
||
|
|
||
|
if len(usedKeyids) < ev.threshold {
|
||
|
return acceptedKeys, errors.New(fmt.Sprintf("Accepted signatures do not match threshold, Found: %d, Expected %d", len(acceptedKeys), ev.threshold))
|
||
|
}
|
||
|
|
||
|
return acceptedKeys, nil
|
||
|
}
|
||
|
|
||
|
func NewEnvelopeVerifier(v ...Verifier) (*EnvelopeVerifier, error) {
|
||
|
return NewMultiEnvelopeVerifier(1, v...)
|
||
|
}
|
||
|
|
||
|
func NewMultiEnvelopeVerifier(threshold int, p ...Verifier) (*EnvelopeVerifier, error) {
|
||
|
|
||
|
if threshold <= 0 || threshold > len(p) {
|
||
|
return nil, errors.New("Invalid threshold")
|
||
|
}
|
||
|
|
||
|
ev := EnvelopeVerifier{
|
||
|
providers: p,
|
||
|
threshold: threshold,
|
||
|
}
|
||
|
return &ev, nil
|
||
|
}
|
||
|
|
||
|
func SHA256KeyID(pub crypto.PublicKey) (string, error) {
|
||
|
// Generate public key fingerprint
|
||
|
sshpk, err := ssh.NewPublicKey(pub)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
fingerprint := ssh.FingerprintSHA256(sshpk)
|
||
|
return fingerprint, nil
|
||
|
}
|
||
|
|
||
|
func removeIndex(v []Verifier, index int) []Verifier {
|
||
|
return append(v[:index], v[index+1:]...)
|
||
|
}
|