...

Source file src/github.com/sassoftware/relic/lib/signdeb/verify.go

Documentation: github.com/sassoftware/relic/lib/signdeb

     1  //
     2  // Copyright (c) SAS Institute Inc.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  //
    16  
    17  package signdeb
    18  
    19  import (
    20  	"bufio"
    21  	"bytes"
    22  	"crypto"
    23  	"errors"
    24  	"fmt"
    25  	"io"
    26  	"io/ioutil"
    27  	"strings"
    28  
    29  	"github.com/qur/ar"
    30  	"github.com/sassoftware/relic/lib/pgptools"
    31  	"golang.org/x/crypto/openpgp"
    32  )
    33  
    34  // Extract and verify signatures from a Debian package. A keyring of known PGP
    35  // certificates must be provided to validate the signatures; if the needed key
    36  // is missing then an ErrNoKey value is returned.
    37  func Verify(r io.Reader, keyring openpgp.EntityList, skipDigest bool) (map[string]*pgptools.PgpSignature, error) {
    38  	reader := ar.NewReader(r)
    39  	digests := make(map[string]string)
    40  	sigs := make(map[string][]byte)
    41  	for {
    42  		hdr, err := reader.Next()
    43  		if err == io.EOF {
    44  			break
    45  		} else if err != nil {
    46  			return nil, err
    47  		}
    48  		if strings.HasPrefix(hdr.Name, "_gpg") {
    49  			role := hdr.Name[4:]
    50  			sigs[role], err = ioutil.ReadAll(reader)
    51  			if err != nil {
    52  				return nil, err
    53  			}
    54  		} else if !skipDigest {
    55  			md5 := crypto.MD5.New()
    56  			sha1 := crypto.SHA1.New()
    57  			if _, err := io.Copy(io.MultiWriter(md5, sha1), reader); err != nil {
    58  				return nil, err
    59  			}
    60  			digests[hdr.Name] = fmt.Sprintf("%x %x", md5.Sum(nil), sha1.Sum(nil))
    61  		}
    62  	}
    63  	ret := make(map[string]*pgptools.PgpSignature, len(sigs))
    64  	for role, sig := range sigs {
    65  		var body bytes.Buffer
    66  		info, err := pgptools.VerifyClearSign(bytes.NewReader(sig), &body, keyring)
    67  		if err != nil {
    68  			return nil, err
    69  		}
    70  		if !skipDigest {
    71  			if err := checkSig(role, &body, digests); err != nil {
    72  				return nil, err
    73  			}
    74  		}
    75  		ret[role] = info
    76  	}
    77  	return ret, nil
    78  }
    79  
    80  func checkSig(role string, body io.Reader, digests map[string]string) error {
    81  	sawFiles := false
    82  	scanner := bufio.NewScanner(body)
    83  	// read header
    84  	for scanner.Scan() {
    85  		line := scanner.Text()
    86  		if line == "Files:" {
    87  			sawFiles = true
    88  			break
    89  		}
    90  	}
    91  	if !sawFiles {
    92  		return errors.New("malformed signature")
    93  	}
    94  	// read digests
    95  	checked := make(map[string]bool, len(digests))
    96  	for scanner.Scan() {
    97  		line := scanner.Text()
    98  		if line == "" {
    99  			break
   100  		} else if line[0] != '\t' || len(line) < 76 {
   101  			return errors.New("malformed signature")
   102  		}
   103  		parts := strings.SplitN(line[1:], " ", 4)
   104  		sums := parts[0] + " " + parts[1]
   105  		name := parts[3]
   106  		calculated := digests[name]
   107  		if calculated == "" {
   108  			return fmt.Errorf("signature references unknown file %s", name)
   109  		} else if calculated != sums {
   110  			return fmt.Errorf("signature mismatch on file %s: (%s) != (%s)", name, calculated, sums)
   111  		}
   112  		checked[name] = true
   113  	}
   114  	// make sure everything was checked
   115  	for name := range digests {
   116  		if checked[name] {
   117  			continue
   118  		}
   119  		return fmt.Errorf("signature does not cover file: %s", name)
   120  	}
   121  	return nil
   122  }
   123  

View as plain text