Arch Linux helpers and parsers
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

184 lines
3.7 KiB

package archlinux
import (
"bytes"
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"errors"
"fmt"
"hash"
"io"
"io/ioutil"
"log"
"os"
"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/packet"
)
// Defaults
const (
DefaultKeyringFile = "/etc/pacman.d/gnupg/pubring.gpg"
)
type hasher struct {
hash.Hash
Name string
Sum []byte
//*sync.WaitGroup
//In chan []byte
}
func newHasher(name string, sum []byte, h hash.Hash) hasher {
n := hasher{
Hash: h,
Name: name,
Sum: sum,
//WaitGroup: new(sync.WaitGroup),
//In: make(chan []byte, 16),
}
/*
n.Add(1)
go n.Run()
*/
return n
}
/*
func (h *hasher) Run() {
defer h.Done()
for {
select {
case b := <-h.In:
if b == nil {
return
}
if _, err := h.Hash.Write(b); err != nil {
panic(err)
}
}
}
}
*/
// Verify verifies the package file checksums. If filename is empty, the Filename
// field of the struct will be used in stead.
func (pkg *Package) Verify(filename string) error {
if filename == "" {
filename = pkg.Filename
}
f, err := os.Open(filename)
if err != nil {
return err
}
defer f.Close()
var hashers []hasher
addHasher := func(name string, sum []byte, fn func() hash.Hash) {
if len(sum) == 0 {
return
}
hashers = append(hashers, newHasher(name, sum, fn()))
}
addHasher("MD5", pkg.MD5, md5.New)
addHasher("SHA1", pkg.SHA1, sha1.New)
addHasher("SHA256", pkg.SHA256, sha256.New)
addHasher("SHA384", pkg.SHA384, sha512.New384)
addHasher("SHA512", pkg.SHA512, sha512.New)
if len(hashers) == 0 {
return ErrPackageNoChecksums
}
var block [4096]byte
for {
n, err := f.Read(block[:])
if err != nil {
if err == io.EOF {
break
}
}
for _, h := range hashers {
//h.In <- block[:n]
if _, err = h.Write(block[:n]); err != nil {
return err
}
}
}
for _, h := range hashers {
// h.Wait()
if s := h.Hash.Sum(nil); !bytes.Equal(s, h.Sum) {
return fmt.Errorf("archlinux: %s checksum mismatch, got %x, expected %x", h.Name, s, h.Sum)
}
}
return nil
}
// IsSigned returns true if the package has a PGP signature
func (pkg *Package) IsSigned() bool {
return len(pkg.PGPSignature) > 0
}
// PGPKeyID returns the PGP key ID that signed the PGP signature. If there is
// no signature, or if there is an error parsing the signature, 0 is returned.
func (pkg *Package) PGPKeyID() uint64 {
if !pkg.IsSigned() {
return 0
}
var (
r = packet.NewReader(bytes.NewBuffer(pkg.PGPSignature))
p packet.Packet
s *packet.Signature
ok bool
err error
)
for {
if p, err = r.Next(); err != nil {
return 0
}
if s, ok = p.(*packet.Signature); ok {
if s.IssuerKeyId != nil {
return *s.IssuerKeyId
}
}
}
}
// VerifySignature verifies the package file in filename with the known PGP
// signature against the passed keyring.
func (pkg *Package) VerifySignature(filename string, keyring openpgp.EntityList) error {
if !pkg.IsSigned() {
return errors.New("archlinux: package is not PGP signed")
}
f, err := os.Open(filename)
if err != nil {
return err
}
defer f.Close()
_, err = openpgp.CheckDetachedSignature(keyring, f, bytes.NewBuffer(pkg.PGPSignature))
return err
}
// VerifySignatureKeyring verifies the package file in filename with the known
// PGP signature against the passed keyring file.
func (pkg *Package) VerifySignatureKeyring(filename, keyringName string) error {
b, err := ioutil.ReadFile(keyringName)
if err != nil {
return err
}
l, err := openpgp.ReadKeyRing(bytes.NewBuffer(b))
if err != nil {
return err
}
if os.Getenv("DEBUG_KEYRING") != "" {
for _, e := range l {
log.Printf("keyring: %s\n", e.PrimaryKey.KeyIdString())
}
}
return pkg.VerifySignature(filename, l)
}