1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
|
/*
* Copyright (c) SAS Institute Inc.
*
* 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 rpmutils
import (
"bytes"
"crypto"
"crypto/md5"
"errors"
"fmt"
"hash"
"io"
"time"
"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/packet"
)
var headerSigTags = []int{SIG_RSA, SIG_DSA}
var payloadSigTags = []int{
SIG_PGP - _SIGHEADER_TAG_BASE,
SIG_GPG - _SIGHEADER_TAG_BASE,
}
// Signature describes a PGP signature found within a RPM while verifying it.
type Signature struct {
// Signer is the PGP identity that created the signature. It may be nil if
// the public key is not available at verification time, but KeyId will
// always be set.
Signer *openpgp.Entity
// Hash is the algorithm used to digest the signature contents
Hash crypto.Hash
// CreationTime is when the signature was created
CreationTime time.Time
// HeaderOnly is true for signatures that only cover the general RPM header,
// and false for signatures that cover the general header plus the payload
HeaderOnly bool
// KeyId is the PGP key that created the signature.
KeyId uint64
packet packet.Packet
hash hash.Hash
}
// Verify the PGP signature over a RPM file. knownKeys should enumerate public
// keys to check against, otherwise the signature validity cannot be verified.
// If knownKeys is nil then digests will be checked but only the raw key ID will
// be available.
func Verify(stream io.Reader, knownKeys openpgp.EntityList) (header *RpmHeader, sigs []*Signature, err error) {
lead, sigHeader, err := readSignatureHeader(stream)
if err != nil {
return nil, nil, err
}
payloadDigest := md5.New()
sigs, headerMulti, payloadMulti, err := setupDigesters(sigHeader, payloadDigest)
if err != nil {
return nil, nil, err
}
// parse the general header and also tee it into a buffer
genHeaderBuf := new(bytes.Buffer)
headerTee := io.TeeReader(stream, genHeaderBuf)
genHeader, err := readHeader(headerTee, getSha1(sigHeader), sigHeader.isSource, false)
if err != nil {
return nil, nil, err
}
genHeaderBlob := genHeaderBuf.Bytes()
if _, err := headerMulti.Write(genHeaderBlob); err != nil {
return nil, nil, err
}
// chain the buffered general header to the rest of the payload, and digest the whole lot of it
genHeaderAndPayload := io.MultiReader(bytes.NewReader(genHeaderBlob), stream)
if _, err := io.Copy(payloadMulti, genHeaderAndPayload); err != nil {
return nil, nil, err
}
if !checkMd5(sigHeader, payloadDigest) {
return nil, nil, errors.New("md5 digest mismatch")
}
if knownKeys != nil {
for _, sig := range sigs {
if err := checkSig(sig, knownKeys); err != nil {
return nil, nil, err
}
}
}
hdr := &RpmHeader{
lead: lead,
sigHeader: sigHeader,
genHeader: genHeader,
isSource: sigHeader.isSource,
}
return hdr, sigs, nil
}
func setupDigester(sigHeader *rpmHeader, tag int) (*Signature, error) {
blob, err := sigHeader.GetBytes(tag)
if _, ok := err.(NoSuchTagError); ok {
return nil, nil
} else if err != nil {
return nil, err
}
packetReader := packet.NewReader(bytes.NewReader(blob))
genpkt, err := packetReader.Next()
if err != nil {
return nil, err
}
var sig *Signature
switch pkt := genpkt.(type) {
case *packet.SignatureV3:
sig = &Signature{
Hash: pkt.Hash,
CreationTime: pkt.CreationTime,
KeyId: pkt.IssuerKeyId,
}
case *packet.Signature:
if pkt.IssuerKeyId == nil {
return nil, errors.New("Missing keyId in signature")
}
sig = &Signature{
Hash: pkt.Hash,
CreationTime: pkt.CreationTime,
KeyId: *pkt.IssuerKeyId,
}
default:
return nil, fmt.Errorf("tag %d does not contain a PGP signature", tag)
}
_, err = packetReader.Next()
if err != io.EOF {
return nil, fmt.Errorf("trailing garbage after signature in tag %d", tag)
}
sig.packet = genpkt
if !sig.Hash.Available() {
return nil, errors.New("signature uses unknown digest")
}
sig.hash = sig.Hash.New()
return sig, nil
}
func setupDigesters(sigHeader *rpmHeader, payloadDigest io.Writer) ([]*Signature, io.Writer, io.Writer, error) {
sigs := make([]*Signature, 0)
headerWriters := make([]io.Writer, 0)
payloadWriters := []io.Writer{payloadDigest}
for _, tag := range headerSigTags {
sig, err := setupDigester(sigHeader, tag)
if err != nil {
return nil, nil, nil, err
} else if sig == nil {
continue
}
sig.HeaderOnly = true
headerWriters = append(headerWriters, sig.hash)
sigs = append(sigs, sig)
}
for _, tag := range payloadSigTags {
sig, err := setupDigester(sigHeader, tag)
if err != nil {
return nil, nil, nil, err
} else if sig == nil {
continue
}
sig.HeaderOnly = false
payloadWriters = append(payloadWriters, sig.hash)
sigs = append(sigs, sig)
}
headerMulti := io.MultiWriter(headerWriters...)
payloadMulti := io.MultiWriter(payloadWriters...)
return sigs, headerMulti, payloadMulti, nil
}
func checkSig(sig *Signature, knownKeys openpgp.EntityList) error {
keys := knownKeys.KeysById(sig.KeyId)
if keys == nil {
return fmt.Errorf("keyid %x not found", sig.KeyId)
}
key := keys[0]
sig.Signer = key.Entity
var err error
switch pkt := sig.packet.(type) {
case *packet.Signature:
err = key.PublicKey.VerifySignature(sig.hash, pkt)
case *packet.SignatureV3:
err = key.PublicKey.VerifySignatureV3(sig.hash, pkt)
}
if err != nil {
return err
}
sig.packet = nil
sig.hash = nil
return nil
}
|