mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 10:03:42 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			147 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			147 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
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:]...)
 | 
						|
}
 |