From c6e2b3ce6f3a11d114a2f317a4daa84b6c084b98 Mon Sep 17 00:00:00 2001 From: Adrian Hassan Abdala Date: Mon, 27 Jul 2020 07:35:49 -0400 Subject: [PATCH 01/11] Bug fix for windows where the root store was not being parsed, and instead was defaulting to the gocertifi library. This bug prevents users from adding trusted roots and verifying signatures using those roots --- command_verify.go | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/command_verify.go b/command_verify.go index 5025a31..ba1b6ee 100644 --- a/command_verify.go +++ b/command_verify.go @@ -7,12 +7,17 @@ import ( "fmt" "io" "os" + "syscall" + "unsafe" - "github.com/certifi/gocertifi" "github.com/mastahyeti/cms" "github.com/pkg/errors" ) +const ( + CRYPT_E_NOT_FOUND = 0x80092004 +) + func commandVerify() error { sNewSig.emit() @@ -165,20 +170,32 @@ func verifyDetached() error { } func verifyOpts() x509.VerifyOptions { - roots, err := x509.SystemCertPool() + roots := x509.NewCertPool() + storeHandle, err := syscall.CertOpenSystemStore(0, syscall.StringToUTF16Ptr("Root")) if err != nil { - // SystemCertPool isn't implemented for Windows. fall back to mozilla trust - // store. - roots, err = gocertifi.CACerts() - if err != nil { - // Fall back to an empty store. Verification will likely fail. - roots = x509.NewCertPool() - } + fmt.Println(syscall.GetLastError()) } - for _, ident := range idents { - if cert, err := ident.Certificate(); err == nil { - roots.AddCert(cert) + var cert *syscall.CertContext + for { + cert, err = syscall.CertEnumCertificatesInStore(storeHandle, cert) + if err != nil { + if errno, ok := err.(syscall.Errno); ok { + if errno == CRYPT_E_NOT_FOUND { + break + } + } + fmt.Println(syscall.GetLastError()) + } + if cert == nil { + break + } + // Copy the buf, since ParseCertificate does not create its own copy. + buf := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:] + buf2 := make([]byte, cert.Length) + copy(buf2, buf) + if c, err := x509.ParseCertificate(buf2); err == nil { + roots.AddCert(c) } } From a07e16251620f17837ef8052525961f161b574d1 Mon Sep 17 00:00:00 2001 From: Adrian Hassan Abdala Date: Mon, 27 Jul 2020 08:56:26 -0400 Subject: [PATCH 02/11] Split the root store parsing function into two different functions that are called depa nding on operating system --- command_verify.go | 58 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/command_verify.go b/command_verify.go index ba1b6ee..8f54dc2 100644 --- a/command_verify.go +++ b/command_verify.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "os" + "runtime" "syscall" "unsafe" @@ -170,10 +171,53 @@ func verifyDetached() error { } func verifyOpts() x509.VerifyOptions { - roots := x509.NewCertPool() - storeHandle, err := syscall.CertOpenSystemStore(0, syscall.StringToUTF16Ptr("Root")) + var ( + roots *x509.CertPool + ) + + // Depending on the operating system, enumerate the trusted root certificate store + switch runtime.GOOS { + case "windows": + err := getRootsWindows(roots) + if err != nil{ + roots = x509.NewCertPool() + } + default: + err := getRoots(roots) + if err != nil{ + roots = x509.NewCertPool() + } + } + return x509.VerifyOptions{ + Roots: roots, + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, + } +} + +func getRoots(roots *x509.CertPool ) error{ + roots, err := x509.SystemCertPool() if err != nil { - fmt.Println(syscall.GetLastError()) + return errors.Wrap(err, "Failed to parse root store") + } + + for _, ident := range idents { + if cert, err := ident.Certificate(); err == nil { + roots.AddCert(cert) + } + } + return nil +} + +func getRootsWindows(roots *x509.CertPool) error{ + roots = x509.NewCertPool() + + storeName, err:= syscall.UTF16PtrFromString("Root") + if err != nil { + return errors.Wrap(err, "Failed to get root store name") + } + storeHandle, err := syscall.CertOpenSystemStore(0, storeName) + if err != nil { + return errors.New(syscall.GetLastError().Error()) } var cert *syscall.CertContext @@ -185,7 +229,7 @@ func verifyOpts() x509.VerifyOptions { break } } - fmt.Println(syscall.GetLastError()) + return errors.New(syscall.GetLastError().Error()) } if cert == nil { break @@ -198,9 +242,5 @@ func verifyOpts() x509.VerifyOptions { roots.AddCert(c) } } - - return x509.VerifyOptions{ - Roots: roots, - KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, - } + return nil } From af315080945ff6b297f951790405ababe9766d70 Mon Sep 17 00:00:00 2001 From: Adrian Hassan Abdala Date: Mon, 27 Jul 2020 09:15:35 -0400 Subject: [PATCH 03/11] Fixed errors with OSX with windows specific syscalls. Split the functions reading the system root store into two files for unix systems and windows systems. --- command_verify.go | 76 ++++-------------------------------------- parse_roots.go | 16 +++++++++ parse_roots_windows.go | 51 ++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 70 deletions(-) create mode 100644 parse_roots.go create mode 100644 parse_roots_windows.go diff --git a/command_verify.go b/command_verify.go index 8f54dc2..3905ac3 100644 --- a/command_verify.go +++ b/command_verify.go @@ -5,19 +5,12 @@ import ( "crypto/x509" "encoding/pem" "fmt" - "io" - "os" - "runtime" - "syscall" - "unsafe" - "github.com/mastahyeti/cms" "github.com/pkg/errors" + "io" + "os" ) -const ( - CRYPT_E_NOT_FOUND = 0x80092004 -) func commandVerify() error { sNewSig.emit() @@ -176,71 +169,14 @@ func verifyOpts() x509.VerifyOptions { ) // Depending on the operating system, enumerate the trusted root certificate store - switch runtime.GOOS { - case "windows": - err := getRootsWindows(roots) - if err != nil{ - roots = x509.NewCertPool() - } - default: - err := getRoots(roots) - if err != nil{ - roots = x509.NewCertPool() - } + err := parseRoots(roots) + if err != nil{ + roots = x509.NewCertPool() } + return x509.VerifyOptions{ Roots: roots, KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, } } -func getRoots(roots *x509.CertPool ) error{ - roots, err := x509.SystemCertPool() - if err != nil { - return errors.Wrap(err, "Failed to parse root store") - } - - for _, ident := range idents { - if cert, err := ident.Certificate(); err == nil { - roots.AddCert(cert) - } - } - return nil -} - -func getRootsWindows(roots *x509.CertPool) error{ - roots = x509.NewCertPool() - - storeName, err:= syscall.UTF16PtrFromString("Root") - if err != nil { - return errors.Wrap(err, "Failed to get root store name") - } - storeHandle, err := syscall.CertOpenSystemStore(0, storeName) - if err != nil { - return errors.New(syscall.GetLastError().Error()) - } - - var cert *syscall.CertContext - for { - cert, err = syscall.CertEnumCertificatesInStore(storeHandle, cert) - if err != nil { - if errno, ok := err.(syscall.Errno); ok { - if errno == CRYPT_E_NOT_FOUND { - break - } - } - return errors.New(syscall.GetLastError().Error()) - } - if cert == nil { - break - } - // Copy the buf, since ParseCertificate does not create its own copy. - buf := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:] - buf2 := make([]byte, cert.Length) - copy(buf2, buf) - if c, err := x509.ParseCertificate(buf2); err == nil { - roots.AddCert(c) - } - } - return nil -} diff --git a/parse_roots.go b/parse_roots.go new file mode 100644 index 0000000..2af3958 --- /dev/null +++ b/parse_roots.go @@ -0,0 +1,16 @@ +// +build !windows + +package main +func parseRoots(roots *x509.CertPool ) error{ + roots, err := x509.SystemCertPool() + if err != nil { + return errors.Wrap(err, "Failed to parse root store") + } + + for _, ident := range idents { + if cert, err := ident.Certificate(); err == nil { + roots.AddCert(cert) + } + } + return nil +} diff --git a/parse_roots_windows.go b/parse_roots_windows.go new file mode 100644 index 0000000..cbf68a8 --- /dev/null +++ b/parse_roots_windows.go @@ -0,0 +1,51 @@ +// +build windows + +package main + +import ( + "crypto/x509" + "github.com/pkg/errors" + "syscall" + "unsafe" +) + +const ( + CryptENotFound = 0x80092004 +) + +func parseRoots(roots *x509.CertPool) error{ + roots = x509.NewCertPool() + + storeName, err:= syscall.UTF16PtrFromString("Root") + if err != nil { + return errors.Wrap(err, "Failed to get root store name") + } + storeHandle, err := syscall.CertOpenSystemStore(0, storeName) + if err != nil { + return errors.New(syscall.GetLastError().Error()) + } + + var cert *syscall.CertContext + for { + cert, err = syscall.CertEnumCertificatesInStore(storeHandle, cert) + if err != nil { + if errno, ok := err.(syscall.Errno); ok { + if errno == CryptENotFound { + break + } + } + return errors.New(syscall.GetLastError().Error()) + } + if cert == nil { + break + } + // Copy the buf, since ParseCertificate does not create its own copy. + buf := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:] + buf2 := make([]byte, cert.Length) + copy(buf2, buf) + if c, err := x509.ParseCertificate(buf2); err == nil { + roots.AddCert(c) + } + } + return nil +} From 9e78f912cfa2ab11862e886580294c395acec1dc Mon Sep 17 00:00:00 2001 From: Adrian Hassan Abdala Date: Mon, 27 Jul 2020 09:20:34 -0400 Subject: [PATCH 04/11] Forgot to include dependancies in the parse_root unix implementation --- parse_roots.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/parse_roots.go b/parse_roots.go index 2af3958..2e02995 100644 --- a/parse_roots.go +++ b/parse_roots.go @@ -1,6 +1,12 @@ // +build !windows package main + +import ( + "crypto/x509" + "github.com/pkg/errors" +) + func parseRoots(roots *x509.CertPool ) error{ roots, err := x509.SystemCertPool() if err != nil { From 071351bc5e7d32e24991a5c8bc603232e86d6d73 Mon Sep 17 00:00:00 2001 From: Lucas Garron Date: Thu, 30 Jul 2020 14:30:29 -0700 Subject: [PATCH 05/11] Update tests to allow chains in any order. Fixes #68. The chain in question is defined in RFC5652 as a CertificateSet, which is a `SET OF` certs: - https://tools.ietf.org/html/rfc5652#section-5.1 - https://tools.ietf.org/html/rfc5652#section-10.2.3 Go 1.15 introduced a change that sorts such sets: https://go.googlesource.com/go/+/f0cea848679b8f8cdc5f76e1b1e36ebb924a68f8 Our tests were implicitly relying on the sets to stay sorted, but the right thing to do at this point is update our tests. --- command_sign_test.go | 49 ++++++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/command_sign_test.go b/command_sign_test.go index d39767a..72ef2f0 100644 --- a/command_sign_test.go +++ b/command_sign_test.go @@ -9,6 +9,15 @@ import ( "github.com/stretchr/testify/require" ) +func chainContains(chain []*x509.Certificate, want *x509.Certificate) bool { + for _, cert := range chain { + if cert.Equal(want) { + return true + } + } + return false +} + func TestSign(t *testing.T) { defer testSetup(t, "--sign", "-u", certHexFingerprint(leaf.Certificate))() @@ -37,8 +46,8 @@ func TestSignIncludeCertsAIA(t *testing.T) { require.NoError(t, err) require.Equal(t, 2, len(certs)) - require.True(t, certs[0].Equal(aiaLeaf.Certificate)) - require.True(t, certs[1].Equal(intermediate.Certificate)) + require.True(t, chainContains(certs, aiaLeaf.Certificate)) + require.True(t, chainContains(certs, intermediate.Certificate)) } func TestSignIncludeCertsDefault(t *testing.T) { @@ -57,8 +66,8 @@ func TestSignIncludeCertsDefault(t *testing.T) { require.NoError(t, err) require.Equal(t, 2, len(certs)) - require.True(t, certs[0].Equal(leaf.Certificate)) - require.True(t, certs[1].Equal(intermediate.Certificate)) + require.True(t, chainContains(certs, leaf.Certificate)) + require.True(t, chainContains(certs, intermediate.Certificate)) } func TestSignIncludeCertsMinus3(t *testing.T) { @@ -77,8 +86,8 @@ func TestSignIncludeCertsMinus3(t *testing.T) { require.NoError(t, err) require.Equal(t, 2, len(certs)) - require.True(t, certs[0].Equal(leaf.Certificate)) - require.True(t, certs[1].Equal(intermediate.Certificate)) + require.True(t, chainContains(certs, leaf.Certificate)) + require.True(t, chainContains(certs, intermediate.Certificate)) } func TestSignIncludeCertsMinus2(t *testing.T) { @@ -97,8 +106,8 @@ func TestSignIncludeCertsMinus2(t *testing.T) { require.NoError(t, err) require.Equal(t, 2, len(certs)) - require.True(t, certs[0].Equal(leaf.Certificate)) - require.True(t, certs[1].Equal(intermediate.Certificate)) + require.True(t, chainContains(certs, leaf.Certificate)) + require.True(t, chainContains(certs, intermediate.Certificate)) } func TestSignIncludeCertsMinus1(t *testing.T) { @@ -117,9 +126,9 @@ func TestSignIncludeCertsMinus1(t *testing.T) { require.NoError(t, err) require.Equal(t, 3, len(certs)) - require.True(t, certs[0].Equal(leaf.Certificate)) - require.True(t, certs[1].Equal(intermediate.Certificate)) - require.True(t, certs[2].Equal(ca.Certificate)) + require.True(t, chainContains(certs, leaf.Certificate)) + require.True(t, chainContains(certs, intermediate.Certificate)) + require.True(t, chainContains(certs, ca.Certificate)) } func TestSignIncludeCerts0(t *testing.T) { @@ -156,7 +165,7 @@ func TestSignIncludeCerts1(t *testing.T) { require.NoError(t, err) require.Equal(t, 1, len(certs)) - require.True(t, certs[0].Equal(leaf.Certificate)) + require.True(t, chainContains(certs, leaf.Certificate)) } func TestSignIncludeCerts2(t *testing.T) { @@ -175,8 +184,8 @@ func TestSignIncludeCerts2(t *testing.T) { require.NoError(t, err) require.Equal(t, 2, len(certs)) - require.True(t, certs[0].Equal(leaf.Certificate)) - require.True(t, certs[1].Equal(intermediate.Certificate)) + require.True(t, chainContains(certs, leaf.Certificate)) + require.True(t, chainContains(certs, intermediate.Certificate)) } func TestSignIncludeCerts3(t *testing.T) { @@ -195,9 +204,9 @@ func TestSignIncludeCerts3(t *testing.T) { require.NoError(t, err) require.Equal(t, 3, len(certs)) - require.True(t, certs[0].Equal(leaf.Certificate)) - require.True(t, certs[1].Equal(intermediate.Certificate)) - require.True(t, certs[2].Equal(ca.Certificate)) + require.True(t, chainContains(certs, leaf.Certificate)) + require.True(t, chainContains(certs, intermediate.Certificate)) + require.True(t, chainContains(certs, ca.Certificate)) } func TestSignIncludeCerts4(t *testing.T) { @@ -216,7 +225,7 @@ func TestSignIncludeCerts4(t *testing.T) { require.NoError(t, err) require.Equal(t, 3, len(certs)) - require.True(t, certs[0].Equal(leaf.Certificate)) - require.True(t, certs[1].Equal(intermediate.Certificate)) - require.True(t, certs[2].Equal(ca.Certificate)) + require.True(t, chainContains(certs, leaf.Certificate)) + require.True(t, chainContains(certs, intermediate.Certificate)) + require.True(t, chainContains(certs, ca.Certificate)) } From 80e6d292ef81a7811ef162cea50e10dd64d85a07 Mon Sep 17 00:00:00 2001 From: Lucas Garron Date: Thu, 30 Jul 2020 10:42:44 -0700 Subject: [PATCH 06/11] Change `BEGING_SIGNING` to `BEGIN_SIGNING`. Per https://github.com/github/smimesign/blob/c068195384830899ef594141a7586039afd0c7ec/status.go#L17 and https://github.com/gpg/gnupg/blob/918792befd835e04b4043b9ce42ea6d829a284fa/doc/DETAILS#begin_signing , sthis is a typo. --- command_sign.go | 2 +- status.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/command_sign.go b/command_sign.go index bb961e5..d15bd38 100644 --- a/command_sign.go +++ b/command_sign.go @@ -24,7 +24,7 @@ func commandSign() error { } // Git is looking for "\n[GNUPG:] SIG_CREATED ", meaning we need to print a - // line before SIG_CREATED. BEGING_SIGNING seems appropraite. GPG emits this, + // line before SIG_CREATED. BEGIN_SIGNING seems appropraite. GPG emits this, // though GPGSM does not. sBeginSigning.emit() diff --git a/status.go b/status.go index 9f789fd..852e66a 100644 --- a/status.go +++ b/status.go @@ -22,7 +22,7 @@ const ( // BEGIN_SIGNING // Mark the start of the actual signing process. This may be used as an // indication that all requested secret keys are ready for use. - sBeginSigning status = "BEGING_SIGNING" + sBeginSigning status = "BEGIN_SIGNING" // SIG_CREATED // A signature has been created using these parameters. From 2e17a57ff2ef4b91975a2ec23458ffa3d2eea36b Mon Sep 17 00:00:00 2001 From: Lucas Garron Date: Tue, 11 Aug 2020 18:02:00 -0700 Subject: [PATCH 07/11] Update dependencies and references for v0.1.0 --- README.md | 6 +++--- command_sign.go | 4 ++-- command_sign_test.go | 4 ++-- command_verify.go | 2 +- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- main.go | 2 +- main_test.go | 4 ++-- 8 files changed, 23 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 3d8d805..d449242 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# smimesign (S/MIME Sign) [![Windows Build Status](https://ci.appveyor.com/api/projects/status/hs3o1m9ornnp9d52/branch/master?svg=true)](https://ci.appveyor.com/project/mastahyeti/smimesign-0bhgj/branch/master) [![MacOS Build Status](https://travis-ci.org/github/smimesign.svg?branch=master)](https://travis-ci.org/github/smimesign) +# smimesign (S/MIME Sign) [![Windows Build Status](https://ci.appveyor.com/api/projects/status/hs3o1m9ornnp9d52/branch/master?svg=true)](https://ci.appveyor.com/project/github/smimesign-0bhgj/branch/main) [![MacOS Build Status](https://travis-ci.org/github/smimesign.svg?branch=master)](https://travis-ci.org/github/smimesign) -Smimesign is an S/MIME signing utility for macOS and Windows that is compatible with Git. This allows developers to sign their Git commits and tags using X.509 certificates issued by public certificate authorities or their organization's internal certificate authority. Smimesign uses keys and certificates already stored in the *macOS Keychain* or the *Windows Certificate Store*. +Smimesign is an S/MIME signing utility for macOS and Windows that is compatible with Git. This allows developers to sign their Git commits and tags using X.509 certificates issued by public certificate authorities or their organization's internal certificate authority. Smimesign uses keys and certificates already stored in the _macOS Keychain_ or the _Windows Certificate Store_. This project is pre-1.0, meaning that APIs and functionality may change without warning. @@ -14,7 +14,7 @@ Git allows developers to sign their work using GnuPG. This is a form of public k Such a model is well suited to an unstructured environment. In hierarchical environments though, such as a corporation or other large organizations, a simpler approach is for digital identities to be issued and vouched for by a centralized authority. With this approach — known as Public Key Infrastructure, or PKI — an organization's certificate authority (CA) issues signed certificates that identify subjects such as people or computers. Embedded in these certificates is the identity's public key, allowing others who trust the CA to verify that identity's signatures. -PKI is used in a variety of applications for encrypting or authenticating communications. Secure Mime (S/MIME) standardized a protocol for encrypting and signing emails using PKI. While protecting email was the original intent, S/MIME can protect any type of data, including Git commits and tags. Signing Git data with S/MIME provides the same protections as GnuPG while allowing for the more hierarchical trust model of PKI. +PKI is used in a variety of applications for encrypting or authenticating communications. Secure Mime (S/MIME) standardized a protocol for encrypting and signing emails using PKI. While protecting email was the original intent, S/MIME can protect any type of data, including Git commits and tags. Signing Git data with S/MIME provides the same protections as GnuPG while allowing for the more hierarchical trust model of PKI. ## Installation diff --git a/command_sign.go b/command_sign.go index d15bd38..35e9e27 100644 --- a/command_sign.go +++ b/command_sign.go @@ -9,8 +9,8 @@ import ( "os" "strings" - "github.com/mastahyeti/certstore" - "github.com/mastahyeti/cms" + "github.com/github/certstore" + "github.com/github/ietf-cms" "github.com/pkg/errors" ) diff --git a/command_sign_test.go b/command_sign_test.go index 72ef2f0..85c9c4d 100644 --- a/command_sign_test.go +++ b/command_sign_test.go @@ -4,8 +4,8 @@ import ( "crypto/x509" "testing" - "github.com/mastahyeti/cms" - "github.com/mastahyeti/cms/protocol" + "github.com/github/ietf-cms/protocol" + "github.com/github/ietf-cms" "github.com/stretchr/testify/require" ) diff --git a/command_verify.go b/command_verify.go index 5025a31..82bfda9 100644 --- a/command_verify.go +++ b/command_verify.go @@ -9,7 +9,7 @@ import ( "os" "github.com/certifi/gocertifi" - "github.com/mastahyeti/cms" + "github.com/github/ietf-cms" "github.com/pkg/errors" ) diff --git a/go.mod b/go.mod index e1d1dd2..514abfd 100644 --- a/go.mod +++ b/go.mod @@ -5,11 +5,11 @@ go 1.12 require ( github.com/certifi/gocertifi v0.0.0-20180118203423-deb3ae2ef261 github.com/davecgh/go-spew v1.1.1 - github.com/mastahyeti/certstore v0.0.5 - github.com/mastahyeti/cms v0.0.6 - github.com/mastahyeti/fakeca v0.0.1 + github.com/github/certstore v0.1.0 + github.com/github/fakeca v0.1.0 + github.com/github/ietf-cms v0.1.0 github.com/pborman/getopt v0.0.0-20180811024354-2b5b3bfb099b - github.com/pkg/errors v0.8.0 + github.com/pkg/errors v0.8.1 github.com/pmezard/go-difflib v1.0.0 github.com/stretchr/testify v1.3.0 golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 diff --git a/go.sum b/go.sum index f983c42..cf43f3c 100644 --- a/go.sum +++ b/go.sum @@ -3,18 +3,18 @@ github.com/certifi/gocertifi v0.0.0-20180118203423-deb3ae2ef261/go.mod h1:GJKEex github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/mastahyeti/certstore v0.0.4 h1:lIS0StbHgmgUIpen7aayW+BGBFs7g141fOcjuobrFj8= -github.com/mastahyeti/certstore v0.0.4/go.mod h1:G29tHH2jDKK45cvISMzk8ZRf3KrhRS7ptoteyYzztsk= -github.com/mastahyeti/certstore v0.0.5 h1:8JV/YC8jN6SD+ocJi46PSdxXfPxwgilJJEA8HnG49ls= -github.com/mastahyeti/certstore v0.0.5/go.mod h1:NHRRUQaEsIFEo+2nAxmf6oSdjb5g8LJoHx0nyND25G8= -github.com/mastahyeti/cms v0.0.6 h1:uBCHEpysYW9vpxunhDwX/+ptTNDRqPkCA0j/XT/2zZM= -github.com/mastahyeti/cms v0.0.6/go.mod h1:AMtGAAONAIEUX7kXN2o5oBLtMlU3+/w0xfEyBgtkB4Y= -github.com/mastahyeti/fakeca v0.0.1 h1:4yzOmKJPS34k5zSKuapDkyShN1gVHteF7iD5xMOC/4A= -github.com/mastahyeti/fakeca v0.0.1/go.mod h1:FUs0aY6rbIiAh2dqCkvirZMFXOc3zH1r6ELiNyNy+FQ= +github.com/github/certstore v0.1.0 h1:oZF2PcqgBo6YNp7gCUDfF6vP9c0kTxh5VhUNrW6d2wc= +github.com/github/certstore v0.1.0/go.mod h1:Sgb3YVYOB2iCO06NJ6We5gjXe7uxxM3zPYoEXjuTKno= +github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I= +github.com/github/fakeca v0.1.0/go.mod h1:+bormgoGMMuamOscx7N91aOuUST7wdaJ2rNjeohylyo= +github.com/github/ietf-cms v0.1.0 h1:D+O9re6xDeWTYRpAFTfM0dm5NqJUcXZKFGOQg5Iq6Ls= +github.com/github/ietf-cms v0.1.0/go.mod h1:eJEmhqWUqjpuS6OoXiqtuTmzOx4u81npQrXOzt/sPqo= github.com/pborman/getopt v0.0.0-20180811024354-2b5b3bfb099b h1:K1wa7ads2Bu1PavI6LfBRMYSy6Zi+Rky0OhWBfrmkmY= github.com/pborman/getopt v0.0.0-20180811024354-2b5b3bfb099b/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/main.go b/main.go index f5b02bd..8d57535 100644 --- a/main.go +++ b/main.go @@ -7,7 +7,7 @@ import ( "io" "os" - "github.com/mastahyeti/certstore" + "github.com/github/certstore" "github.com/pborman/getopt/v2" "github.com/pkg/errors" ) diff --git a/main_test.go b/main_test.go index d38c61d..0a46190 100644 --- a/main_test.go +++ b/main_test.go @@ -8,8 +8,8 @@ import ( "os" "testing" - "github.com/mastahyeti/certstore" - "github.com/mastahyeti/fakeca" + "github.com/github/certstore" + "github.com/github/fakeca" "github.com/pborman/getopt/v2" ) From d02f769b748a117f394322946169e90b1781f3e1 Mon Sep 17 00:00:00 2001 From: Lucas Garron Date: Thu, 13 Aug 2020 13:17:37 -0700 Subject: [PATCH 08/11] Set Appveyor branch to `main`. --- .appveyor.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 8fb98bc..b1f591b 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,7 +1,7 @@ skip_branch_with_pr: true -version: '{build}-{branch}' +version: "{build}-{branch}" -os: 'Visual Studio 2015' +os: "Visual Studio 2015" clone_folder: C:\gopath\src\github.com\github\smimesign @@ -79,5 +79,5 @@ deploy: draft: false prerelease: false on: - branch: master + branch: main appveyor_repo_tag: true From a76a9f14b4280d16e70a6630e01cf54019acdc48 Mon Sep 17 00:00:00 2001 From: Lucas Garron Date: Mon, 17 Aug 2020 12:25:31 -0700 Subject: [PATCH 09/11] Update build status link. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d449242..619c5b1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# smimesign (S/MIME Sign) [![Windows Build Status](https://ci.appveyor.com/api/projects/status/hs3o1m9ornnp9d52/branch/master?svg=true)](https://ci.appveyor.com/project/github/smimesign-0bhgj/branch/main) [![MacOS Build Status](https://travis-ci.org/github/smimesign.svg?branch=master)](https://travis-ci.org/github/smimesign) +# smimesign (S/MIME Sign) [![Windows Build Status](https://ci.appveyor.com/api/projects/status/hs3o1m9ornnp9d52/branch/master?svg=true)](https://ci.appveyor.com/project/github/smimesign-0bhgj/branch/main) [![macOS Build Status](https://travis-ci.org/github/smimesign.svg?branch=main)](https://travis-ci.org/github/smimesign) Smimesign is an S/MIME signing utility for macOS and Windows that is compatible with Git. This allows developers to sign their Git commits and tags using X.509 certificates issued by public certificate authorities or their organization's internal certificate authority. Smimesign uses keys and certificates already stored in the _macOS Keychain_ or the _Windows Certificate Store_. From aacfb7e9c0d9939af7db1397b7c6e1988eecc78b Mon Sep 17 00:00:00 2001 From: Adrian Abdala Date: Mon, 21 Dec 2020 11:30:26 -0500 Subject: [PATCH 10/11] Added a defer statement to ensure that there is a syscall to close the Certificate Store when completed enumarating trusted roots on Windows --- command_verify.go | 2 +- parse_roots_windows.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/command_verify.go b/command_verify.go index bfe3826..aff28de 100644 --- a/command_verify.go +++ b/command_verify.go @@ -8,7 +8,6 @@ import ( "io" "os" - "github.com/certifi/gocertifi" "github.com/github/ietf-cms" "github.com/pkg/errors" ) @@ -173,6 +172,7 @@ func verifyOpts() x509.VerifyOptions { // Depending on the operating system, enumerate the trusted root certificate store err := parseRoots(roots) if err != nil{ + // Fall back to an empty store. Verifications will likely fail. roots = x509.NewCertPool() } diff --git a/parse_roots_windows.go b/parse_roots_windows.go index cbf68a8..03188d4 100644 --- a/parse_roots_windows.go +++ b/parse_roots_windows.go @@ -24,6 +24,7 @@ func parseRoots(roots *x509.CertPool) error{ if err != nil { return errors.New(syscall.GetLastError().Error()) } + defer syscall.CertCloseStore(storeHandle, 0) var cert *syscall.CertContext for { From 1517782a35345a4935eab624550ea70ae70b67b9 Mon Sep 17 00:00:00 2001 From: Adrian Abdala Date: Mon, 21 Dec 2020 12:29:15 -0500 Subject: [PATCH 11/11] Use gocertifi certificate pool as a baseline and add system trusted roots to this pool. Duplicate certs are automatically dropped --- parse_roots_windows.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/parse_roots_windows.go b/parse_roots_windows.go index 03188d4..ab75109 100644 --- a/parse_roots_windows.go +++ b/parse_roots_windows.go @@ -4,6 +4,7 @@ package main import ( "crypto/x509" + "github.com/certifi/gocertifi" "github.com/pkg/errors" "syscall" "unsafe" @@ -14,8 +15,15 @@ const ( ) func parseRoots(roots *x509.CertPool) error{ - roots = x509.NewCertPool() + // The windows trust store is dynamically populated, to prevent issues with generally trusted + // roots not being enumerated, use the mozilla trust store as a baseline + roots, err := gocertifi.CACerts() + if err != nil { + roots = x509.NewCertPool() + } + + // Enumerate the local machine trust store and add any missing certificates. storeName, err:= syscall.UTF16PtrFromString("Root") if err != nil { return errors.Wrap(err, "Failed to get root store name") @@ -45,8 +53,13 @@ func parseRoots(roots *x509.CertPool) error{ buf2 := make([]byte, cert.Length) copy(buf2, buf) if c, err := x509.ParseCertificate(buf2); err == nil { + // AddCert contains logic to prevent adding a duplicate certificate to the pool roots.AddCert(c) } } + + + + return nil }