// // Copyright 2021 The Sigstore Authors. // // 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 minisign import ( "bytes" "crypto/ed25519" "crypto/sha256" "encoding/base64" "encoding/hex" "errors" "fmt" "io" "strings" minisign "github.com/jedisct1/go-minisign" "github.com/sigstore/rekor/pkg/pki/identity" "github.com/sigstore/sigstore/pkg/cryptoutils" sigsig "github.com/sigstore/sigstore/pkg/signature" "golang.org/x/crypto/blake2b" ) // Signature Signature that follows the minisign standard; supports both minisign and signify generated signatures type Signature struct { signature *minisign.Signature } // NewSignature creates and validates a minisign signature object func NewSignature(r io.Reader) (*Signature, error) { var s Signature var inputBuffer bytes.Buffer if _, err := io.Copy(&inputBuffer, r); err != nil { return nil, fmt.Errorf("unable to read minisign signature: %w", err) } inputString := inputBuffer.String() signature, err := minisign.DecodeSignature(inputString) if err != nil { // try to parse as signify lines := strings.Split(strings.TrimRight(inputString, "\n"), "\n") if len(lines) != 2 { return nil, fmt.Errorf("invalid signature provided: %v lines detected", len(lines)) } sigBytes, b64Err := base64.StdEncoding.DecodeString(lines[1]) if b64Err != nil { return nil, errors.New("invalid signature provided: base64 decoding failed") } if len(sigBytes) != len(signature.SignatureAlgorithm)+len(signature.KeyId)+len(signature.Signature) { return nil, fmt.Errorf("invalid signature provided: incorrect size %v detected", len(sigBytes)) } copy(signature.SignatureAlgorithm[:], sigBytes[0:2]) copy(signature.KeyId[:], sigBytes[2:10]) copy(signature.Signature[:], sigBytes[10:]) } s.signature = &signature return &s, nil } // CanonicalValue implements the pki.Signature interface func (s Signature) CanonicalValue() ([]byte, error) { if s.signature == nil { return nil, errors.New("minisign signature has not been initialized") } buf := bytes.NewBuffer([]byte("untrusted comment:\n")) b64Buf := bytes.NewBuffer(s.signature.SignatureAlgorithm[:]) if _, err := b64Buf.Write(s.signature.KeyId[:]); err != nil { return nil, fmt.Errorf("error canonicalizing minisign signature: %w", err) } if _, err := b64Buf.Write(s.signature.Signature[:]); err != nil { return nil, fmt.Errorf("error canonicalizing minisign signature: %w", err) } if _, err := buf.WriteString(base64.StdEncoding.EncodeToString(b64Buf.Bytes())); err != nil { return nil, fmt.Errorf("error canonicalizing minisign signature: %w", err) } return buf.Bytes(), nil } // Verify implements the pki.Signature interface func (s Signature) Verify(r io.Reader, k interface{}, opts ...sigsig.VerifyOption) error { if s.signature == nil { return errors.New("minisign signature has not been initialized") } key, ok := k.(*PublicKey) if !ok { return errors.New("cannot use Verify with a non-minisign key") } if key.key == nil { return errors.New("minisign public key has not been initialized") } verifier, err := sigsig.LoadED25519Verifier(key.key.PublicKey[:]) if err != nil { return err } prehashed := s.signature.SignatureAlgorithm[1] == 0x44 if prehashed { h, _ := blake2b.New512(nil) _, err := io.Copy(h, r) if err != nil { return errors.New("reading minisign data") } r = bytes.NewReader(h.Sum(nil)) } return verifier.VerifySignature(bytes.NewReader(s.signature.Signature[:]), r, opts...) } // PublicKey Public Key that follows the minisign standard; supports signify and minisign public keys type PublicKey struct { key *minisign.PublicKey } // NewPublicKey implements the pki.PublicKey interface func NewPublicKey(r io.Reader) (*PublicKey, error) { var k PublicKey var inputBuffer bytes.Buffer if _, err := io.Copy(&inputBuffer, r); err != nil { return nil, fmt.Errorf("unable to read minisign public key: %w", err) } inputString := inputBuffer.String() // There are three ways a minisign key can be stored. // 1. The entire text key // 2. A base64 encoded string // 3. A legacy format we stored of just the key material (no key ID or Algorithm) due to bug fixed in https://github.com/sigstore/rekor/pull/562 key, err := minisign.DecodePublicKey(inputString) if err == nil { k.key = &key return &k, nil } key, err = minisign.NewPublicKey(inputString) if err == nil { k.key = &key return &k, nil } if len(inputString) == 32 { k.key = &minisign.PublicKey{ SignatureAlgorithm: [2]byte{'E', 'd'}, KeyId: [8]byte{}, } copy(k.key.PublicKey[:], inputBuffer.Bytes()) return &k, nil } return nil, fmt.Errorf("unable to read minisign public key: %w", err) } // CanonicalValue implements the pki.PublicKey interface func (k PublicKey) CanonicalValue() ([]byte, error) { if k.key == nil { return nil, errors.New("minisign public key has not been initialized") } bin := []byte{} bin = append(bin, k.key.SignatureAlgorithm[:]...) bin = append(bin, k.key.KeyId[:]...) bin = append(bin, k.key.PublicKey[:]...) b64Key := base64.StdEncoding.EncodeToString(bin) return []byte(b64Key), nil } // EmailAddresses implements the pki.PublicKey interface func (k PublicKey) EmailAddresses() []string { return nil } // Subjects implements the pki.PublicKey interface func (k PublicKey) Subjects() []string { return nil } // Identities implements the pki.PublicKey interface func (k PublicKey) Identities() ([]identity.Identity, error) { // PKIX encode ed25519 public key pkixKey, err := cryptoutils.MarshalPublicKeyToDER(ed25519.PublicKey(k.key.PublicKey[:])) if err != nil { return nil, err } digest := sha256.Sum256(pkixKey) return []identity.Identity{{ Crypto: k.key, Raw: pkixKey, Fingerprint: hex.EncodeToString(digest[:]), }}, nil }