443 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			443 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
|   | // 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 be 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) | ||
|  | } |