Skip to content

Commit

Permalink
Remove replace directive for github.com/github/smimesign
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
wlynch committed May 12, 2022
1 parent d2819f1 commit 6519737
Show file tree
Hide file tree
Showing 8 changed files with 246 additions and 10 deletions.
2 changes: 1 addition & 1 deletion command_sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import (
"io"
"os"

"github.com/github/smimesign/signature"
"github.com/pkg/errors"
"github.com/wlynch/smimecosign/fulcio"
"github.com/wlynch/smimecosign/git"
"github.com/wlynch/smimecosign/signature"
)

func commandSign() error {
Expand Down
2 changes: 1 addition & 1 deletion command_verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import (
"io"
"os"

"github.com/github/smimesign/signature"
"github.com/pkg/errors"
"github.com/sigstore/cosign/cmd/cosign/cli/fulcio/fulcioroots"
"github.com/wlynch/smimecosign/git"
"github.com/wlynch/smimecosign/signature"
)

func commandVerify() error {
Expand Down
2 changes: 1 addition & 1 deletion git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import (
"encoding/pem"

cms "github.com/github/smimesign/ietf-cms"
"github.com/github/smimesign/signature"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/pkg/errors"
"github.com/sigstore/cosign/cmd/cosign/cli/fulcio/fulcioroots"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/wlynch/smimecosign/fulcio"
"github.com/wlynch/smimecosign/rekor"
"github.com/wlynch/smimecosign/signature"
)

func Sign(ctx context.Context, ident *fulcio.Identity, data []byte, opts signature.SignOptions) ([]byte, *x509.Certificate, error) {
Expand Down
7 changes: 2 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,17 @@ go 1.18
require (
github.com/github/smimesign v0.2.0
github.com/go-git/go-git/v5 v5.4.2
github.com/go-openapi/runtime v0.24.0
github.com/go-openapi/strfmt v0.21.2
github.com/go-openapi/swag v0.21.1
github.com/pborman/getopt/v2 v2.1.0
github.com/pkg/errors v0.9.1
github.com/sigstore/cosign v1.8.1-0.20220502185546-8efb042c0427
github.com/sigstore/rekor v0.4.1-0.20220114213500-23f583409af3
github.com/sigstore/sigstore v1.2.1-0.20220424143412-3d41663116d5
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
)

// Remove once https://github.com/github/smimesign/pull/108 is merged.
replace github.com/github/smimesign => github.com/wlynch/smimesign v0.2.1-0.20220502200102-5d7d5b14387f

require (
bitbucket.org/creachadair/shell v0.0.6 // indirect
cloud.google.com/go v0.100.2 // indirect
Expand Down Expand Up @@ -94,7 +93,6 @@ require (
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.6 // indirect
github.com/go-openapi/loads v0.21.1 // indirect
github.com/go-openapi/runtime v0.24.0 // indirect
github.com/go-openapi/spec v0.20.4 // indirect
github.com/go-openapi/validate v0.21.0 // indirect
github.com/go-piv/piv-go v1.9.0 // indirect
Expand Down Expand Up @@ -169,7 +167,6 @@ require (
github.com/sergi/go-diff v1.2.0 // indirect
github.com/shibumi/go-pathspec v1.3.0 // indirect
github.com/sigstore/fulcio v0.1.2-0.20220114150912-86a2036f9bc7 // indirect
github.com/sigstore/sigstore v1.2.1-0.20220424143412-3d41663116d5 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
github.com/soheilhy/cmux v0.1.5 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,8 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/gin-gonic/gin v1.7.3/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
github.com/github/smimesign v0.2.0 h1:Hho4YcX5N1I9XNqhq0fNx0Sts8MhLonHd+HRXVGNjvk=
github.com/github/smimesign v0.2.0/go.mod h1:iZiiwNT4HbtGRVqCQu7uJPEZCuEE5sfSSttcnePkDl4=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
Expand Down Expand Up @@ -1965,8 +1967,6 @@ github.com/vmihailenco/msgpack/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvC
github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY=
github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
github.com/wlynch/smimesign v0.2.1-0.20220502200102-5d7d5b14387f h1:IKLDd3pc2H3fTawZlEqnCyBiFs9c/nmTfOfD7Byo2aU=
github.com/wlynch/smimesign v0.2.1-0.20220502200102-5d7d5b14387f/go.mod h1:iZiiwNT4HbtGRVqCQu7uJPEZCuEE5sfSSttcnePkDl4=
github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
github.com/xanzy/go-gitlab v0.64.0 h1:rMgQdW9S1w3qvNAH2LYpFd2xh7KNLk+JWJd7sorNuTc=
github.com/xanzy/go-gitlab v0.64.0/go.mod h1:F0QEXwmqiBUxCgJm8fE9S+1veX4XC9Z4cfaAbqwk4YM=
Expand Down
148 changes: 148 additions & 0 deletions signature/sign.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package signature

import (
"bytes"
"crypto"
"crypto/x509"
"encoding/pem"

cms "github.com/github/smimesign/ietf-cms"
"github.com/pkg/errors"
)

type SignOptions struct {
// Make a detached signature
Detached bool
// URL of RFC3161 timestamp authority to use for timestamping
TimestampAuthority string
// Create ascii armored output
Armor bool
// IncludeCerts specifies what certs to include in the resulting signature.
// -3 is the same as -2, but ommits issuer when cert has Authority Information Access extension.
// -2 includes all certs except root.
// -1 includes all certs.
// 0 includes no certs.
// 1 includes leaf cert.
// >1 includes n from the leaf.
IncludeCerts int
}

// Identity is a copy of smimesign.Identity to allow for compatibility without
// needing a dependency on the whole package. This can be removed once
// https://github.com/github/smimesign/pull/108 is merged.
type Identity interface {
// Certificate gets the identity's certificate.
Certificate() (*x509.Certificate, error)
// CertificateChain attempts to get the identity's full certificate chain.
CertificateChain() ([]*x509.Certificate, error)
// Signer gets a crypto.Signer that uses the identity's private key.
Signer() (crypto.Signer, error)
// Delete deletes this identity from the system.
Delete() error
// Close any manually managed memory held by the Identity.
Close()
}

// Sign signs a given payload for the given identity.
// The resulting signature and cert used is returned.
func Sign(ident Identity, body []byte, opts SignOptions) ([]byte, *x509.Certificate, error) {
cert, err := ident.Certificate()
if err != nil {
return nil, nil, errors.Wrap(err, "failed to get idenity certificate")
}
signer, err := ident.Signer()
if err != nil {
return nil, nil, errors.Wrap(err, "failed to get idenity signer")
}

sd, err := cms.NewSignedData(body)
if err != nil {
return nil, nil, errors.Wrap(err, "failed to create signed data")
}

if err := sd.Sign([]*x509.Certificate{cert}, signer); err != nil {
return nil, nil, errors.Wrap(err, "failed to sign message")
}
if opts.Detached {
sd.Detached()
}

if len(opts.TimestampAuthority) > 0 {
if err = sd.AddTimestamps(opts.TimestampAuthority); err != nil {
return nil, nil, errors.Wrap(err, "failed to add timestamp")
}
}

chain, err := ident.CertificateChain()
if err != nil {
return nil, nil, errors.Wrap(err, "failed to get idenity certificate chain")
}
if chain, err = certsForSignature(chain, opts.IncludeCerts); err != nil {
return nil, nil, err
}
if err := sd.SetCertificates(chain); err != nil {
return nil, nil, errors.Wrap(err, "failed to set certificates")
}

der, err := sd.ToDER()
if err != nil {
return nil, nil, errors.Wrap(err, "failed to serialize signature")
}

if opts.Armor {
return pem.EncodeToMemory(&pem.Block{
Type: "SIGNED MESSAGE",
Bytes: der,
}), cert, nil
} else {
return der, cert, nil
}
}

// certsForSignature determines which certificates to include in the signature
// based on the --include-certs option specified by the user.
func certsForSignature(chain []*x509.Certificate, include int) ([]*x509.Certificate, error) {
if include < -3 {
include = -2 // default
}
if include > len(chain) {
include = len(chain)
}

switch include {
case -3:
for i := len(chain) - 1; i > 0; i-- {
issuer, cert := chain[i], chain[i-1]

// remove issuer when cert has AIA extension
if bytes.Equal(issuer.RawSubject, cert.RawIssuer) && len(cert.IssuingCertificateURL) > 0 {
chain = chain[0:i]
}
}
return chainWithoutRoot(chain), nil
case -2:
return chainWithoutRoot(chain), nil
case -1:
return chain, nil
default:
return chain[0:include], nil
}
}

// Returns the provided chain, having removed the root certificate, if present.
// This includes removing the cert itself if the chain is a single self-signed
// cert.
func chainWithoutRoot(chain []*x509.Certificate) []*x509.Certificate {
if len(chain) == 0 {
return chain
}

lastIdx := len(chain) - 1
last := chain[lastIdx]

if bytes.Equal(last.RawIssuer, last.RawSubject) {
return chain[0:lastIdx]
}

return chain
}
57 changes: 57 additions & 0 deletions signature/signature_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package signature

import (
"crypto"
"crypto/x509"
"fmt"
"testing"

"github.com/github/smimesign/fakeca"
)

type identity struct {
Identity
base *fakeca.Identity
}

func (i *identity) Certificate() (*x509.Certificate, error) {
return i.base.Certificate, nil
}

func (i *identity) CertificateChain() ([]*x509.Certificate, error) {
return i.base.Chain(), nil
}

func (i *identity) Signer() (crypto.Signer, error) {
return i.base.PrivateKey, nil
}

// TestSignVerify is a basic test to ensure that the Sign/Verify funcs can be
// used with each other. We're assuming that the actual signature format has
// been more thoroghly vetted in other packages (i.e. ietf-cms).
func TestSignVerify(t *testing.T) {
id := &identity{
base: fakeca.New(),
}
data := []byte("tacocat")

sig, _, err := Sign(id, data, SignOptions{
Detached: true,
Armor: true,
// Fake CA outputs self-signed certs, so we need to use -1 to make sure
// the self-signed cert itself is included in the chain, otherwise
// Verify cannot find a cert to use for verification.
IncludeCerts: -1,
})
if err != nil {
t.Fatalf("Sign() = %v", err)
}

fmt.Println(id.base.Chain())
if _, err := Verify(data, sig, true, x509.VerifyOptions{
// Trust the fake CA
Roots: id.base.ChainPool(),
}); err != nil {
t.Fatalf("Verify() = %v", err)
}
}
34 changes: 34 additions & 0 deletions signature/verify.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package signature

import (
"crypto/x509"
"encoding/pem"

cms "github.com/github/smimesign/ietf-cms"
"github.com/pkg/errors"
)

// Verify verifies a signature for a given identity.
//
// WARNING: this function doesn't do any revocation checking.
func Verify(body, sig []byte, detached bool, opts x509.VerifyOptions) ([][][]*x509.Certificate, error) {
// Try decoding as PEM
var der []byte
if blk, _ := pem.Decode(sig); blk != nil {
der = blk.Bytes
} else {
der = sig
}

// Parse signature
sd, err := cms.ParseSignedData(der)
if err != nil {
return nil, errors.Wrap(err, "failed to parse signature")
}

if detached {
return sd.VerifyDetached(body, opts)
} else {
return sd.Verify(opts)
}
}

0 comments on commit 6519737

Please sign in to comment.