Skip to content

Commit def6e3e

Browse files
committed
Remove replace directive for github.com/github/smimesign
This forks the changes made in github/smimesign#108 into this repo. This was done because the replace directive was preventing go install from functioning properly. Once the upstream PR is merged, we can revert this change. Signed-off-by: Billy Lynch <billy@chainguard.dev>
1 parent 7299d32 commit def6e3e

File tree

8 files changed

+246
-10
lines changed

8 files changed

+246
-10
lines changed

command_sign.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ import (
66
"io"
77
"os"
88

9-
"github.com/github/smimesign/signature"
109
"github.com/pkg/errors"
1110
"github.com/wlynch/smimecosign/fulcio"
1211
"github.com/wlynch/smimecosign/git"
12+
"github.com/wlynch/smimecosign/signature"
1313
)
1414

1515
func commandSign() error {

command_verify.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import (
88
"io"
99
"os"
1010

11-
"github.com/github/smimesign/signature"
1211
"github.com/pkg/errors"
1312
"github.com/sigstore/cosign/cmd/cosign/cli/fulcio/fulcioroots"
1413
"github.com/wlynch/smimecosign/git"
14+
"github.com/wlynch/smimecosign/signature"
1515
)
1616

1717
func commandVerify() error {

git/git.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ import (
77
"encoding/pem"
88

99
cms "github.com/github/smimesign/ietf-cms"
10-
"github.com/github/smimesign/signature"
1110
"github.com/go-git/go-git/v5/plumbing"
1211
"github.com/go-git/go-git/v5/plumbing/object"
1312
"github.com/pkg/errors"
1413
"github.com/sigstore/cosign/cmd/cosign/cli/fulcio/fulcioroots"
1514
"github.com/sigstore/rekor/pkg/generated/models"
1615
"github.com/wlynch/smimecosign/fulcio"
1716
"github.com/wlynch/smimecosign/rekor"
17+
"github.com/wlynch/smimecosign/signature"
1818
)
1919

2020
func Sign(ctx context.Context, ident *fulcio.Identity, data []byte, opts signature.SignOptions) ([]byte, *x509.Certificate, error) {

go.mod

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,17 @@ go 1.18
55
require (
66
github.com/github/smimesign v0.2.0
77
github.com/go-git/go-git/v5 v5.4.2
8+
github.com/go-openapi/runtime v0.24.0
89
github.com/go-openapi/strfmt v0.21.2
910
github.com/go-openapi/swag v0.21.1
1011
github.com/pborman/getopt/v2 v2.1.0
1112
github.com/pkg/errors v0.9.1
1213
github.com/sigstore/cosign v1.8.1-0.20220502185546-8efb042c0427
1314
github.com/sigstore/rekor v0.4.1-0.20220114213500-23f583409af3
15+
github.com/sigstore/sigstore v1.2.1-0.20220424143412-3d41663116d5
1416
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
1517
)
1618

17-
// Remove once https://github.com/github/smimesign/pull/108 is merged.
18-
replace github.com/github/smimesign => github.com/wlynch/smimesign v0.2.1-0.20220502200102-5d7d5b14387f
19-
2019
require (
2120
bitbucket.org/creachadair/shell v0.0.6 // indirect
2221
cloud.google.com/go v0.100.2 // indirect
@@ -94,7 +93,6 @@ require (
9493
github.com/go-openapi/jsonpointer v0.19.5 // indirect
9594
github.com/go-openapi/jsonreference v0.19.6 // indirect
9695
github.com/go-openapi/loads v0.21.1 // indirect
97-
github.com/go-openapi/runtime v0.24.0 // indirect
9896
github.com/go-openapi/spec v0.20.4 // indirect
9997
github.com/go-openapi/validate v0.21.0 // indirect
10098
github.com/go-piv/piv-go v1.9.0 // indirect
@@ -169,7 +167,6 @@ require (
169167
github.com/sergi/go-diff v1.2.0 // indirect
170168
github.com/shibumi/go-pathspec v1.3.0 // indirect
171169
github.com/sigstore/fulcio v0.1.2-0.20220114150912-86a2036f9bc7 // indirect
172-
github.com/sigstore/sigstore v1.2.1-0.20220424143412-3d41663116d5 // indirect
173170
github.com/sirupsen/logrus v1.8.1 // indirect
174171
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
175172
github.com/soheilhy/cmux v0.1.5 // indirect

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,8 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm
710710
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
711711
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
712712
github.com/gin-gonic/gin v1.7.3/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
713+
github.com/github/smimesign v0.2.0 h1:Hho4YcX5N1I9XNqhq0fNx0Sts8MhLonHd+HRXVGNjvk=
714+
github.com/github/smimesign v0.2.0/go.mod h1:iZiiwNT4HbtGRVqCQu7uJPEZCuEE5sfSSttcnePkDl4=
713715
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
714716
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
715717
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
@@ -1965,8 +1967,6 @@ github.com/vmihailenco/msgpack/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvC
19651967
github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY=
19661968
github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
19671969
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
1968-
github.com/wlynch/smimesign v0.2.1-0.20220502200102-5d7d5b14387f h1:IKLDd3pc2H3fTawZlEqnCyBiFs9c/nmTfOfD7Byo2aU=
1969-
github.com/wlynch/smimesign v0.2.1-0.20220502200102-5d7d5b14387f/go.mod h1:iZiiwNT4HbtGRVqCQu7uJPEZCuEE5sfSSttcnePkDl4=
19701970
github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
19711971
github.com/xanzy/go-gitlab v0.64.0 h1:rMgQdW9S1w3qvNAH2LYpFd2xh7KNLk+JWJd7sorNuTc=
19721972
github.com/xanzy/go-gitlab v0.64.0/go.mod h1:F0QEXwmqiBUxCgJm8fE9S+1veX4XC9Z4cfaAbqwk4YM=

signature/sign.go

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package signature
2+
3+
import (
4+
"bytes"
5+
"crypto"
6+
"crypto/x509"
7+
"encoding/pem"
8+
9+
cms "github.com/github/smimesign/ietf-cms"
10+
"github.com/pkg/errors"
11+
)
12+
13+
type SignOptions struct {
14+
// Make a detached signature
15+
Detached bool
16+
// URL of RFC3161 timestamp authority to use for timestamping
17+
TimestampAuthority string
18+
// Create ascii armored output
19+
Armor bool
20+
// IncludeCerts specifies what certs to include in the resulting signature.
21+
// -3 is the same as -2, but ommits issuer when cert has Authority Information Access extension.
22+
// -2 includes all certs except root.
23+
// -1 includes all certs.
24+
// 0 includes no certs.
25+
// 1 includes leaf cert.
26+
// >1 includes n from the leaf.
27+
IncludeCerts int
28+
}
29+
30+
// Identity is a copy of smimesign.Identity to allow for compatibility without
31+
// needing a dependency on the whole package. This can be removed once
32+
// https://github.com/github/smimesign/pull/108 is merged.
33+
type Identity interface {
34+
// Certificate gets the identity's certificate.
35+
Certificate() (*x509.Certificate, error)
36+
// CertificateChain attempts to get the identity's full certificate chain.
37+
CertificateChain() ([]*x509.Certificate, error)
38+
// Signer gets a crypto.Signer that uses the identity's private key.
39+
Signer() (crypto.Signer, error)
40+
// Delete deletes this identity from the system.
41+
Delete() error
42+
// Close any manually managed memory held by the Identity.
43+
Close()
44+
}
45+
46+
// Sign signs a given payload for the given identity.
47+
// The resulting signature and cert used is returned.
48+
func Sign(ident Identity, body []byte, opts SignOptions) ([]byte, *x509.Certificate, error) {
49+
cert, err := ident.Certificate()
50+
if err != nil {
51+
return nil, nil, errors.Wrap(err, "failed to get idenity certificate")
52+
}
53+
signer, err := ident.Signer()
54+
if err != nil {
55+
return nil, nil, errors.Wrap(err, "failed to get idenity signer")
56+
}
57+
58+
sd, err := cms.NewSignedData(body)
59+
if err != nil {
60+
return nil, nil, errors.Wrap(err, "failed to create signed data")
61+
}
62+
63+
if err := sd.Sign([]*x509.Certificate{cert}, signer); err != nil {
64+
return nil, nil, errors.Wrap(err, "failed to sign message")
65+
}
66+
if opts.Detached {
67+
sd.Detached()
68+
}
69+
70+
if len(opts.TimestampAuthority) > 0 {
71+
if err = sd.AddTimestamps(opts.TimestampAuthority); err != nil {
72+
return nil, nil, errors.Wrap(err, "failed to add timestamp")
73+
}
74+
}
75+
76+
chain, err := ident.CertificateChain()
77+
if err != nil {
78+
return nil, nil, errors.Wrap(err, "failed to get idenity certificate chain")
79+
}
80+
if chain, err = certsForSignature(chain, opts.IncludeCerts); err != nil {
81+
return nil, nil, err
82+
}
83+
if err := sd.SetCertificates(chain); err != nil {
84+
return nil, nil, errors.Wrap(err, "failed to set certificates")
85+
}
86+
87+
der, err := sd.ToDER()
88+
if err != nil {
89+
return nil, nil, errors.Wrap(err, "failed to serialize signature")
90+
}
91+
92+
if opts.Armor {
93+
return pem.EncodeToMemory(&pem.Block{
94+
Type: "SIGNED MESSAGE",
95+
Bytes: der,
96+
}), cert, nil
97+
} else {
98+
return der, cert, nil
99+
}
100+
}
101+
102+
// certsForSignature determines which certificates to include in the signature
103+
// based on the --include-certs option specified by the user.
104+
func certsForSignature(chain []*x509.Certificate, include int) ([]*x509.Certificate, error) {
105+
if include < -3 {
106+
include = -2 // default
107+
}
108+
if include > len(chain) {
109+
include = len(chain)
110+
}
111+
112+
switch include {
113+
case -3:
114+
for i := len(chain) - 1; i > 0; i-- {
115+
issuer, cert := chain[i], chain[i-1]
116+
117+
// remove issuer when cert has AIA extension
118+
if bytes.Equal(issuer.RawSubject, cert.RawIssuer) && len(cert.IssuingCertificateURL) > 0 {
119+
chain = chain[0:i]
120+
}
121+
}
122+
return chainWithoutRoot(chain), nil
123+
case -2:
124+
return chainWithoutRoot(chain), nil
125+
case -1:
126+
return chain, nil
127+
default:
128+
return chain[0:include], nil
129+
}
130+
}
131+
132+
// Returns the provided chain, having removed the root certificate, if present.
133+
// This includes removing the cert itself if the chain is a single self-signed
134+
// cert.
135+
func chainWithoutRoot(chain []*x509.Certificate) []*x509.Certificate {
136+
if len(chain) == 0 {
137+
return chain
138+
}
139+
140+
lastIdx := len(chain) - 1
141+
last := chain[lastIdx]
142+
143+
if bytes.Equal(last.RawIssuer, last.RawSubject) {
144+
return chain[0:lastIdx]
145+
}
146+
147+
return chain
148+
}

signature/signature_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package signature
2+
3+
import (
4+
"crypto"
5+
"crypto/x509"
6+
"fmt"
7+
"testing"
8+
9+
"github.com/github/smimesign/fakeca"
10+
)
11+
12+
type identity struct {
13+
Identity
14+
base *fakeca.Identity
15+
}
16+
17+
func (i *identity) Certificate() (*x509.Certificate, error) {
18+
return i.base.Certificate, nil
19+
}
20+
21+
func (i *identity) CertificateChain() ([]*x509.Certificate, error) {
22+
return i.base.Chain(), nil
23+
}
24+
25+
func (i *identity) Signer() (crypto.Signer, error) {
26+
return i.base.PrivateKey, nil
27+
}
28+
29+
// TestSignVerify is a basic test to ensure that the Sign/Verify funcs can be
30+
// used with each other. We're assuming that the actual signature format has
31+
// been more thoroghly vetted in other packages (i.e. ietf-cms).
32+
func TestSignVerify(t *testing.T) {
33+
id := &identity{
34+
base: fakeca.New(),
35+
}
36+
data := []byte("tacocat")
37+
38+
sig, _, err := Sign(id, data, SignOptions{
39+
Detached: true,
40+
Armor: true,
41+
// Fake CA outputs self-signed certs, so we need to use -1 to make sure
42+
// the self-signed cert itself is included in the chain, otherwise
43+
// Verify cannot find a cert to use for verification.
44+
IncludeCerts: -1,
45+
})
46+
if err != nil {
47+
t.Fatalf("Sign() = %v", err)
48+
}
49+
50+
fmt.Println(id.base.Chain())
51+
if _, err := Verify(data, sig, true, x509.VerifyOptions{
52+
// Trust the fake CA
53+
Roots: id.base.ChainPool(),
54+
}); err != nil {
55+
t.Fatalf("Verify() = %v", err)
56+
}
57+
}

signature/verify.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package signature
2+
3+
import (
4+
"crypto/x509"
5+
"encoding/pem"
6+
7+
cms "github.com/github/smimesign/ietf-cms"
8+
"github.com/pkg/errors"
9+
)
10+
11+
// Verify verifies a signature for a given identity.
12+
//
13+
// WARNING: this function doesn't do any revocation checking.
14+
func Verify(body, sig []byte, detached bool, opts x509.VerifyOptions) ([][][]*x509.Certificate, error) {
15+
// Try decoding as PEM
16+
var der []byte
17+
if blk, _ := pem.Decode(sig); blk != nil {
18+
der = blk.Bytes
19+
} else {
20+
der = sig
21+
}
22+
23+
// Parse signature
24+
sd, err := cms.ParseSignedData(der)
25+
if err != nil {
26+
return nil, errors.Wrap(err, "failed to parse signature")
27+
}
28+
29+
if detached {
30+
return sd.VerifyDetached(body, opts)
31+
} else {
32+
return sd.Verify(opts)
33+
}
34+
}

0 commit comments

Comments
 (0)