Skip to content

Commit

Permalink
Refactor Sign/Verify functions into their own library.
Browse files Browse the repository at this point in the history
This commit should have no change in existing behavior, but does the following:

1. Pulls the sign/verify commands into its own package that can be invoked directly
	 instead of needing to go through main.

2. Refactors the certstore library (a dependency of the sign library) to separate out
	 the OS-dependent libraries so that any platform can safely pull in the certstore
	 Identity interface. Adds a register func so that this can be set dynamically in
	 main.

My hope is to use this to allow similar tools to reuse this to provide additional
identities and optional verification behavior.

Signed-off-by: Billy Lynch <[email protected]>
  • Loading branch information
wlynch committed Apr 22, 2022
1 parent 3564e86 commit fb42e64
Show file tree
Hide file tree
Showing 14 changed files with 294 additions and 169 deletions.
8 changes: 8 additions & 0 deletions certstore/certstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,16 @@ var (
// ErrUnsupportedHash is returned by Signer.Sign() when the provided hash
// algorithm isn't supported.
ErrUnsupportedHash = errors.New("unsupported hash algorithm")

openStore func() (Store, error)
)

// RegisterStore registers a func to initialize a new certificate store.
// This should be invoked by providers during init().
func RegisterStore(f func() (Store, error)) {
openStore = f
}

// Open opens the system's certificate store.
func Open() (Store, error) {
return openStore()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package certstore
package providers

/*
#cgo CFLAGS: -x objective-c
Expand All @@ -16,8 +16,14 @@ import (
"fmt"
"io"
"unsafe"

"github.com/github/smimesign/certstore"
)

func init() {
certstore.RegisterStore(openStore)
}

// work around https://golang.org/doc/go1.10#cgo
// in go>=1.10 CFTypeRefs are translated to uintptrs instead of pointers.
var (
Expand All @@ -37,12 +43,12 @@ var (
type macStore int

// openStore is a function for opening a macStore.
func openStore() (macStore, error) {
func openStore() (certstore.Store, error) {
return macStore(0), nil
}

// Identities implements the Store interface.
func (s macStore) Identities() ([]Identity, error) {
func (s macStore) Identities() ([]certstore.Identity, error) {
query := mapToCFDictionary(map[C.CFTypeRef]C.CFTypeRef{
C.CFTypeRef(C.kSecClass): C.CFTypeRef(C.kSecClassIdentity),
C.CFTypeRef(C.kSecReturnRef): C.CFTypeRef(C.kCFBooleanTrue),
Expand All @@ -56,7 +62,7 @@ func (s macStore) Identities() ([]Identity, error) {
var absResult C.CFTypeRef
if err := osStatusError(C.SecItemCopyMatching(query, &absResult)); err != nil {
if err == errSecItemNotFound {
return []Identity{}, nil
return []certstore.Identity{}, nil
}

return nil, err
Expand All @@ -71,7 +77,7 @@ func (s macStore) Identities() ([]Identity, error) {
identRefs := make([]C.CFTypeRef, n)
C.CFArrayGetValues(aryResult, C.CFRange{0, n}, (*unsafe.Pointer)(unsafe.Pointer(&identRefs[0])))

idents := make([]Identity, 0, n)
idents := make([]certstore.Identity, 0, n)
for _, identRef := range identRefs {
idents = append(idents, newMacIdentity(C.SecIdentityRef(identRef)))
}
Expand Down Expand Up @@ -319,7 +325,7 @@ func (i *macIdentity) getAlgo(hash crypto.Hash) (algo C.SecKeyAlgorithm, err err
case crypto.SHA512:
algo = C.kSecKeyAlgorithmECDSASignatureDigestX962SHA512
default:
err = ErrUnsupportedHash
err = certstore.ErrUnsupportedHash
}
case *rsa.PublicKey:
switch hash {
Expand All @@ -332,7 +338,7 @@ func (i *macIdentity) getAlgo(hash crypto.Hash) (algo C.SecKeyAlgorithm, err err
case crypto.SHA512:
algo = C.kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA512
default:
err = ErrUnsupportedHash
err = certstore.ErrUnsupportedHash
}
default:
err = errors.New("unsupported key type")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package certstore
package providers

import "errors"

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package certstore
package providers

import (
"crypto"
Expand All @@ -11,6 +11,7 @@ import (
"crypto/x509"
"testing"

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

Expand All @@ -24,7 +25,7 @@ func TestImportDeleteECDSA(t *testing.T) {

// ImportDeleteHelper is an abstraction for testing identity Import()/Delete().
func ImportDeleteHelper(t *testing.T, i *fakeca.Identity) {
withStore(t, func(store Store) {
withStore(t, func(store certstore.Store) {
// Import an identity
if err := store.Import(i.PFX("asdf"), "asdf"); err != nil {
t.Fatal(err)
Expand All @@ -39,7 +40,7 @@ func ImportDeleteHelper(t *testing.T, i *fakeca.Identity) {
defer ident.Close()
}

var found Identity
var found certstore.Identity
for _, ident := range idents {
crt, errr := ident.Certificate()
if errr != nil {
Expand Down Expand Up @@ -95,7 +96,7 @@ func TestSignerRSA(t *testing.T) {
t.Fatal("expected priv to be an RSA private key")
}

withIdentity(t, leafRSA, func(ident Identity) {
withIdentity(t, leafRSA, func(ident certstore.Identity) {
signer, err := ident.Signer()
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -129,7 +130,7 @@ func TestSignerRSA(t *testing.T) {
// SHA256WithRSA
sha256Digest := sha256.Sum256([]byte("hello"))
sig, err = signer.Sign(rand.Reader, sha256Digest[:], crypto.SHA256)
if err == ErrUnsupportedHash {
if err == certstore.ErrUnsupportedHash {
// Some Windows CSPs may not support this algorithm. Pass...
} else if err != nil {
t.Fatal(err)
Expand All @@ -142,7 +143,7 @@ func TestSignerRSA(t *testing.T) {
// SHA384WithRSA
sha384Digest := sha512.Sum384([]byte("hello"))
sig, err = signer.Sign(rand.Reader, sha384Digest[:], crypto.SHA384)
if err == ErrUnsupportedHash {
if err == certstore.ErrUnsupportedHash {
// Some Windows CSPs may not support this algorithm. Pass...
} else if err != nil {
t.Fatal(err)
Expand All @@ -155,7 +156,7 @@ func TestSignerRSA(t *testing.T) {
// SHA512WithRSA
sha512Digest := sha512.Sum512([]byte("hello"))
sig, err = signer.Sign(rand.Reader, sha512Digest[:], crypto.SHA512)
if err == ErrUnsupportedHash {
if err == certstore.ErrUnsupportedHash {
// Some Windows CSPs may not support this algorithm. Pass...
} else if err != nil {
t.Fatal(err)
Expand All @@ -174,7 +175,7 @@ func TestSignerRSA(t *testing.T) {
// Unsupported hash
sha224Digest := sha256.Sum224([]byte("hello"))
_, err = signer.Sign(rand.Reader, sha224Digest[:], crypto.SHA224)
if err != ErrUnsupportedHash {
if err != certstore.ErrUnsupportedHash {
t.Fatal("expected ErrUnsupportedHash, got ", err)
}
})
Expand All @@ -186,7 +187,7 @@ func TestSignerECDSA(t *testing.T) {
t.Fatal("expected priv to be an ECDSA private key")
}

withIdentity(t, leafEC, func(ident Identity) {
withIdentity(t, leafEC, func(ident certstore.Identity) {
signer, err := ident.Signer()
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -263,9 +264,9 @@ func TestCertificateEC(t *testing.T) {
}

func CertificateHelper(t *testing.T, leaf *fakeca.Identity) {
withIdentity(t, root, func(caIdent Identity) {
withIdentity(t, intermediate, func(interIdent Identity) {
withIdentity(t, leaf, func(leafIdent Identity) {
withIdentity(t, root, func(caIdent certstore.Identity) {
withIdentity(t, intermediate, func(interIdent certstore.Identity) {
withIdentity(t, leaf, func(leafIdent certstore.Identity) {
crtActual, err := leafIdent.Certificate()
if err != nil {
t.Fatal(err)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package certstore
package providers

/*
#cgo windows LDFLAGS: -lcrypt32 -lncrypt
Expand Down Expand Up @@ -75,8 +75,12 @@ type winStore struct {
store C.HCERTSTORE
}

func init() {
certstore.RegisterStore(openStore)
}

// openStore opens the current user's personal cert store.
func openStore() (*winStore, error) {
func openStore() (certstore.Store, error) {
storeName := unsafe.Pointer(stringToUTF16("MY"))
defer C.free(storeName)

Expand Down
File renamed without changes.
18 changes: 10 additions & 8 deletions certstore/main_test.go → certstore/providers/main_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package certstore
package providers

import (
"crypto/ecdsa"
Expand All @@ -9,6 +9,7 @@ import (
"crypto/x509/pkix"
"testing"

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

Expand Down Expand Up @@ -38,11 +39,12 @@ var (

func init() {
// delete any fixtures from a previous test run.
certstore.RegisterStore(openStore)
clearFixtures()
}

func withStore(t *testing.T, cb func(Store)) {
store, err := Open()
func withStore(t *testing.T, cb func(certstore.Store)) {
store, err := certstore.Open()
if err != nil {
t.Fatal(err)
}
Expand All @@ -51,8 +53,8 @@ func withStore(t *testing.T, cb func(Store)) {
cb(store)
}

func withIdentity(t *testing.T, i *fakeca.Identity, cb func(Identity)) {
withStore(t, func(store Store) {
func withIdentity(t *testing.T, i *fakeca.Identity, cb func(certstore.Identity)) {
withStore(t, func(store certstore.Store) {
// Import an identity
if err := store.Import(i.PFX("asdf"), "asdf"); err != nil {
t.Fatal(err)
Expand All @@ -67,7 +69,7 @@ func withIdentity(t *testing.T, i *fakeca.Identity, cb func(Identity)) {
defer ident.Close()
}

var found Identity
var found certstore.Identity
for _, ident := range idents {
crt, err := ident.Certificate()
if err != nil {
Expand All @@ -86,7 +88,7 @@ func withIdentity(t *testing.T, i *fakeca.Identity, cb func(Identity)) {
}

// Clean up after ourselves.
defer func(f Identity) {
defer func(f certstore.Identity) {
if err := f.Delete(); err != nil {
t.Fatal(err)
}
Expand All @@ -97,7 +99,7 @@ func withIdentity(t *testing.T, i *fakeca.Identity, cb func(Identity)) {
}

func clearFixtures() {
store, err := Open()
store, err := certstore.Open()
if err != nil {
panic(err)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package certstore
package providers

import (
"fmt"
Expand Down
Loading

0 comments on commit fb42e64

Please sign in to comment.