зеркало из https://github.com/github/smimesign.git
190 строки
3.9 KiB
Go
190 строки
3.9 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/x509"
|
|
"encoding/pem"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
|
|
"github.com/certifi/gocertifi"
|
|
"github.com/mastahyeti/cms"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
func commandVerify() error {
|
|
sNewSig.emit()
|
|
|
|
if len(fileArgs) < 2 {
|
|
return verifyAttached()
|
|
}
|
|
|
|
return verifyDetached()
|
|
}
|
|
|
|
func verifyAttached() error {
|
|
var (
|
|
f io.ReadCloser
|
|
err error
|
|
)
|
|
|
|
// Read in signature
|
|
if len(fileArgs) == 1 {
|
|
if f, err = os.Open(fileArgs[0]); err != nil {
|
|
return errors.Wrapf(err, "failed to open signature file (%s)", fileArgs[0])
|
|
}
|
|
defer f.Close()
|
|
} else {
|
|
f = stdin
|
|
}
|
|
|
|
buf := new(bytes.Buffer)
|
|
if _, err = io.Copy(buf, f); err != nil {
|
|
return errors.Wrap(err, "failed to read signature")
|
|
}
|
|
|
|
// Try decoding as PEM
|
|
var der []byte
|
|
if blk, _ := pem.Decode(buf.Bytes()); blk != nil {
|
|
der = blk.Bytes
|
|
} else {
|
|
der = buf.Bytes()
|
|
}
|
|
|
|
// Parse signature
|
|
sd, err := cms.ParseSignedData(der)
|
|
if err != nil {
|
|
return errors.Wrap(err, "failed to parse signature")
|
|
}
|
|
|
|
// Verify signature
|
|
chains, err := sd.Verify(verifyOpts())
|
|
if err != nil {
|
|
if len(chains) > 0 {
|
|
emitBadSig(chains)
|
|
} else {
|
|
// TODO: We're omitting a bunch of arguments here.
|
|
sErrSig.emit()
|
|
}
|
|
|
|
return errors.Wrap(err, "failed to verify signature")
|
|
}
|
|
|
|
var (
|
|
cert = chains[0][0][0]
|
|
fpr = certHexFingerprint(cert)
|
|
subj = cert.Subject.String()
|
|
)
|
|
|
|
fmt.Fprintf(stderr, "smimesign: Signature made using certificate ID 0x%s\n", fpr)
|
|
emitGoodSig(chains)
|
|
|
|
// TODO: Maybe split up signature checking and certificate checking so we can
|
|
// output something more meaningful.
|
|
fmt.Fprintf(stderr, "smimesign: Good signature from \"%s\"\n", subj)
|
|
emitTrustFully()
|
|
|
|
return nil
|
|
}
|
|
|
|
func verifyDetached() error {
|
|
var (
|
|
f io.ReadCloser
|
|
err error
|
|
)
|
|
|
|
// Read in signature
|
|
if f, err = os.Open(fileArgs[0]); err != nil {
|
|
return errors.Wrapf(err, "failed to open signature file (%s)", fileArgs[0])
|
|
}
|
|
defer f.Close()
|
|
|
|
buf := new(bytes.Buffer)
|
|
if _, err = io.Copy(buf, f); err != nil {
|
|
return errors.Wrap(err, "failed to read signature file")
|
|
}
|
|
|
|
// Try decoding as PEM
|
|
var der []byte
|
|
if blk, _ := pem.Decode(buf.Bytes()); blk != nil {
|
|
der = blk.Bytes
|
|
} else {
|
|
der = buf.Bytes()
|
|
}
|
|
|
|
// Parse signature
|
|
sd, err := cms.ParseSignedData(der)
|
|
if err != nil {
|
|
return errors.Wrap(err, "failed to parse signature")
|
|
}
|
|
|
|
// Read in signed data
|
|
if fileArgs[1] == "-" {
|
|
f = stdin
|
|
} else {
|
|
if f, err = os.Open(fileArgs[1]); err != nil {
|
|
errors.Wrapf(err, "failed to open message file (%s)", fileArgs[1])
|
|
}
|
|
defer f.Close()
|
|
}
|
|
|
|
// Verify signature
|
|
buf.Reset()
|
|
if _, err = io.Copy(buf, f); err != nil {
|
|
return errors.Wrap(err, "failed to read message file")
|
|
}
|
|
|
|
chains, err := sd.VerifyDetached(buf.Bytes(), verifyOpts())
|
|
if err != nil {
|
|
if len(chains) > 0 {
|
|
emitBadSig(chains)
|
|
} else {
|
|
// TODO: We're omitting a bunch of arguments here.
|
|
sErrSig.emit()
|
|
}
|
|
|
|
return errors.Wrap(err, "failed to verify signature")
|
|
}
|
|
|
|
var (
|
|
cert = chains[0][0][0]
|
|
fpr = certHexFingerprint(cert)
|
|
subj = cert.Subject.String()
|
|
)
|
|
|
|
fmt.Fprintf(stderr, "smimesign: Signature made using certificate ID 0x%s\n", fpr)
|
|
emitGoodSig(chains)
|
|
|
|
// TODO: Maybe split up signature checking and certificate checking so we can
|
|
// output something more meaningful.
|
|
fmt.Fprintf(stderr, "smimesign: Good signature from \"%s\"\n", subj)
|
|
emitTrustFully()
|
|
|
|
return nil
|
|
}
|
|
|
|
func verifyOpts() x509.VerifyOptions {
|
|
roots, err := x509.SystemCertPool()
|
|
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()
|
|
}
|
|
}
|
|
|
|
for _, ident := range idents {
|
|
if cert, err := ident.Certificate(); err == nil {
|
|
roots.AddCert(cert)
|
|
}
|
|
}
|
|
|
|
return x509.VerifyOptions{
|
|
Roots: roots,
|
|
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
|
|
}
|
|
}
|