| // 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 "golang.org/x/crypto/openpgp" |
| |
| import ( |
| "crypto" |
| _ "crypto/sha256" |
| "hash" |
| "io" |
| "strconv" |
| |
| "golang.org/x/crypto/openpgp/armor" |
| "golang.org/x/crypto/openpgp/errors" |
| "golang.org/x/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: |
| break |
| default: |
| continue |
| } |
| var keys []Key |
| if p.KeyId == 0 { |
| keys = keyring.DecryptionKeys() |
| } else { |
| keys = keyring.KeysById(p.KeyId) |
| } |
| 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, 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 { |
| scr.md.SignatureError = scr.md.SignedBy.PublicKey.VerifySignature(scr.h, scr.md.Signature) |
| } 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) { |
| var issuerKeyId uint64 |
| 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, errors.ErrUnknownIssuer |
| } |
| if err != nil { |
| return nil, err |
| } |
| |
| switch sig := p.(type) { |
| case *packet.Signature: |
| if sig.IssuerKeyId == nil { |
| return nil, errors.StructuralError("signature doesn't have an issuer") |
| } |
| issuerKeyId = *sig.IssuerKeyId |
| hashFunc = sig.Hash |
| sigType = sig.SigType |
| case *packet.SignatureV3: |
| issuerKeyId = sig.IssuerKeyId |
| hashFunc = sig.Hash |
| sigType = sig.SigType |
| default: |
| return nil, errors.StructuralError("non signature packet found") |
| } |
| |
| keys = keyring.KeysByIdUsage(issuerKeyId, packet.KeyFlagSign) |
| if len(keys) > 0 { |
| break |
| } |
| } |
| |
| if len(keys) == 0 { |
| panic("unreachable") |
| } |
| |
| h, wrappedHash, err := hashForSignature(hashFunc, sigType) |
| if err != nil { |
| return nil, err |
| } |
| |
| if _, err := io.Copy(wrappedHash, signed); err != nil && err != io.EOF { |
| return 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, nil |
| } |
| } |
| |
| return 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) { |
| body, err := readArmored(signature, SignatureType) |
| if err != nil { |
| return |
| } |
| |
| return CheckDetachedSignature(keyring, signed, body) |
| } |