|
- // Copyright 2011 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
-
- // Package openpgp implements high level operations on OpenPGP messages.
- package openpgp // import "github.com/keybase/go-crypto/openpgp"
-
- import (
- "crypto"
- "crypto/hmac"
- _ "crypto/sha256"
- "hash"
- "io"
- "strconv"
-
- "github.com/keybase/go-crypto/openpgp/armor"
- "github.com/keybase/go-crypto/openpgp/errors"
- "github.com/keybase/go-crypto/openpgp/packet"
- )
-
- // SignatureType is the armor type for a PGP signature.
- var SignatureType = "PGP SIGNATURE"
-
- // readArmored reads an armored block with the given type.
- func readArmored(r io.Reader, expectedType string) (body io.Reader, err error) {
- block, err := armor.Decode(r)
- if err != nil {
- return
- }
-
- if block.Type != expectedType {
- return nil, errors.InvalidArgumentError("expected '" + expectedType + "', got: " + block.Type)
- }
-
- return block.Body, nil
- }
-
- // MessageDetails contains the result of parsing an OpenPGP encrypted and/or
- // signed message.
- type MessageDetails struct {
- IsEncrypted bool // true if the message was encrypted.
- EncryptedToKeyIds []uint64 // the list of recipient key ids.
- IsSymmetricallyEncrypted bool // true if a passphrase could have decrypted the message.
- DecryptedWith Key // the private key used to decrypt the message, if any.
- IsSigned bool // true if the message is signed.
- SignedByKeyId uint64 // the key id of the signer, if any.
- SignedBy *Key // the key of the signer, if available.
- LiteralData *packet.LiteralData // the metadata of the contents
- UnverifiedBody io.Reader // the contents of the message.
-
- // If IsSigned is true and SignedBy is non-zero then the signature will
- // be verified as UnverifiedBody is read. The signature cannot be
- // checked until the whole of UnverifiedBody is read so UnverifiedBody
- // must be consumed until EOF before the data can trusted. Even if a
- // message isn't signed (or the signer is unknown) the data may contain
- // an authentication code that is only checked once UnverifiedBody has
- // been consumed. Once EOF has been seen, the following fields are
- // valid. (An authentication code failure is reported as a
- // SignatureError error when reading from UnverifiedBody.)
- SignatureError error // nil if the signature is good.
- Signature *packet.Signature // the signature packet itself, if v4 (default)
- SignatureV3 *packet.SignatureV3 // the signature packet if it is a v2 or v3 signature
-
- decrypted io.ReadCloser
- }
-
- // A PromptFunction is used as a callback by functions that may need to decrypt
- // a private key, or prompt for a passphrase. It is called with a list of
- // acceptable, encrypted private keys and a boolean that indicates whether a
- // passphrase is usable. It should either decrypt a private key or return a
- // passphrase to try. If the decrypted private key or given passphrase isn't
- // correct, the function will be called again, forever. Any error returned will
- // be passed up.
- type PromptFunction func(keys []Key, symmetric bool) ([]byte, error)
-
- // A keyEnvelopePair is used to store a private key with the envelope that
- // contains a symmetric key, encrypted with that key.
- type keyEnvelopePair struct {
- key Key
- encryptedKey *packet.EncryptedKey
- }
-
- // ReadMessage parses an OpenPGP message that may be signed and/or encrypted.
- // The given KeyRing should contain both public keys (for signature
- // verification) and, possibly encrypted, private keys for decrypting.
- // If config is nil, sensible defaults will be used.
- func ReadMessage(r io.Reader, keyring KeyRing, prompt PromptFunction, config *packet.Config) (md *MessageDetails, err error) {
- var p packet.Packet
-
- var symKeys []*packet.SymmetricKeyEncrypted
- var pubKeys []keyEnvelopePair
- var se *packet.SymmetricallyEncrypted
-
- packets := packet.NewReader(r)
- md = new(MessageDetails)
- md.IsEncrypted = true
-
- // The message, if encrypted, starts with a number of packets
- // containing an encrypted decryption key. The decryption key is either
- // encrypted to a public key, or with a passphrase. This loop
- // collects these packets.
- ParsePackets:
- for {
- p, err = packets.Next()
- if err != nil {
- return nil, err
- }
- switch p := p.(type) {
- case *packet.SymmetricKeyEncrypted:
- // This packet contains the decryption key encrypted with a passphrase.
- md.IsSymmetricallyEncrypted = true
- symKeys = append(symKeys, p)
- case *packet.EncryptedKey:
- // This packet contains the decryption key encrypted to a public key.
- md.EncryptedToKeyIds = append(md.EncryptedToKeyIds, p.KeyId)
- switch p.Algo {
- case packet.PubKeyAlgoRSA, packet.PubKeyAlgoRSAEncryptOnly, packet.PubKeyAlgoElGamal, packet.PubKeyAlgoECDH:
- break
- default:
- continue
- }
- var keys []Key
- if p.KeyId == 0 {
- keys = keyring.DecryptionKeys()
- } else {
- keys = keyring.KeysById(p.KeyId, nil)
- }
- for _, k := range keys {
- pubKeys = append(pubKeys, keyEnvelopePair{k, p})
- }
- case *packet.SymmetricallyEncrypted:
- se = p
- break ParsePackets
- case *packet.Compressed, *packet.LiteralData, *packet.OnePassSignature:
- // This message isn't encrypted.
- if len(symKeys) != 0 || len(pubKeys) != 0 {
- return nil, errors.StructuralError("key material not followed by encrypted message")
- }
- packets.Unread(p)
- return readSignedMessage(packets, nil, keyring)
- }
- }
-
- var candidates []Key
- var decrypted io.ReadCloser
-
- // Now that we have the list of encrypted keys we need to decrypt at
- // least one of them or, if we cannot, we need to call the prompt
- // function so that it can decrypt a key or give us a passphrase.
- FindKey:
- for {
- // See if any of the keys already have a private key available
- candidates = candidates[:0]
- candidateFingerprints := make(map[string]bool)
-
- for _, pk := range pubKeys {
- if pk.key.PrivateKey == nil {
- continue
- }
- if !pk.key.PrivateKey.Encrypted {
- if len(pk.encryptedKey.Key) == 0 {
- pk.encryptedKey.Decrypt(pk.key.PrivateKey, config)
- }
- if len(pk.encryptedKey.Key) == 0 {
- continue
- }
- decrypted, err = se.Decrypt(pk.encryptedKey.CipherFunc, pk.encryptedKey.Key)
- if err != nil && err != errors.ErrKeyIncorrect {
- return nil, err
- }
- if decrypted != nil {
- md.DecryptedWith = pk.key
- break FindKey
- }
- } else {
- fpr := string(pk.key.PublicKey.Fingerprint[:])
- if v := candidateFingerprints[fpr]; v {
- continue
- }
- candidates = append(candidates, pk.key)
- candidateFingerprints[fpr] = true
- }
- }
-
- if len(candidates) == 0 && len(symKeys) == 0 {
- return nil, errors.ErrKeyIncorrect
- }
-
- if prompt == nil {
- return nil, errors.ErrKeyIncorrect
- }
-
- passphrase, err := prompt(candidates, len(symKeys) != 0)
- if err != nil {
- return nil, err
- }
-
- // Try the symmetric passphrase first
- if len(symKeys) != 0 && passphrase != nil {
- for _, s := range symKeys {
- key, cipherFunc, err := s.Decrypt(passphrase)
- if err == nil {
- decrypted, err = se.Decrypt(cipherFunc, key)
- if err != nil && err != errors.ErrKeyIncorrect {
- return nil, err
- }
- if decrypted != nil {
- break FindKey
- }
- }
-
- }
- }
- }
-
- md.decrypted = decrypted
- if err := packets.Push(decrypted); err != nil {
- return nil, err
- }
- return readSignedMessage(packets, md, keyring)
- }
-
- // readSignedMessage reads a possibly signed message if mdin is non-zero then
- // that structure is updated and returned. Otherwise a fresh MessageDetails is
- // used.
- func readSignedMessage(packets *packet.Reader, mdin *MessageDetails, keyring KeyRing) (md *MessageDetails, err error) {
- if mdin == nil {
- mdin = new(MessageDetails)
- }
- md = mdin
-
- var p packet.Packet
- var h hash.Hash
- var wrappedHash hash.Hash
- FindLiteralData:
- for {
- p, err = packets.Next()
- if err != nil {
- return nil, err
- }
- switch p := p.(type) {
- case *packet.Compressed:
- if err := packets.Push(p.Body); err != nil {
- return nil, err
- }
- case *packet.OnePassSignature:
- if !p.IsLast {
- return nil, errors.UnsupportedError("nested signatures")
- }
-
- h, wrappedHash, err = hashForSignature(p.Hash, p.SigType)
- if err != nil {
- md = nil
- return
- }
-
- md.IsSigned = true
- md.SignedByKeyId = p.KeyId
- keys := keyring.KeysByIdUsage(p.KeyId, nil, packet.KeyFlagSign)
- if len(keys) > 0 {
- md.SignedBy = &keys[0]
- }
- case *packet.LiteralData:
- md.LiteralData = p
- break FindLiteralData
- }
- }
-
- if md.SignedBy != nil {
- md.UnverifiedBody = &signatureCheckReader{packets, h, wrappedHash, md}
- } else if md.decrypted != nil {
- md.UnverifiedBody = checkReader{md}
- } else {
- md.UnverifiedBody = md.LiteralData.Body
- }
-
- return md, nil
- }
-
- // hashForSignature returns a pair of hashes that can be used to verify a
- // signature. The signature may specify that the contents of the signed message
- // should be preprocessed (i.e. to normalize line endings). Thus this function
- // returns two hashes. The second should be used to hash the message itself and
- // performs any needed preprocessing.
- func hashForSignature(hashId crypto.Hash, sigType packet.SignatureType) (hash.Hash, hash.Hash, error) {
- if !hashId.Available() {
- return nil, nil, errors.UnsupportedError("hash not available: " + strconv.Itoa(int(hashId)))
- }
- h := hashId.New()
-
- switch sigType {
- case packet.SigTypeBinary:
- return h, h, nil
- case packet.SigTypeText:
- return h, NewCanonicalTextHash(h), nil
- }
-
- return nil, nil, errors.UnsupportedError("unsupported signature type: " + strconv.Itoa(int(sigType)))
- }
-
- // checkReader wraps an io.Reader from a LiteralData packet. When it sees EOF
- // it closes the ReadCloser from any SymmetricallyEncrypted packet to trigger
- // MDC checks.
- type checkReader struct {
- md *MessageDetails
- }
-
- func (cr checkReader) Read(buf []byte) (n int, err error) {
- n, err = cr.md.LiteralData.Body.Read(buf)
- if err == io.EOF {
- mdcErr := cr.md.decrypted.Close()
- if mdcErr != nil {
- err = mdcErr
- }
- }
- return
- }
-
- // signatureCheckReader wraps an io.Reader from a LiteralData packet and hashes
- // the data as it is read. When it sees an EOF from the underlying io.Reader
- // it parses and checks a trailing Signature packet and triggers any MDC checks.
- type signatureCheckReader struct {
- packets *packet.Reader
- h, wrappedHash hash.Hash
- md *MessageDetails
- }
-
- func (scr *signatureCheckReader) Read(buf []byte) (n int, err error) {
- n, err = scr.md.LiteralData.Body.Read(buf)
- scr.wrappedHash.Write(buf[:n])
- if err == io.EOF {
- var p packet.Packet
- p, scr.md.SignatureError = scr.packets.Next()
- if scr.md.SignatureError != nil {
- return
- }
-
- var ok bool
- if scr.md.Signature, ok = p.(*packet.Signature); ok {
- var err error
- if fingerprint := scr.md.Signature.IssuerFingerprint; fingerprint != nil {
- if !hmac.Equal(fingerprint, scr.md.SignedBy.PublicKey.Fingerprint[:]) {
- err = errors.StructuralError("bad key fingerprint")
- }
- }
- if err == nil {
- err = scr.md.SignedBy.PublicKey.VerifySignature(scr.h, scr.md.Signature)
- }
- scr.md.SignatureError = err
- } else if scr.md.SignatureV3, ok = p.(*packet.SignatureV3); ok {
- scr.md.SignatureError = scr.md.SignedBy.PublicKey.VerifySignatureV3(scr.h, scr.md.SignatureV3)
- } else {
- scr.md.SignatureError = errors.StructuralError("LiteralData not followed by Signature")
- return
- }
-
- // The SymmetricallyEncrypted packet, if any, might have an
- // unsigned hash of its own. In order to check this we need to
- // close that Reader.
- if scr.md.decrypted != nil {
- mdcErr := scr.md.decrypted.Close()
- if mdcErr != nil {
- err = mdcErr
- }
- }
- }
- return
- }
-
- // CheckDetachedSignature takes a signed file and a detached signature and
- // returns the signer if the signature is valid. If the signer isn't known,
- // ErrUnknownIssuer is returned.
- func CheckDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signer *Entity, err error) {
- signer, _, err = checkDetachedSignature(keyring, signed, signature)
- return signer, err
- }
-
- func checkDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signer *Entity, issuer *uint64, err error) {
- var issuerKeyId uint64
- var issuerFingerprint []byte
- var hashFunc crypto.Hash
- var sigType packet.SignatureType
- var keys []Key
- var p packet.Packet
-
- packets := packet.NewReader(signature)
- for {
- p, err = packets.Next()
- if err == io.EOF {
- return nil, nil, errors.ErrUnknownIssuer
- }
- if err != nil {
- return nil, nil, err
- }
-
- switch sig := p.(type) {
- case *packet.Signature:
- if sig.IssuerKeyId == nil {
- return nil, nil, errors.StructuralError("signature doesn't have an issuer")
- }
- issuerKeyId = *sig.IssuerKeyId
- hashFunc = sig.Hash
- sigType = sig.SigType
- issuerFingerprint = sig.IssuerFingerprint
- case *packet.SignatureV3:
- issuerKeyId = sig.IssuerKeyId
- hashFunc = sig.Hash
- sigType = sig.SigType
- default:
- return nil, nil, errors.StructuralError("non signature packet found")
- }
-
- keys = keyring.KeysByIdUsage(issuerKeyId, issuerFingerprint, packet.KeyFlagSign)
- if len(keys) > 0 {
- break
- }
- }
-
- if len(keys) == 0 {
- panic("unreachable")
- }
-
- h, wrappedHash, err := hashForSignature(hashFunc, sigType)
- if err != nil {
- return nil, nil, err
- }
-
- if _, err := io.Copy(wrappedHash, signed); err != nil && err != io.EOF {
- return nil, nil, err
- }
-
- for _, key := range keys {
- switch sig := p.(type) {
- case *packet.Signature:
- err = key.PublicKey.VerifySignature(h, sig)
- case *packet.SignatureV3:
- err = key.PublicKey.VerifySignatureV3(h, sig)
- default:
- panic("unreachable")
- }
-
- if err == nil {
- return key.Entity, &issuerKeyId, nil
- }
- }
-
- return nil, nil, err
- }
-
- // CheckArmoredDetachedSignature performs the same actions as
- // CheckDetachedSignature but expects the signature to be armored.
- func CheckArmoredDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signer *Entity, err error) {
- signer, _, err = checkArmoredDetachedSignature(keyring, signed, signature)
- return signer, err
- }
-
- func checkArmoredDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signer *Entity, issuer *uint64, err error) {
- body, err := readArmored(signature, SignatureType)
- if err != nil {
- return
- }
- return checkDetachedSignature(keyring, signed, body)
- }
|