55package v2
66
77import (
8+ "bytes"
89 "crypto"
10+ goerrors "errors"
911 "hash"
1012 "io"
1113 "strconv"
@@ -221,6 +223,11 @@ type EncryptParams struct {
221223 // SessionKey provides a session key to be used for encryption.
222224 // If nil, a one-time session key is generated
223225 SessionKey []byte
226+ // OutsideSig allows to set a signature that should be included
227+ // in the message to encrypt.
228+ // Should only be used for exceptional cases.
229+ // If nil, ignored.
230+ OutsideSig []byte
224231 // Config provides the config to be used.
225232 // If Config is nil, sensible defaults will be used.
226233 Config * packet.Config
@@ -383,8 +390,46 @@ func Encrypt(ciphertext io.Writer, to, toHidden []*Entity, signers []*Entity, hi
383390// that aids the recipients in processing the message. The resulting
384391// WriteCloser must be closed after the contents of the file have been
385392// written. If config is nil, sensible defaults will be used.
386- func writeAndSign (payload io.WriteCloser , candidateHashes [][]uint8 , signEntities []* Entity , hints * FileHints , sigType packet.SignatureType , intendedRecipients []* packet.Recipient , config * packet.Config ) (plaintext io.WriteCloser , err error ) {
393+ func writeAndSign (payload io.WriteCloser , candidateHashes [][]uint8 , signEntities []* Entity , hints * FileHints , sigType packet.SignatureType , intendedRecipients []* packet.Recipient , outsideSig [] byte , config * packet.Config ) (plaintext io.WriteCloser , err error ) {
387394 var signers []* signatureContext
395+ var numberOfOutsideSigs int
396+
397+ if outsideSig != nil {
398+ outSigPacket , err := parseOutsideSig (outsideSig )
399+ if err != nil {
400+ return nil , err
401+ }
402+ opsVersion := 3
403+ if outSigPacket .Version == 6 {
404+ opsVersion = 6
405+ }
406+ opsOutside := & packet.OnePassSignature {
407+ Version : opsVersion ,
408+ SigType : outSigPacket .SigType ,
409+ Hash : outSigPacket .Hash ,
410+ PubKeyAlgo : outSigPacket .PubKeyAlgo ,
411+ KeyId : * outSigPacket .IssuerKeyId ,
412+ IsLast : len (signEntities ) == 0 ,
413+ }
414+ sigContext := signatureContext {
415+ outsideSig : outSigPacket ,
416+ }
417+ if outSigPacket .Version == 6 {
418+ opsOutside .KeyFingerprint = outSigPacket .IssuerFingerprint
419+ sigContext .salt = outSigPacket .Salt ()
420+ opsOutside .Salt = outSigPacket .Salt ()
421+ }
422+ sigContext .h , sigContext .wrappedHash , err = hashForSignature (outSigPacket .Hash , sigType , sigContext .salt )
423+ if err != nil {
424+ return nil , err
425+ }
426+ if err := opsOutside .Serialize (payload ); err != nil {
427+ return nil , err
428+ }
429+ signers = append ([]* signatureContext {& sigContext }, signers ... )
430+ numberOfOutsideSigs = 1
431+ }
432+
388433 for signEntityIdx , signEntity := range signEntities {
389434 if signEntity == nil {
390435 continue
@@ -442,7 +487,7 @@ func writeAndSign(payload io.WriteCloser, candidateHashes [][]uint8, signEntitie
442487 signers = append ([]* signatureContext {& sigContext }, signers ... )
443488 }
444489
445- if signEntities != nil && len (signers ) < 1 {
490+ if signEntities != nil && len (signEntities ) + numberOfOutsideSigs != len ( signers ) {
446491 return nil , errors .InvalidArgumentError ("no valid signing key" )
447492 }
448493
@@ -451,7 +496,7 @@ func writeAndSign(payload io.WriteCloser, candidateHashes [][]uint8, signEntitie
451496 }
452497
453498 w := payload
454- if signers != nil {
499+ if signers != nil || numberOfOutsideSigs > 0 {
455500 // If we need to write a signature packet after the literal
456501 // data then we need to stop literalData from closing
457502 // encryptedData.
@@ -467,7 +512,7 @@ func writeAndSign(payload io.WriteCloser, candidateHashes [][]uint8, signEntitie
467512 return nil , err
468513 }
469514
470- if signers != nil {
515+ if signers != nil || numberOfOutsideSigs > 0 {
471516 metadata := & packet.LiteralData {
472517 Format : 'b' ,
473518 FileName : hints .FileName ,
@@ -640,7 +685,7 @@ func encryptDataAndSign(
640685 if err != nil {
641686 return nil , err
642687 }
643- return writeAndSign (payload , candidateHashes , params .Signers , params .Hints , sigType , intendedRecipients , params .Config )
688+ return writeAndSign (payload , candidateHashes , params .Signers , params .Hints , sigType , intendedRecipients , params .OutsideSig , params . Config )
644689}
645690
646691type SignParams struct {
@@ -649,6 +694,11 @@ type SignParams struct {
649694 Hints * FileHints
650695 // TextSig indicates if signatures of type SigTypeText should be produced
651696 TextSig bool
697+ // OutsideSig allows to set a signature that should be included
698+ // in an inline signed message.
699+ // Should only be used for exceptional cases.
700+ // If nil, ignored.
701+ OutsideSig []byte
652702 // Config provides the config to be used.
653703 // If Config is nil, sensible defaults will be used.
654704 Config * packet.Config
@@ -661,7 +711,7 @@ func SignWithParams(output io.Writer, signers []*Entity, params *SignParams) (in
661711 if params == nil {
662712 params = & SignParams {}
663713 }
664- if len (signers ) < 1 {
714+ if len (signers ) < 1 && params . OutsideSig == nil {
665715 return nil , errors .InvalidArgumentError ("no signer provided" )
666716 }
667717 var candidateHashesPerSignature [][]uint8
@@ -708,7 +758,7 @@ func SignWithParams(output io.Writer, signers []*Entity, params *SignParams) (in
708758 if err != nil {
709759 return nil , err
710760 }
711- return writeAndSign (payload , candidateHashesPerSignature , signers , params .Hints , sigType , nil , params .Config )
761+ return writeAndSign (payload , candidateHashesPerSignature , signers , params .Hints , sigType , nil , params .OutsideSig , params . Config )
712762}
713763
714764// Sign signs a message. The resulting WriteCloser must be closed after the
@@ -742,6 +792,7 @@ type signatureContext struct {
742792 h hash.Hash
743793 salt []byte // v6 only
744794 signer * packet.PrivateKey
795+ outsideSig * packet.Signature
745796}
746797
747798func (s signatureWriter ) Write (data []byte ) (int , error ) {
@@ -764,16 +815,21 @@ func (s signatureWriter) Close() error {
764815 return err
765816 }
766817 for _ , ctx := range s .signatureContexts {
767- sig := createSignaturePacket (& ctx .signer .PublicKey , s .sigType , s .config )
768- sig .Hash = ctx .hashType
769- sig .Metadata = s .metadata
770- sig .IntendedRecipients = s .intendedRecipients
771-
772- if err := sig .SetSalt (ctx .salt ); err != nil {
773- return err
774- }
775- if err := sig .Sign (ctx .h , ctx .signer , s .config ); err != nil {
776- return err
818+ var sig * packet.Signature
819+ if ctx .outsideSig != nil {
820+ // Signature that was supplied outside
821+ sig = ctx .outsideSig
822+ } else {
823+ sig = createSignaturePacket (& ctx .signer .PublicKey , s .sigType , s .config )
824+ sig .Hash = ctx .hashType
825+ sig .Metadata = s .metadata
826+ sig .IntendedRecipients = s .intendedRecipients
827+ if err := sig .SetSalt (ctx .salt ); err != nil {
828+ return err
829+ }
830+ if err := sig .Sign (ctx .h , ctx .signer , s .config ); err != nil {
831+ return err
832+ }
777833 }
778834 if err := sig .Serialize (s .encryptedData ); err != nil {
779835 return err
@@ -872,3 +928,25 @@ func selectHash(candidateHashes []byte, configuredHash crypto.Hash) (hash crypto
872928 }
873929 return
874930}
931+
932+ func parseOutsideSig (outsideSig []byte ) (outSigPacket * packet.Signature , err error ) {
933+ var p packet.Packet
934+ packets := packet .NewReader (bytes .NewReader (outsideSig ))
935+ p , err = packets .Next ()
936+ if goerrors .Is (err , io .EOF ) {
937+ return nil , errors .ErrUnknownIssuer
938+ }
939+ if err != nil {
940+ return nil , err
941+ }
942+
943+ var ok bool
944+ outSigPacket , ok = p .(* packet.Signature )
945+ if ! ok {
946+ return nil , errors .StructuralError ("non signature packet found" )
947+ }
948+ if outSigPacket .IssuerKeyId == nil {
949+ return nil , errors .StructuralError ("signature doesn't have an issuer" )
950+ }
951+ return outSigPacket , nil
952+ }
0 commit comments