1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package starman
18
19
20
21 import (
22 "archive/tar"
23 "bytes"
24 "fmt"
25 "io"
26 "time"
27
28 "golang.org/x/crypto/openpgp"
29 "golang.org/x/crypto/openpgp/armor"
30 "golang.org/x/crypto/openpgp/packet"
31
32 "github.com/sassoftware/relic/lib/binpatch"
33 "github.com/sassoftware/relic/lib/certloader"
34 "github.com/sassoftware/relic/lib/magic"
35 "github.com/sassoftware/relic/lib/pgptools"
36 "github.com/sassoftware/relic/signers"
37 "github.com/sassoftware/relic/signers/sigerrors"
38 )
39
40 var Signer = &signers.Signer{
41 Name: "starman",
42 Magic: magic.FileTypeStarman,
43 CertTypes: signers.CertTypePgp,
44 Sign: sign,
45 VerifyStream: verify,
46 }
47
48 func init() {
49 signers.Register(Signer)
50 }
51
52 func sign(r io.Reader, cert *certloader.Certificate, opts signers.SignOpts) ([]byte, error) {
53 info, err := verifyMeta(r)
54 if err != nil {
55 return nil, err
56 }
57
58 var sigbuf bytes.Buffer
59 config := &packet.Config{
60 DefaultHash: opts.Hash,
61 Time: func() time.Time { return opts.Time },
62 }
63 if err := openpgp.ArmoredDetachSign(&sigbuf, cert.PgpKey, bytes.NewReader(info.mdblob), config); err != nil {
64 return nil, err
65 }
66
67 padding := len(info.sigblob) - sigbuf.Len()
68 if padding > 0 {
69 sigbuf.Write(make([]byte, padding))
70 }
71
72 var twbuf bytes.Buffer
73 tw := tar.NewWriter(&twbuf)
74 tw.WriteHeader(&tar.Header{
75 Name: info.mdname + sigSuffix,
76 Mode: 0644,
77 Size: int64(sigbuf.Len()),
78 Typeflag: tar.TypeReg,
79 Uname: "root",
80 Gname: "root",
81 })
82 tw.Write(sigbuf.Bytes())
83 tw.Flush()
84
85 patch := binpatch.New()
86 patch.Add(info.sigStart, info.sigEnd-info.sigStart, twbuf.Bytes())
87 opts.Audit.Attributes["rpm.name"] = info.md.Name
88 opts.Audit.Attributes["rpm.epoch"] = info.md.Version.Epoch
89 opts.Audit.Attributes["rpm.version"] = info.md.Version.Version
90 opts.Audit.Attributes["rpm.release"] = info.md.Version.Release
91 opts.Audit.Attributes["rpm.arch"] = info.md.Arch
92 if epoch := info.md.Version.Epoch; epoch != "" && epoch != "0" {
93 opts.Audit.Attributes["rpm.nevra"] = fmt.Sprintf("%s-%s:%s-%s.%s", info.md.Name, epoch, info.md.Version.Version, info.md.Version.Release, info.md.Arch)
94 } else {
95 opts.Audit.Attributes["rpm.nevra"] = fmt.Sprintf("%s-%s-%s.%s", info.md.Name, info.md.Version.Version, info.md.Version.Release, info.md.Arch)
96 }
97 return opts.SetBinPatch(patch)
98 }
99
100 func verify(r io.Reader, opts signers.VerifyOpts) ([]*signers.Signature, error) {
101 info, err := verifyMeta(r)
102 if err != nil {
103 return nil, err
104 }
105 if !info.hasSig {
106 return nil, sigerrors.NotSignedError{Type: "TAR"}
107 }
108 block, err := armor.Decode(bytes.NewReader(info.sigblob))
109 if err != nil {
110 return nil, err
111 }
112 sig, err := pgptools.VerifyDetached(block.Body, bytes.NewReader(info.mdblob), opts.TrustedPgp)
113 if err == nil {
114 return []*signers.Signature{&signers.Signature{
115 CreationTime: sig.CreationTime,
116 Hash: sig.Hash,
117 SignerPgp: sig.Key.Entity,
118 }}, nil
119 } else if sig != nil {
120 return nil, fmt.Errorf("bad signature from %s(%x) [%s]: %s", pgptools.EntityName(sig.Key.Entity), sig.Key.PublicKey.KeyId, sig.CreationTime, err)
121 }
122 return nil, err
123 }
124
View as plain text