@ -6,8 +6,6 @@ import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/sha256"
"dehub.dev/src/dehub.git/fs"
"dehub.dev/src/dehub.git/yamlutil"
"fmt"
"io"
"io/ioutil"
@ -16,6 +14,9 @@ import (
"strings"
"time"
"dehub.dev/src/dehub.git/fs"
"dehub.dev/src/dehub.git/yamlutil"
"golang.org/x/crypto/openpgp/armor"
"golang.org/x/crypto/openpgp/packet"
)
@ -23,8 +24,22 @@ import (
// CredentialPGPSignature describes a PGP signature which has been used to sign
// a commit.
type CredentialPGPSignature struct {
PubKeyID string ` yaml:"pub_key_id" `
Body yamlutil . Blob ` yaml:"body" `
PubKeyID string ` yaml:"pub_key_id" `
PubKeyBody string ` yaml:"pub_key_body,omitempty" `
Body yamlutil . Blob ` yaml:"body" `
}
// SelfVerify will only work if PubKeyBody is filled in. If so, Body will
// attempt to be verified by that public key.
func ( c * CredentialPGPSignature ) SelfVerify ( data [ ] byte ) error {
if c . PubKeyBody == "" {
return ErrNotSelfVerifying {
Subject : "PGP signature Credential with no pub_key_body field" ,
}
}
sig := SignifierPGP { Body : c . PubKeyBody }
return sig . Verify ( nil , data , Credential { PGPSignature : c } )
}
type pgpPubKey struct {
@ -134,10 +149,7 @@ func SignifierPGPTmp(accountID string, randReader io.Reader) (SignifierInterface
panic ( err )
}
return accountSignifier {
accountID : accountID ,
SignifierInterface : privKey ,
} , pubKeyBody
return accountSignifier ( accountID , privKey ) , pubKeyBody
}
func ( s pgpPrivKey ) Sign ( _ fs . FS , data [ ] byte ) ( Credential , error ) {
@ -164,120 +176,119 @@ func (s pgpPrivKey) Sign(_ fs.FS, data []byte) (Credential, error) {
}
// SignifierPGP describes a pgp public key whose corresponding private key will
// be used as a signing key.
// be used as a signing key. The public key can be described by one of multiple
// fields, each being a different method of loading the public key. Only one
// field should be set.
type SignifierPGP struct {
Body string ` yaml:"body" `
}
var _ SignifierInterface = SignifierPGP { }
// An armored string encoding of the public key, as exported via
// `gpg -a --export <key-id>`
Body string ` yaml:"body,omitempty" `
func ( s SignifierPGP ) load ( ) ( pgpPubKey , error ) {
return newPGPPubKey ( strings . NewReader ( s . Body ) )
// Path, relative to the root of the repo, of the armored public key file.
Path string ` yaml:"path,omitempty" `
}
// Sign will sign the given arbitrary bytes using the private key corresponding
// to the pgp public key embedded in this Signifier.
func ( s SignifierPGP ) Sign ( fs fs . FS , data [ ] byte ) ( Credential , error ) {
sigPGP , err := s . load ( )
if err != nil {
return Credential { } , err
}
var _ SignifierInterface = SignifierPGP { }
func cmdGPG ( stdin [ ] byte , args ... string ) ( [ ] byte , error ) {
args = append ( [ ] string { "--openpgp" } , args ... )
stderr := new ( bytes . Buffer )
cmd := exec . Command ( "gpg" ,
"--openpgp" ,
"--detach-sign" ,
"--local-user" , sigPGP . pubKey . KeyIdString ( ) )
cmd . Stdin = bytes . NewBuffer ( data )
cmd := exec . Command ( "gpg" , args ... )
cmd . Stdin = bytes . NewBuffer ( stdin )
cmd . Stderr = stderr
sig , err := cmd . Output ( )
out , err := cmd . Output ( )
if err != nil {
return Credential { } , fmt . Errorf ( "error signing with gpg (%v): %s" , err , stderr . String ( ) )
return nil , fmt . Errorf ( "calling gpg command (%v): %s" , err , stderr . String ( ) )
}
return Credential {
PGPSignature : & CredentialPGPSignature {
PubKeyID : sigPGP . pubKey . KeyIdString ( ) ,
Body : sig ,
} ,
} , nil
return out , nil
}
// Signed returns true if the private key corresponding to the pgp public key
// embedded in this Signifier was used to produce the given Credential.
func ( s SignifierPGP ) Signed ( fs fs . FS , cred Credential ) ( bool , error ) {
sigPGP , err := s . load ( )
// SignifierPGPFromKeyID loads a pgp key using the given identifier. The key is
// assumed to be stored. in the client's keyring already.
//
// If setPubKeyBody is true, then CredentialPGPSignature instances produced by
// the returned Signifier will have their PubKeyBody field set.
func SignifierPGPFromKeyID ( keyID string , setPubKeyBody bool ) ( SignifierInterface , error ) {
pubKey , err := cmdGPG ( nil , "-a" , "--export" , keyID )
if err != nil {
return false , err
return nil , fmt . Errorf ( "loading public k ey: %w" , err )
}
return sigPGP . Signed ( fs , cred )
}
// Verify asserts that the given signature was produced by this key signing the
// given piece of data.
func ( s SignifierPGP ) Verify ( fs fs . FS , data [ ] byte , cred Credential ) error {
sigPGP , err := s . load ( )
if err != nil {
return err
var sigInt SignifierInterface = & SignifierPGP { Body : string ( pubKey ) }
if setPubKeyBody {
sigInt = signifierMiddleware {
SignifierInterface : sigInt ,
signCallback : func ( cred * Credential ) {
cred . PGPSignature . PubKeyBody = string ( pubKey )
} ,
}
}
return sigPGP . Verify ( fs , data , cred )
}
// SignifierPGPFile is the same as SignifierPGP, except that the public key is
// found in the repo rather than encoded into the object.
type SignifierPGPFile struct {
Path string ` yaml:"path" `
return sigInt , nil
}
var _ SignifierInterface = SignifierPGPFile { }
func ( s SignifierPGP ) load ( fs fs . FS ) ( pgpPubKey , error ) {
if s . Body != "" {
return newPGPPubKey ( strings . NewReader ( s . Body ) )
}
func ( s SignifierPGPFile ) load ( fs fs . FS ) ( SignifierPGP , error ) {
path := filepath . Clean ( s . Path )
fr , err := fs . Open ( path )
if err != nil {
return SignifierPGP { } , fmt . Errorf ( "could not open PGP public key file at %q: %w" , path , err )
return pgpPubKey { } , fmt . Errorf ( "opening PGP public key file at %q: %w" , path , err )
}
defer fr . Close ( )
pubKeyB , err := ioutil . ReadAll ( fr )
if err != nil {
return SignifierPGP { } , fmt . Errorf ( "could not read PGP public key from file blob at %q: %w" , s . Path , err )
return pgpPubKey { } , fmt . Errorf ( "reading PGP public key from file at %q: %w" , s . Path , err )
}
return SignifierPGP { Body : string ( pubKeyB ) } , nil
return SignifierPGP { Body : string ( pubKeyB ) } . load ( fs )
}
// Sign will sign the given arbitrary bytes using the private key corresponding
// to the pgp public key located by this Signifier.
func ( s SignifierPGPFile ) Sign ( fs fs . FS , data [ ] byte ) ( Credential , error ) {
// to the pgp public key embedded in this Signifier.
func ( s SignifierPGP ) Sign ( fs fs . FS , data [ ] byte ) ( Credential , error ) {
sigPGP , err := s . load ( fs )
if err != nil {
return Credential { } , err
}
return sigPGP . Sign ( fs , data )
}
// Signed returns true if the private key corresponding to the pgp public key
// located by this Signifier was used to produce the given Credential.
func ( s SignifierPGPFile ) Signed ( fs fs . FS , cred Credential ) ( bool , error ) {
if cred . PGPSignature == nil {
return false , nil
sig , err := cmdGPG ( data , "--detach-sign" , "--local-user" , sigPGP . pubKey . KeyIdString ( ) )
if err != nil {
return Credential { } , fmt . Errorf ( "signing with pgp key: %w" , err )
}
return Credential {
PGPSignature : & CredentialPGPSignature {
PubKeyID : sigPGP . pubKey . KeyIdString ( ) ,
Body : sig ,
} ,
} , nil
}
// Signed returns true if the private key corresponding to the pgp public key
// embedded in this Signifier was used to produce the given Credential.
func ( s SignifierPGP ) Signed ( fs fs . FS , cred Credential ) ( bool , error ) {
sigPGP , err := s . load ( fs )
if err != nil {
return false , err
}
return sigPGP . Signed ( fs , cred )
}
// Verify asserts that the given signature was produced by this key signing the
// given piece of data.
func ( s SignifierPGPFile ) Verify ( fs fs . FS , data [ ] byte , cred Credential ) error {
func ( s SignifierPGP ) Verify ( fs fs . FS , data [ ] byte , cred Credential ) error {
sigPGP , err := s . load ( fs )
if err != nil {
return err
}
return sigPGP . Verify ( fs , data , cred )
}
// SignifierPGPFile is deprecated and should not be used, use the Path field of
// SignifierPGP instead.
type SignifierPGPFile struct {
Path string ` yaml:"path" `
}