github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/libkb/pgp_dec.go (about) 1 // Copyright 2015 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package libkb 5 6 import ( 7 "bytes" 8 "fmt" 9 "io" 10 11 "time" 12 13 "github.com/keybase/go-crypto/openpgp" 14 "github.com/keybase/go-crypto/openpgp/armor" 15 "github.com/keybase/go-crypto/openpgp/clearsign" 16 "github.com/keybase/go-crypto/openpgp/errors" 17 ) 18 19 type SignatureStatus struct { 20 IsSigned bool 21 Verified bool 22 SignatureError error 23 KeyID uint64 24 Entity *openpgp.Entity 25 SignatureTime time.Time 26 RecipientKeyIDs []uint64 27 Warnings HashSecurityWarnings 28 } 29 30 func PGPDecryptWithBundles(g *GlobalContext, source io.Reader, sink io.Writer, keys []*PGPKeyBundle) (*SignatureStatus, error) { 31 opkr := make(openpgp.EntityList, len(keys)) 32 for i, k := range keys { 33 opkr[i] = k.Entity 34 } 35 return PGPDecrypt(g, source, sink, opkr) 36 } 37 38 // PGPDecrypt only generates warnings about insecure _message_ signatures, not 39 // _key_ signatures - that is handled by engine.PGPDecrypt. 40 func PGPDecrypt(g *GlobalContext, source io.Reader, sink io.Writer, kr openpgp.KeyRing) (*SignatureStatus, error) { 41 42 var sc StreamClassification 43 var err error 44 45 sc, source, err = ClassifyStream(source) 46 if err != nil { 47 return nil, err 48 } 49 50 if sc.Format != CryptoMessageFormatPGP { 51 return nil, WrongCryptoFormatError{ 52 Wanted: CryptoMessageFormatPGP, 53 Received: sc.Format, 54 Operation: "decrypt", 55 } 56 } 57 58 if sc.Type == CryptoMessageTypeClearSignature { 59 return pgpDecryptClearsign(g, source, sink, kr) 60 } 61 62 if sc.Armored { 63 b, err := armor.Decode(source) 64 if err != nil { 65 return nil, err 66 } 67 source = b.Body 68 } 69 70 g.Log.Debug("Calling into openpgp ReadMessage for decryption") 71 md, err := openpgp.ReadMessage(source, kr, nil, nil) 72 if err != nil { 73 if err == errors.ErrKeyIncorrect { 74 return nil, NoDecryptionKeyError{Msg: "unable to find a PGP decryption key for this message"} 75 } 76 return nil, err 77 } 78 79 if md.IsSigned { 80 g.Log.Debug("message is signed (SignedByKeyId: %+v) (have key? %v)", md.SignedByKeyId, md.SignedBy != nil) 81 } 82 83 n, err := io.Copy(sink, md.UnverifiedBody) 84 if err != nil { 85 return nil, err 86 } 87 g.Log.Debug("PGPDecrypt: copied %d bytes to writer", n) 88 89 var status SignatureStatus 90 if md.IsSigned { 91 status.IsSigned = true 92 status.KeyID = md.SignedByKeyId 93 if md.Signature != nil { 94 status.SignatureTime = md.Signature.CreationTime 95 96 if !IsHashSecure(md.Signature.Hash) { 97 status.Warnings = append( 98 status.Warnings, 99 NewHashSecurityWarning( 100 HashSecurityWarningSignatureHash, 101 md.Signature.Hash, 102 nil, 103 ), 104 ) 105 } 106 } 107 if md.SignedBy != nil { 108 status.Entity = md.SignedBy.Entity 109 } 110 if md.SignatureError != nil { 111 status.SignatureError = md.SignatureError 112 } else { 113 status.Verified = true 114 } 115 } 116 117 status.RecipientKeyIDs = md.EncryptedToKeyIds 118 119 return &status, nil 120 } 121 122 func pgpDecryptClearsign(g *GlobalContext, source io.Reader, sink io.Writer, kr openpgp.KeyRing) (*SignatureStatus, error) { 123 // clearsign decode only works with the whole data slice, not a reader 124 // so have to read it all here: 125 msg, err := io.ReadAll(source) 126 if err != nil { 127 return nil, err 128 } 129 b, _ := clearsign.Decode(msg) 130 if b == nil { 131 return nil, fmt.Errorf("Unable to decode clearsigned message") 132 } 133 134 sigBytes, err := io.ReadAll(b.ArmoredSignature.Body) 135 if err != nil { 136 return nil, err 137 } 138 139 signer, err := openpgp.CheckDetachedSignature(kr, bytes.NewReader(b.Bytes), bytes.NewReader(sigBytes)) 140 if err != nil { 141 return nil, fmt.Errorf("Check sig error: %s", err) 142 } 143 144 n, err := io.Copy(sink, bytes.NewReader(b.Plaintext)) 145 if err != nil { 146 return nil, err 147 } 148 g.Log.Debug("PGPDecrypt: copied %d bytes to writer", n) 149 150 var status SignatureStatus 151 if signer == nil { 152 return &status, nil 153 } 154 155 // Reexamine the signature to figure out its hash 156 digestHash, signerKeyID, err := ExtractPGPSignatureHashMethod(kr, sigBytes) 157 if err != nil { 158 return nil, err 159 } 160 if !IsHashSecure(digestHash) { 161 status.Warnings = append( 162 status.Warnings, 163 NewHashSecurityWarning( 164 HashSecurityWarningSignatureHash, 165 digestHash, 166 nil, 167 ), 168 ) 169 } 170 171 status.IsSigned = true 172 status.Verified = true 173 status.Entity = signer 174 status.KeyID = signerKeyID 175 176 return &status, nil 177 }