123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- 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:]...)
- }
|