зеркало из https://github.com/github/smimesign.git
emit statuses during verification
This commit is contained in:
Родитель
6778dab005
Коммит
8d6b7a642f
|
@ -30,6 +30,11 @@ func commandSign() int {
|
|||
return 1
|
||||
}
|
||||
|
||||
// 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,
|
||||
// though GPGSM does not.
|
||||
sBeginSigning.emit()
|
||||
|
||||
chain, err := userIdent.CertificateChain()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -55,7 +60,6 @@ func commandSign() int {
|
|||
panic(err)
|
||||
}
|
||||
|
||||
// SIG_CREATED
|
||||
emitSigCreated(chain[0], *detachSignFlag)
|
||||
|
||||
if *armorFlag {
|
||||
|
|
|
@ -13,6 +13,8 @@ import (
|
|||
)
|
||||
|
||||
func commandVerify() int {
|
||||
sNewSig.emit()
|
||||
|
||||
if len(fileArgs) < 2 {
|
||||
return verifyAttached()
|
||||
}
|
||||
|
@ -56,12 +58,25 @@ func verifyAttached() int {
|
|||
}
|
||||
|
||||
// Verify signature
|
||||
if _, err = sd.Verify(rootsPool()); err != nil {
|
||||
certs, err := sd.Verify(rootsPool())
|
||||
if err != nil {
|
||||
if len(certs) > 0 {
|
||||
emitBadSig(certs)
|
||||
} else {
|
||||
// TODO: We're ommitting a bunch of arguments here.
|
||||
sErrSig.emit()
|
||||
}
|
||||
|
||||
fmt.Printf("Sinature verification failed: %s\n", err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
fmt.Println("Signature verified")
|
||||
emitGoodSig(certs)
|
||||
|
||||
// TODO: Maybe split up signature checking and certificate checking so we can
|
||||
// output something more meaningful.
|
||||
emitTrustFully()
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
@ -107,12 +122,26 @@ func verifyDetached() int {
|
|||
if _, err = io.Copy(buf, f); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if _, err = sd.VerifyDetached(buf.Bytes(), rootsPool()); err != nil {
|
||||
|
||||
certs, err := sd.VerifyDetached(buf.Bytes(), rootsPool())
|
||||
if err != nil {
|
||||
if len(certs) > 0 {
|
||||
emitBadSig(certs)
|
||||
} else {
|
||||
// TODO: We're ommitting a bunch of arguments here.
|
||||
sErrSig.emit()
|
||||
}
|
||||
|
||||
fmt.Printf("Sinature verification failed: %s\n", err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
fmt.Println("Signature verified")
|
||||
emitGoodSig(certs)
|
||||
|
||||
// TODO: Maybe split up signature checking and certificate checking so we can
|
||||
// output something more meaningful.
|
||||
emitTrustFully()
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// The following was copied from the crypto/openpgpg/packet package.
|
||||
|
||||
// The original license can be found at https://git.io/vbUMQ
|
||||
//
|
||||
// Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
var attributeTypeNames = map[string]string{
|
||||
"2.5.4.6": "C",
|
||||
"2.5.4.10": "O",
|
||||
"2.5.4.11": "OU",
|
||||
"2.5.4.3": "CN",
|
||||
"2.5.4.5": "SERIALNUMBER",
|
||||
"2.5.4.7": "L",
|
||||
"2.5.4.8": "ST",
|
||||
"2.5.4.9": "STREET",
|
||||
"2.5.4.17": "POSTALCODE",
|
||||
}
|
||||
|
||||
// The orignal code can be found at https://git.io/vbUMS
|
||||
//
|
||||
// String implements the fmt.Stringer interface. It loosely follows the
|
||||
// string conversion rules for Distinguished Names from RFC 2253.
|
||||
func RDNSequenceString(r pkix.RDNSequence) string {
|
||||
s := ""
|
||||
for i := 0; i < len(r); i++ {
|
||||
rdn := r[len(r)-1-i]
|
||||
if i > 0 {
|
||||
s += ","
|
||||
}
|
||||
for j, tv := range rdn {
|
||||
if j > 0 {
|
||||
s += "+"
|
||||
}
|
||||
|
||||
oidString := tv.Type.String()
|
||||
typeName, ok := attributeTypeNames[oidString]
|
||||
if !ok {
|
||||
derBytes, err := asn1.Marshal(tv.Value)
|
||||
if err == nil {
|
||||
s += oidString + "=#" + hex.EncodeToString(derBytes)
|
||||
continue // No value escaping necessary.
|
||||
}
|
||||
|
||||
typeName = oidString
|
||||
}
|
||||
|
||||
valueString := fmt.Sprint(tv.Value)
|
||||
escaped := make([]rune, 0, len(valueString))
|
||||
|
||||
for k, c := range valueString {
|
||||
escape := false
|
||||
|
||||
switch c {
|
||||
case ',', '+', '"', '\\', '<', '>', ';':
|
||||
escape = true
|
||||
|
||||
case ' ':
|
||||
escape = k == 0 || k == len(valueString)-1
|
||||
|
||||
case '#':
|
||||
escape = k == 0
|
||||
}
|
||||
|
||||
if escape {
|
||||
escaped = append(escaped, '\\', c)
|
||||
} else {
|
||||
escaped = append(escaped, c)
|
||||
}
|
||||
}
|
||||
|
||||
s += typeName + "=" + string(escaped)
|
||||
}
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
110
status.go
110
status.go
|
@ -19,6 +19,10 @@ import (
|
|||
type status string
|
||||
|
||||
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"
|
||||
|
||||
// SIG_CREATED <type> <pk_algo> <hash_algo> <class> <timestamp> <keyfpr>
|
||||
// A signature has been created using these parameters.
|
||||
|
@ -54,44 +58,27 @@ const (
|
|||
// also be available for OpenPGP.
|
||||
sGoodSig status = "GOODSIG"
|
||||
|
||||
// VALIDSIG <args>
|
||||
// BADSIG <long_keyid_or_fpr> <username>
|
||||
// The signature with the keyid has not been verified okay. The username is
|
||||
// the primary one encoded in UTF-8 and %XX escaped. The fingerprint may be
|
||||
// used instead of the long keyid if it is available. This is the case with
|
||||
// CMS and might eventually also be available for OpenPGP.
|
||||
sBadSig status = "BADSIG"
|
||||
|
||||
// ERRSIG <keyid> <pkalgo> <hashalgo> <sig_class> <time> <rc>
|
||||
//
|
||||
// The args are:
|
||||
// It was not possible to check the signature. This may be caused by a
|
||||
// missing public key or an unsupported algorithm. A RC of 4 indicates
|
||||
// unknown algorithm, a 9 indicates a missing public key. The other fields
|
||||
// give more information about this signature. sig_class is a 2 byte hex-
|
||||
// value. The fingerprint may be used instead of the keyid if it is
|
||||
// available. This is the case with gpgsm and might eventually also be
|
||||
// available for OpenPGP.
|
||||
//
|
||||
// - <fingerprint_in_hex>
|
||||
// - <sig_creation_date>
|
||||
// - <sig-timestamp>
|
||||
// - <expire-timestamp>
|
||||
// - <sig-version>
|
||||
// - <reserved>
|
||||
// - <pubkey-algo>
|
||||
// - <hash-algo>
|
||||
// - <sig-class>
|
||||
// - [ <primary-key-fpr> ]
|
||||
//
|
||||
// This status indicates that the signature is cryptographically
|
||||
// valid. This is similar to GOODSIG, EXPSIG, EXPKEYSIG, or REVKEYSIG
|
||||
// (depending on the date and the state of the signature and signing
|
||||
// key) but has the fingerprint as the argument. Multiple status
|
||||
// lines (VALIDSIG and the other appropriate *SIG status) are emitted
|
||||
// for a valid signature. All arguments here are on one long line.
|
||||
// sig-timestamp is the signature creation time in seconds after the
|
||||
// epoch. expire-timestamp is the signature expiration time in
|
||||
// seconds after the epoch (zero means "does not
|
||||
// expire"). sig-version, pubkey-algo, hash-algo, and sig-class (a
|
||||
// 2-byte hex value) are all straight from the signature packet.
|
||||
// PRIMARY-KEY-FPR is the fingerprint of the primary key or identical
|
||||
// to the first argument. This is useful to get back to the primary
|
||||
// key without running gpg again for this purpose.
|
||||
//
|
||||
// The primary-key-fpr parameter is used for OpenPGP and not
|
||||
// available for CMS signatures. The sig-version as well as the sig
|
||||
// class is not defined for CMS and currently set to 0 and 00.
|
||||
//
|
||||
// Note, that *-TIMESTAMP may either be a number of seconds since
|
||||
// Epoch or an ISO 8601 string which can be detected by the presence
|
||||
// of the letter 'T'.
|
||||
sValidSig status = "VALIDSIG"
|
||||
// Note, that TIME may either be the number of seconds since Epoch or an ISO
|
||||
// 8601 string. The latter can be detected by the presence of the letter
|
||||
// ‘T’.
|
||||
sErrSig status = "ERRSIG"
|
||||
|
||||
// TRUST_
|
||||
// These are several similar status codes:
|
||||
|
@ -119,18 +106,7 @@ const (
|
|||
//
|
||||
// Note that the term =TRUST_= in the status names is used for
|
||||
// historic reasons; we now speak of validity.
|
||||
sTrustUndefined status = "TRUST_UNDEFINED"
|
||||
sTrustNever status = "TRUST_NEVER"
|
||||
sTrustMarginal status = "TRUST_MARGINAL"
|
||||
sTrustFully status = "TRUST_FULLY"
|
||||
sTrustUltimate status = "TRUST_ULTIMATE"
|
||||
|
||||
// VERIFICATION_COMPLIANCE_MODE <flags>
|
||||
// Indicates that the current signature verification operation is in
|
||||
// compliance with the given set of modes. "flags" is a space
|
||||
// separated list of numerical flags, see "Field 18 - Compliance
|
||||
// flags" above.
|
||||
sVerificationComplianceMode = "VERIFICATION_COMPLIANCE_MODE"
|
||||
sTrustFully status = "TRUST_FULLY"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -138,7 +114,7 @@ var (
|
|||
statusFile *os.File
|
||||
)
|
||||
|
||||
func (s status) emit(format string, args ...interface{}) {
|
||||
func (s status) emitf(format string, args ...interface{}) {
|
||||
setupStatus.Do(func() {
|
||||
if *statusFdOpt > 0 {
|
||||
// TODO: debugging output if this fails
|
||||
|
@ -156,6 +132,22 @@ func (s status) emit(format string, args ...interface{}) {
|
|||
fmt.Fprintf(statusFile, " "+format+"\n", args...)
|
||||
}
|
||||
|
||||
func (s status) emit() {
|
||||
setupStatus.Do(func() {
|
||||
if *statusFdOpt > 0 {
|
||||
// TODO: debugging output if this fails
|
||||
statusFile = os.NewFile(uintptr(*statusFdOpt), "status")
|
||||
}
|
||||
})
|
||||
|
||||
if statusFile == nil {
|
||||
return
|
||||
}
|
||||
|
||||
const prefix = "[GNUPG:] "
|
||||
statusFile.WriteString(prefix + string(s) + "\n")
|
||||
}
|
||||
|
||||
func emitSigCreated(cert *x509.Certificate, isDetached bool) {
|
||||
// SIG_CREATED arguments
|
||||
var (
|
||||
|
@ -194,5 +186,23 @@ func emitSigCreated(cert *x509.Certificate, isDetached bool) {
|
|||
now = time.Now().Unix()
|
||||
fpr = certHexFingerprint(cert)
|
||||
|
||||
sSigCreated.emit("%s %d %d %02x %d %s", sigType, pkAlgo, hashAlgo, sigClass, now, fpr)
|
||||
sSigCreated.emitf("%s %d %d %02x %d %s", sigType, pkAlgo, hashAlgo, sigClass, now, fpr)
|
||||
}
|
||||
|
||||
func emitGoodSig(certs []*x509.Certificate) {
|
||||
subj := RDNSequenceString(certs[0].Subject.ToRDNSequence())
|
||||
fpr := certHexFingerprint(certs[0])
|
||||
|
||||
sGoodSig.emitf("%s %s", fpr, subj)
|
||||
}
|
||||
|
||||
func emitBadSig(certs []*x509.Certificate) {
|
||||
subj := RDNSequenceString(certs[0].Subject.ToRDNSequence())
|
||||
fpr := certHexFingerprint(certs[0])
|
||||
|
||||
sBadSig.emitf("%s %s", fpr, subj)
|
||||
}
|
||||
|
||||
func emitTrustFully() {
|
||||
sTrustFully.emitf("0 shell")
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче