732 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			732 lines
		
	
	
		
			21 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 packet | ||
|  | 
 | ||
|  | import ( | ||
|  | 	"bytes" | ||
|  | 	"crypto" | ||
|  | 	"crypto/dsa" | ||
|  | 	"crypto/ecdsa" | ||
|  | 	"encoding/asn1" | ||
|  | 	"encoding/binary" | ||
|  | 	"hash" | ||
|  | 	"io" | ||
|  | 	"math/big" | ||
|  | 	"strconv" | ||
|  | 	"time" | ||
|  | 
 | ||
|  | 	"golang.org/x/crypto/openpgp/errors" | ||
|  | 	"golang.org/x/crypto/openpgp/s2k" | ||
|  | ) | ||
|  | 
 | ||
|  | const ( | ||
|  | 	// See RFC 4880, section 5.2.3.21 for details. | ||
|  | 	KeyFlagCertify = 1 << iota | ||
|  | 	KeyFlagSign | ||
|  | 	KeyFlagEncryptCommunications | ||
|  | 	KeyFlagEncryptStorage | ||
|  | ) | ||
|  | 
 | ||
|  | // Signature represents a signature. See RFC 4880, section 5.2. | ||
|  | type Signature struct { | ||
|  | 	SigType    SignatureType | ||
|  | 	PubKeyAlgo PublicKeyAlgorithm | ||
|  | 	Hash       crypto.Hash | ||
|  | 
 | ||
|  | 	// HashSuffix is extra data that is hashed in after the signed data. | ||
|  | 	HashSuffix []byte | ||
|  | 	// HashTag contains the first two bytes of the hash for fast rejection | ||
|  | 	// of bad signed data. | ||
|  | 	HashTag      [2]byte | ||
|  | 	CreationTime time.Time | ||
|  | 
 | ||
|  | 	RSASignature         parsedMPI | ||
|  | 	DSASigR, DSASigS     parsedMPI | ||
|  | 	ECDSASigR, ECDSASigS parsedMPI | ||
|  | 
 | ||
|  | 	// rawSubpackets contains the unparsed subpackets, in order. | ||
|  | 	rawSubpackets []outputSubpacket | ||
|  | 
 | ||
|  | 	// The following are optional so are nil when not included in the | ||
|  | 	// signature. | ||
|  | 
 | ||
|  | 	SigLifetimeSecs, KeyLifetimeSecs                        *uint32 | ||
|  | 	PreferredSymmetric, PreferredHash, PreferredCompression []uint8 | ||
|  | 	IssuerKeyId                                             *uint64 | ||
|  | 	IsPrimaryId                                             *bool | ||
|  | 
 | ||
|  | 	// FlagsValid is set if any flags were given. See RFC 4880, section | ||
|  | 	// 5.2.3.21 for details. | ||
|  | 	FlagsValid                                                           bool | ||
|  | 	FlagCertify, FlagSign, FlagEncryptCommunications, FlagEncryptStorage bool | ||
|  | 
 | ||
|  | 	// RevocationReason is set if this signature has been revoked. | ||
|  | 	// See RFC 4880, section 5.2.3.23 for details. | ||
|  | 	RevocationReason     *uint8 | ||
|  | 	RevocationReasonText string | ||
|  | 
 | ||
|  | 	// MDC is set if this signature has a feature packet that indicates | ||
|  | 	// support for MDC subpackets. | ||
|  | 	MDC bool | ||
|  | 
 | ||
|  | 	// EmbeddedSignature, if non-nil, is a signature of the parent key, by | ||
|  | 	// this key. This prevents an attacker from claiming another's signing | ||
|  | 	// subkey as their own. | ||
|  | 	EmbeddedSignature *Signature | ||
|  | 
 | ||
|  | 	outSubpackets []outputSubpacket | ||
|  | } | ||
|  | 
 | ||
|  | func (sig *Signature) parse(r io.Reader) (err error) { | ||
|  | 	// RFC 4880, section 5.2.3 | ||
|  | 	var buf [5]byte | ||
|  | 	_, err = readFull(r, buf[:1]) | ||
|  | 	if err != nil { | ||
|  | 		return | ||
|  | 	} | ||
|  | 	if buf[0] != 4 { | ||
|  | 		err = errors.UnsupportedError("signature packet version " + strconv.Itoa(int(buf[0]))) | ||
|  | 		return | ||
|  | 	} | ||
|  | 
 | ||
|  | 	_, err = readFull(r, buf[:5]) | ||
|  | 	if err != nil { | ||
|  | 		return | ||
|  | 	} | ||
|  | 	sig.SigType = SignatureType(buf[0]) | ||
|  | 	sig.PubKeyAlgo = PublicKeyAlgorithm(buf[1]) | ||
|  | 	switch sig.PubKeyAlgo { | ||
|  | 	case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA: | ||
|  | 	default: | ||
|  | 		err = errors.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo))) | ||
|  | 		return | ||
|  | 	} | ||
|  | 
 | ||
|  | 	var ok bool | ||
|  | 	sig.Hash, ok = s2k.HashIdToHash(buf[2]) | ||
|  | 	if !ok { | ||
|  | 		return errors.UnsupportedError("hash function " + strconv.Itoa(int(buf[2]))) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	hashedSubpacketsLength := int(buf[3])<<8 | int(buf[4]) | ||
|  | 	l := 6 + hashedSubpacketsLength | ||
|  | 	sig.HashSuffix = make([]byte, l+6) | ||
|  | 	sig.HashSuffix[0] = 4 | ||
|  | 	copy(sig.HashSuffix[1:], buf[:5]) | ||
|  | 	hashedSubpackets := sig.HashSuffix[6:l] | ||
|  | 	_, err = readFull(r, hashedSubpackets) | ||
|  | 	if err != nil { | ||
|  | 		return | ||
|  | 	} | ||
|  | 	// See RFC 4880, section 5.2.4 | ||
|  | 	trailer := sig.HashSuffix[l:] | ||
|  | 	trailer[0] = 4 | ||
|  | 	trailer[1] = 0xff | ||
|  | 	trailer[2] = uint8(l >> 24) | ||
|  | 	trailer[3] = uint8(l >> 16) | ||
|  | 	trailer[4] = uint8(l >> 8) | ||
|  | 	trailer[5] = uint8(l) | ||
|  | 
 | ||
|  | 	err = parseSignatureSubpackets(sig, hashedSubpackets, true) | ||
|  | 	if err != nil { | ||
|  | 		return | ||
|  | 	} | ||
|  | 
 | ||
|  | 	_, err = readFull(r, buf[:2]) | ||
|  | 	if err != nil { | ||
|  | 		return | ||
|  | 	} | ||
|  | 	unhashedSubpacketsLength := int(buf[0])<<8 | int(buf[1]) | ||
|  | 	unhashedSubpackets := make([]byte, unhashedSubpacketsLength) | ||
|  | 	_, err = readFull(r, unhashedSubpackets) | ||
|  | 	if err != nil { | ||
|  | 		return | ||
|  | 	} | ||
|  | 	err = parseSignatureSubpackets(sig, unhashedSubpackets, false) | ||
|  | 	if err != nil { | ||
|  | 		return | ||
|  | 	} | ||
|  | 
 | ||
|  | 	_, err = readFull(r, sig.HashTag[:2]) | ||
|  | 	if err != nil { | ||
|  | 		return | ||
|  | 	} | ||
|  | 
 | ||
|  | 	switch sig.PubKeyAlgo { | ||
|  | 	case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: | ||
|  | 		sig.RSASignature.bytes, sig.RSASignature.bitLength, err = readMPI(r) | ||
|  | 	case PubKeyAlgoDSA: | ||
|  | 		sig.DSASigR.bytes, sig.DSASigR.bitLength, err = readMPI(r) | ||
|  | 		if err == nil { | ||
|  | 			sig.DSASigS.bytes, sig.DSASigS.bitLength, err = readMPI(r) | ||
|  | 		} | ||
|  | 	case PubKeyAlgoECDSA: | ||
|  | 		sig.ECDSASigR.bytes, sig.ECDSASigR.bitLength, err = readMPI(r) | ||
|  | 		if err == nil { | ||
|  | 			sig.ECDSASigS.bytes, sig.ECDSASigS.bitLength, err = readMPI(r) | ||
|  | 		} | ||
|  | 	default: | ||
|  | 		panic("unreachable") | ||
|  | 	} | ||
|  | 	return | ||
|  | } | ||
|  | 
 | ||
|  | // parseSignatureSubpackets parses subpackets of the main signature packet. See | ||
|  | // RFC 4880, section 5.2.3.1. | ||
|  | func parseSignatureSubpackets(sig *Signature, subpackets []byte, isHashed bool) (err error) { | ||
|  | 	for len(subpackets) > 0 { | ||
|  | 		subpackets, err = parseSignatureSubpacket(sig, subpackets, isHashed) | ||
|  | 		if err != nil { | ||
|  | 			return | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if sig.CreationTime.IsZero() { | ||
|  | 		err = errors.StructuralError("no creation time in signature") | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return | ||
|  | } | ||
|  | 
 | ||
|  | type signatureSubpacketType uint8 | ||
|  | 
 | ||
|  | const ( | ||
|  | 	creationTimeSubpacket        signatureSubpacketType = 2 | ||
|  | 	signatureExpirationSubpacket signatureSubpacketType = 3 | ||
|  | 	keyExpirationSubpacket       signatureSubpacketType = 9 | ||
|  | 	prefSymmetricAlgosSubpacket  signatureSubpacketType = 11 | ||
|  | 	issuerSubpacket              signatureSubpacketType = 16 | ||
|  | 	prefHashAlgosSubpacket       signatureSubpacketType = 21 | ||
|  | 	prefCompressionSubpacket     signatureSubpacketType = 22 | ||
|  | 	primaryUserIdSubpacket       signatureSubpacketType = 25 | ||
|  | 	keyFlagsSubpacket            signatureSubpacketType = 27 | ||
|  | 	reasonForRevocationSubpacket signatureSubpacketType = 29 | ||
|  | 	featuresSubpacket            signatureSubpacketType = 30 | ||
|  | 	embeddedSignatureSubpacket   signatureSubpacketType = 32 | ||
|  | ) | ||
|  | 
 | ||
|  | // parseSignatureSubpacket parses a single subpacket. len(subpacket) is >= 1. | ||
|  | func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (rest []byte, err error) { | ||
|  | 	// RFC 4880, section 5.2.3.1 | ||
|  | 	var ( | ||
|  | 		length     uint32 | ||
|  | 		packetType signatureSubpacketType | ||
|  | 		isCritical bool | ||
|  | 	) | ||
|  | 	switch { | ||
|  | 	case subpacket[0] < 192: | ||
|  | 		length = uint32(subpacket[0]) | ||
|  | 		subpacket = subpacket[1:] | ||
|  | 	case subpacket[0] < 255: | ||
|  | 		if len(subpacket) < 2 { | ||
|  | 			goto Truncated | ||
|  | 		} | ||
|  | 		length = uint32(subpacket[0]-192)<<8 + uint32(subpacket[1]) + 192 | ||
|  | 		subpacket = subpacket[2:] | ||
|  | 	default: | ||
|  | 		if len(subpacket) < 5 { | ||
|  | 			goto Truncated | ||
|  | 		} | ||
|  | 		length = uint32(subpacket[1])<<24 | | ||
|  | 			uint32(subpacket[2])<<16 | | ||
|  | 			uint32(subpacket[3])<<8 | | ||
|  | 			uint32(subpacket[4]) | ||
|  | 		subpacket = subpacket[5:] | ||
|  | 	} | ||
|  | 	if length > uint32(len(subpacket)) { | ||
|  | 		goto Truncated | ||
|  | 	} | ||
|  | 	rest = subpacket[length:] | ||
|  | 	subpacket = subpacket[:length] | ||
|  | 	if len(subpacket) == 0 { | ||
|  | 		err = errors.StructuralError("zero length signature subpacket") | ||
|  | 		return | ||
|  | 	} | ||
|  | 	packetType = signatureSubpacketType(subpacket[0] & 0x7f) | ||
|  | 	isCritical = subpacket[0]&0x80 == 0x80 | ||
|  | 	subpacket = subpacket[1:] | ||
|  | 	sig.rawSubpackets = append(sig.rawSubpackets, outputSubpacket{isHashed, packetType, isCritical, subpacket}) | ||
|  | 	switch packetType { | ||
|  | 	case creationTimeSubpacket: | ||
|  | 		if !isHashed { | ||
|  | 			err = errors.StructuralError("signature creation time in non-hashed area") | ||
|  | 			return | ||
|  | 		} | ||
|  | 		if len(subpacket) != 4 { | ||
|  | 			err = errors.StructuralError("signature creation time not four bytes") | ||
|  | 			return | ||
|  | 		} | ||
|  | 		t := binary.BigEndian.Uint32(subpacket) | ||
|  | 		sig.CreationTime = time.Unix(int64(t), 0) | ||
|  | 	case signatureExpirationSubpacket: | ||
|  | 		// Signature expiration time, section 5.2.3.10 | ||
|  | 		if !isHashed { | ||
|  | 			return | ||
|  | 		} | ||
|  | 		if len(subpacket) != 4 { | ||
|  | 			err = errors.StructuralError("expiration subpacket with bad length") | ||
|  | 			return | ||
|  | 		} | ||
|  | 		sig.SigLifetimeSecs = new(uint32) | ||
|  | 		*sig.SigLifetimeSecs = binary.BigEndian.Uint32(subpacket) | ||
|  | 	case keyExpirationSubpacket: | ||
|  | 		// Key expiration time, section 5.2.3.6 | ||
|  | 		if !isHashed { | ||
|  | 			return | ||
|  | 		} | ||
|  | 		if len(subpacket) != 4 { | ||
|  | 			err = errors.StructuralError("key expiration subpacket with bad length") | ||
|  | 			return | ||
|  | 		} | ||
|  | 		sig.KeyLifetimeSecs = new(uint32) | ||
|  | 		*sig.KeyLifetimeSecs = binary.BigEndian.Uint32(subpacket) | ||
|  | 	case prefSymmetricAlgosSubpacket: | ||
|  | 		// Preferred symmetric algorithms, section 5.2.3.7 | ||
|  | 		if !isHashed { | ||
|  | 			return | ||
|  | 		} | ||
|  | 		sig.PreferredSymmetric = make([]byte, len(subpacket)) | ||
|  | 		copy(sig.PreferredSymmetric, subpacket) | ||
|  | 	case issuerSubpacket: | ||
|  | 		// Issuer, section 5.2.3.5 | ||
|  | 		if len(subpacket) != 8 { | ||
|  | 			err = errors.StructuralError("issuer subpacket with bad length") | ||
|  | 			return | ||
|  | 		} | ||
|  | 		sig.IssuerKeyId = new(uint64) | ||
|  | 		*sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket) | ||
|  | 	case prefHashAlgosSubpacket: | ||
|  | 		// Preferred hash algorithms, section 5.2.3.8 | ||
|  | 		if !isHashed { | ||
|  | 			return | ||
|  | 		} | ||
|  | 		sig.PreferredHash = make([]byte, len(subpacket)) | ||
|  | 		copy(sig.PreferredHash, subpacket) | ||
|  | 	case prefCompressionSubpacket: | ||
|  | 		// Preferred compression algorithms, section 5.2.3.9 | ||
|  | 		if !isHashed { | ||
|  | 			return | ||
|  | 		} | ||
|  | 		sig.PreferredCompression = make([]byte, len(subpacket)) | ||
|  | 		copy(sig.PreferredCompression, subpacket) | ||
|  | 	case primaryUserIdSubpacket: | ||
|  | 		// Primary User ID, section 5.2.3.19 | ||
|  | 		if !isHashed { | ||
|  | 			return | ||
|  | 		} | ||
|  | 		if len(subpacket) != 1 { | ||
|  | 			err = errors.StructuralError("primary user id subpacket with bad length") | ||
|  | 			return | ||
|  | 		} | ||
|  | 		sig.IsPrimaryId = new(bool) | ||
|  | 		if subpacket[0] > 0 { | ||
|  | 			*sig.IsPrimaryId = true | ||
|  | 		} | ||
|  | 	case keyFlagsSubpacket: | ||
|  | 		// Key flags, section 5.2.3.21 | ||
|  | 		if !isHashed { | ||
|  | 			return | ||
|  | 		} | ||
|  | 		if len(subpacket) == 0 { | ||
|  | 			err = errors.StructuralError("empty key flags subpacket") | ||
|  | 			return | ||
|  | 		} | ||
|  | 		sig.FlagsValid = true | ||
|  | 		if subpacket[0]&KeyFlagCertify != 0 { | ||
|  | 			sig.FlagCertify = true | ||
|  | 		} | ||
|  | 		if subpacket[0]&KeyFlagSign != 0 { | ||
|  | 			sig.FlagSign = true | ||
|  | 		} | ||
|  | 		if subpacket[0]&KeyFlagEncryptCommunications != 0 { | ||
|  | 			sig.FlagEncryptCommunications = true | ||
|  | 		} | ||
|  | 		if subpacket[0]&KeyFlagEncryptStorage != 0 { | ||
|  | 			sig.FlagEncryptStorage = true | ||
|  | 		} | ||
|  | 	case reasonForRevocationSubpacket: | ||
|  | 		// Reason For Revocation, section 5.2.3.23 | ||
|  | 		if !isHashed { | ||
|  | 			return | ||
|  | 		} | ||
|  | 		if len(subpacket) == 0 { | ||
|  | 			err = errors.StructuralError("empty revocation reason subpacket") | ||
|  | 			return | ||
|  | 		} | ||
|  | 		sig.RevocationReason = new(uint8) | ||
|  | 		*sig.RevocationReason = subpacket[0] | ||
|  | 		sig.RevocationReasonText = string(subpacket[1:]) | ||
|  | 	case featuresSubpacket: | ||
|  | 		// Features subpacket, section 5.2.3.24 specifies a very general | ||
|  | 		// mechanism for OpenPGP implementations to signal support for new | ||
|  | 		// features. In practice, the subpacket is used exclusively to | ||
|  | 		// indicate support for MDC-protected encryption. | ||
|  | 		sig.MDC = len(subpacket) >= 1 && subpacket[0]&1 == 1 | ||
|  | 	case embeddedSignatureSubpacket: | ||
|  | 		// Only usage is in signatures that cross-certify | ||
|  | 		// signing subkeys. section 5.2.3.26 describes the | ||
|  | 		// format, with its usage described in section 11.1 | ||
|  | 		if sig.EmbeddedSignature != nil { | ||
|  | 			err = errors.StructuralError("Cannot have multiple embedded signatures") | ||
|  | 			return | ||
|  | 		} | ||
|  | 		sig.EmbeddedSignature = new(Signature) | ||
|  | 		// Embedded signatures are required to be v4 signatures see | ||
|  | 		// section 12.1. However, we only parse v4 signatures in this | ||
|  | 		// file anyway. | ||
|  | 		if err := sig.EmbeddedSignature.parse(bytes.NewBuffer(subpacket)); err != nil { | ||
|  | 			return nil, err | ||
|  | 		} | ||
|  | 		if sigType := sig.EmbeddedSignature.SigType; sigType != SigTypePrimaryKeyBinding { | ||
|  | 			return nil, errors.StructuralError("cross-signature has unexpected type " + strconv.Itoa(int(sigType))) | ||
|  | 		} | ||
|  | 	default: | ||
|  | 		if isCritical { | ||
|  | 			err = errors.UnsupportedError("unknown critical signature subpacket type " + strconv.Itoa(int(packetType))) | ||
|  | 			return | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return | ||
|  | 
 | ||
|  | Truncated: | ||
|  | 	err = errors.StructuralError("signature subpacket truncated") | ||
|  | 	return | ||
|  | } | ||
|  | 
 | ||
|  | // subpacketLengthLength returns the length, in bytes, of an encoded length value. | ||
|  | func subpacketLengthLength(length int) int { | ||
|  | 	if length < 192 { | ||
|  | 		return 1 | ||
|  | 	} | ||
|  | 	if length < 16320 { | ||
|  | 		return 2 | ||
|  | 	} | ||
|  | 	return 5 | ||
|  | } | ||
|  | 
 | ||
|  | // serializeSubpacketLength marshals the given length into to. | ||
|  | func serializeSubpacketLength(to []byte, length int) int { | ||
|  | 	// RFC 4880, Section 4.2.2. | ||
|  | 	if length < 192 { | ||
|  | 		to[0] = byte(length) | ||
|  | 		return 1 | ||
|  | 	} | ||
|  | 	if length < 16320 { | ||
|  | 		length -= 192 | ||
|  | 		to[0] = byte((length >> 8) + 192) | ||
|  | 		to[1] = byte(length) | ||
|  | 		return 2 | ||
|  | 	} | ||
|  | 	to[0] = 255 | ||
|  | 	to[1] = byte(length >> 24) | ||
|  | 	to[2] = byte(length >> 16) | ||
|  | 	to[3] = byte(length >> 8) | ||
|  | 	to[4] = byte(length) | ||
|  | 	return 5 | ||
|  | } | ||
|  | 
 | ||
|  | // subpacketsLength returns the serialized length, in bytes, of the given | ||
|  | // subpackets. | ||
|  | func subpacketsLength(subpackets []outputSubpacket, hashed bool) (length int) { | ||
|  | 	for _, subpacket := range subpackets { | ||
|  | 		if subpacket.hashed == hashed { | ||
|  | 			length += subpacketLengthLength(len(subpacket.contents) + 1) | ||
|  | 			length += 1 // type byte | ||
|  | 			length += len(subpacket.contents) | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return | ||
|  | } | ||
|  | 
 | ||
|  | // serializeSubpackets marshals the given subpackets into to. | ||
|  | func serializeSubpackets(to []byte, subpackets []outputSubpacket, hashed bool) { | ||
|  | 	for _, subpacket := range subpackets { | ||
|  | 		if subpacket.hashed == hashed { | ||
|  | 			n := serializeSubpacketLength(to, len(subpacket.contents)+1) | ||
|  | 			to[n] = byte(subpacket.subpacketType) | ||
|  | 			to = to[1+n:] | ||
|  | 			n = copy(to, subpacket.contents) | ||
|  | 			to = to[n:] | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return | ||
|  | } | ||
|  | 
 | ||
|  | // KeyExpired returns whether sig is a self-signature of a key that has | ||
|  | // expired. | ||
|  | func (sig *Signature) KeyExpired(currentTime time.Time) bool { | ||
|  | 	if sig.KeyLifetimeSecs == nil { | ||
|  | 		return false | ||
|  | 	} | ||
|  | 	expiry := sig.CreationTime.Add(time.Duration(*sig.KeyLifetimeSecs) * time.Second) | ||
|  | 	return currentTime.After(expiry) | ||
|  | } | ||
|  | 
 | ||
|  | // buildHashSuffix constructs the HashSuffix member of sig in preparation for signing. | ||
|  | func (sig *Signature) buildHashSuffix() (err error) { | ||
|  | 	hashedSubpacketsLen := subpacketsLength(sig.outSubpackets, true) | ||
|  | 
 | ||
|  | 	var ok bool | ||
|  | 	l := 6 + hashedSubpacketsLen | ||
|  | 	sig.HashSuffix = make([]byte, l+6) | ||
|  | 	sig.HashSuffix[0] = 4 | ||
|  | 	sig.HashSuffix[1] = uint8(sig.SigType) | ||
|  | 	sig.HashSuffix[2] = uint8(sig.PubKeyAlgo) | ||
|  | 	sig.HashSuffix[3], ok = s2k.HashToHashId(sig.Hash) | ||
|  | 	if !ok { | ||
|  | 		sig.HashSuffix = nil | ||
|  | 		return errors.InvalidArgumentError("hash cannot be represented in OpenPGP: " + strconv.Itoa(int(sig.Hash))) | ||
|  | 	} | ||
|  | 	sig.HashSuffix[4] = byte(hashedSubpacketsLen >> 8) | ||
|  | 	sig.HashSuffix[5] = byte(hashedSubpacketsLen) | ||
|  | 	serializeSubpackets(sig.HashSuffix[6:l], sig.outSubpackets, true) | ||
|  | 	trailer := sig.HashSuffix[l:] | ||
|  | 	trailer[0] = 4 | ||
|  | 	trailer[1] = 0xff | ||
|  | 	trailer[2] = byte(l >> 24) | ||
|  | 	trailer[3] = byte(l >> 16) | ||
|  | 	trailer[4] = byte(l >> 8) | ||
|  | 	trailer[5] = byte(l) | ||
|  | 	return | ||
|  | } | ||
|  | 
 | ||
|  | func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err error) { | ||
|  | 	err = sig.buildHashSuffix() | ||
|  | 	if err != nil { | ||
|  | 		return | ||
|  | 	} | ||
|  | 
 | ||
|  | 	h.Write(sig.HashSuffix) | ||
|  | 	digest = h.Sum(nil) | ||
|  | 	copy(sig.HashTag[:], digest) | ||
|  | 	return | ||
|  | } | ||
|  | 
 | ||
|  | // Sign signs a message with a private key. The hash, h, must contain | ||
|  | // the hash of the message to be signed and will be mutated by this function. | ||
|  | // On success, the signature is stored in sig. Call Serialize to write it out. | ||
|  | // If config is nil, sensible defaults will be used. | ||
|  | func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err error) { | ||
|  | 	sig.outSubpackets = sig.buildSubpackets() | ||
|  | 	digest, err := sig.signPrepareHash(h) | ||
|  | 	if err != nil { | ||
|  | 		return | ||
|  | 	} | ||
|  | 
 | ||
|  | 	switch priv.PubKeyAlgo { | ||
|  | 	case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: | ||
|  | 		// supports both *rsa.PrivateKey and crypto.Signer | ||
|  | 		sig.RSASignature.bytes, err = priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, sig.Hash) | ||
|  | 		sig.RSASignature.bitLength = uint16(8 * len(sig.RSASignature.bytes)) | ||
|  | 	case PubKeyAlgoDSA: | ||
|  | 		dsaPriv := priv.PrivateKey.(*dsa.PrivateKey) | ||
|  | 
 | ||
|  | 		// Need to truncate hashBytes to match FIPS 186-3 section 4.6. | ||
|  | 		subgroupSize := (dsaPriv.Q.BitLen() + 7) / 8 | ||
|  | 		if len(digest) > subgroupSize { | ||
|  | 			digest = digest[:subgroupSize] | ||
|  | 		} | ||
|  | 		r, s, err := dsa.Sign(config.Random(), dsaPriv, digest) | ||
|  | 		if err == nil { | ||
|  | 			sig.DSASigR.bytes = r.Bytes() | ||
|  | 			sig.DSASigR.bitLength = uint16(8 * len(sig.DSASigR.bytes)) | ||
|  | 			sig.DSASigS.bytes = s.Bytes() | ||
|  | 			sig.DSASigS.bitLength = uint16(8 * len(sig.DSASigS.bytes)) | ||
|  | 		} | ||
|  | 	case PubKeyAlgoECDSA: | ||
|  | 		var r, s *big.Int | ||
|  | 		if pk, ok := priv.PrivateKey.(*ecdsa.PrivateKey); ok { | ||
|  | 			// direct support, avoid asn1 wrapping/unwrapping | ||
|  | 			r, s, err = ecdsa.Sign(config.Random(), pk, digest) | ||
|  | 		} else { | ||
|  | 			var b []byte | ||
|  | 			b, err = priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, nil) | ||
|  | 			if err == nil { | ||
|  | 				r, s, err = unwrapECDSASig(b) | ||
|  | 			} | ||
|  | 		} | ||
|  | 		if err == nil { | ||
|  | 			sig.ECDSASigR = fromBig(r) | ||
|  | 			sig.ECDSASigS = fromBig(s) | ||
|  | 		} | ||
|  | 	default: | ||
|  | 		err = errors.UnsupportedError("public key algorithm: " + strconv.Itoa(int(sig.PubKeyAlgo))) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return | ||
|  | } | ||
|  | 
 | ||
|  | // unwrapECDSASig parses the two integer components of an ASN.1-encoded ECDSA | ||
|  | // signature. | ||
|  | func unwrapECDSASig(b []byte) (r, s *big.Int, err error) { | ||
|  | 	var ecsdaSig struct { | ||
|  | 		R, S *big.Int | ||
|  | 	} | ||
|  | 	_, err = asn1.Unmarshal(b, &ecsdaSig) | ||
|  | 	if err != nil { | ||
|  | 		return | ||
|  | 	} | ||
|  | 	return ecsdaSig.R, ecsdaSig.S, nil | ||
|  | } | ||
|  | 
 | ||
|  | // SignUserId computes a signature from priv, asserting that pub is a valid | ||
|  | // key for the identity id.  On success, the signature is stored in sig. Call | ||
|  | // Serialize to write it out. | ||
|  | // If config is nil, sensible defaults will be used. | ||
|  | func (sig *Signature) SignUserId(id string, pub *PublicKey, priv *PrivateKey, config *Config) error { | ||
|  | 	h, err := userIdSignatureHash(id, pub, sig.Hash) | ||
|  | 	if err != nil { | ||
|  | 		return err | ||
|  | 	} | ||
|  | 	return sig.Sign(h, priv, config) | ||
|  | } | ||
|  | 
 | ||
|  | // SignKey computes a signature from priv, asserting that pub is a subkey. On | ||
|  | // success, the signature is stored in sig. Call Serialize to write it out. | ||
|  | // If config is nil, sensible defaults will be used. | ||
|  | func (sig *Signature) SignKey(pub *PublicKey, priv *PrivateKey, config *Config) error { | ||
|  | 	h, err := keySignatureHash(&priv.PublicKey, pub, sig.Hash) | ||
|  | 	if err != nil { | ||
|  | 		return err | ||
|  | 	} | ||
|  | 	return sig.Sign(h, priv, config) | ||
|  | } | ||
|  | 
 | ||
|  | // Serialize marshals sig to w. Sign, SignUserId or SignKey must have been | ||
|  | // called first. | ||
|  | func (sig *Signature) Serialize(w io.Writer) (err error) { | ||
|  | 	if len(sig.outSubpackets) == 0 { | ||
|  | 		sig.outSubpackets = sig.rawSubpackets | ||
|  | 	} | ||
|  | 	if sig.RSASignature.bytes == nil && sig.DSASigR.bytes == nil && sig.ECDSASigR.bytes == nil { | ||
|  | 		return errors.InvalidArgumentError("Signature: need to call Sign, SignUserId or SignKey before Serialize") | ||
|  | 	} | ||
|  | 
 | ||
|  | 	sigLength := 0 | ||
|  | 	switch sig.PubKeyAlgo { | ||
|  | 	case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: | ||
|  | 		sigLength = 2 + len(sig.RSASignature.bytes) | ||
|  | 	case PubKeyAlgoDSA: | ||
|  | 		sigLength = 2 + len(sig.DSASigR.bytes) | ||
|  | 		sigLength += 2 + len(sig.DSASigS.bytes) | ||
|  | 	case PubKeyAlgoECDSA: | ||
|  | 		sigLength = 2 + len(sig.ECDSASigR.bytes) | ||
|  | 		sigLength += 2 + len(sig.ECDSASigS.bytes) | ||
|  | 	default: | ||
|  | 		panic("impossible") | ||
|  | 	} | ||
|  | 
 | ||
|  | 	unhashedSubpacketsLen := subpacketsLength(sig.outSubpackets, false) | ||
|  | 	length := len(sig.HashSuffix) - 6 /* trailer not included */ + | ||
|  | 		2 /* length of unhashed subpackets */ + unhashedSubpacketsLen + | ||
|  | 		2 /* hash tag */ + sigLength | ||
|  | 	err = serializeHeader(w, packetTypeSignature, length) | ||
|  | 	if err != nil { | ||
|  | 		return | ||
|  | 	} | ||
|  | 
 | ||
|  | 	_, err = w.Write(sig.HashSuffix[:len(sig.HashSuffix)-6]) | ||
|  | 	if err != nil { | ||
|  | 		return | ||
|  | 	} | ||
|  | 
 | ||
|  | 	unhashedSubpackets := make([]byte, 2+unhashedSubpacketsLen) | ||
|  | 	unhashedSubpackets[0] = byte(unhashedSubpacketsLen >> 8) | ||
|  | 	unhashedSubpackets[1] = byte(unhashedSubpacketsLen) | ||
|  | 	serializeSubpackets(unhashedSubpackets[2:], sig.outSubpackets, false) | ||
|  | 
 | ||
|  | 	_, err = w.Write(unhashedSubpackets) | ||
|  | 	if err != nil { | ||
|  | 		return | ||
|  | 	} | ||
|  | 	_, err = w.Write(sig.HashTag[:]) | ||
|  | 	if err != nil { | ||
|  | 		return | ||
|  | 	} | ||
|  | 
 | ||
|  | 	switch sig.PubKeyAlgo { | ||
|  | 	case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: | ||
|  | 		err = writeMPIs(w, sig.RSASignature) | ||
|  | 	case PubKeyAlgoDSA: | ||
|  | 		err = writeMPIs(w, sig.DSASigR, sig.DSASigS) | ||
|  | 	case PubKeyAlgoECDSA: | ||
|  | 		err = writeMPIs(w, sig.ECDSASigR, sig.ECDSASigS) | ||
|  | 	default: | ||
|  | 		panic("impossible") | ||
|  | 	} | ||
|  | 	return | ||
|  | } | ||
|  | 
 | ||
|  | // outputSubpacket represents a subpacket to be marshaled. | ||
|  | type outputSubpacket struct { | ||
|  | 	hashed        bool // true if this subpacket is in the hashed area. | ||
|  | 	subpacketType signatureSubpacketType | ||
|  | 	isCritical    bool | ||
|  | 	contents      []byte | ||
|  | } | ||
|  | 
 | ||
|  | func (sig *Signature) buildSubpackets() (subpackets []outputSubpacket) { | ||
|  | 	creationTime := make([]byte, 4) | ||
|  | 	binary.BigEndian.PutUint32(creationTime, uint32(sig.CreationTime.Unix())) | ||
|  | 	subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, false, creationTime}) | ||
|  | 
 | ||
|  | 	if sig.IssuerKeyId != nil { | ||
|  | 		keyId := make([]byte, 8) | ||
|  | 		binary.BigEndian.PutUint64(keyId, *sig.IssuerKeyId) | ||
|  | 		subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, false, keyId}) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if sig.SigLifetimeSecs != nil && *sig.SigLifetimeSecs != 0 { | ||
|  | 		sigLifetime := make([]byte, 4) | ||
|  | 		binary.BigEndian.PutUint32(sigLifetime, *sig.SigLifetimeSecs) | ||
|  | 		subpackets = append(subpackets, outputSubpacket{true, signatureExpirationSubpacket, true, sigLifetime}) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Key flags may only appear in self-signatures or certification signatures. | ||
|  | 
 | ||
|  | 	if sig.FlagsValid { | ||
|  | 		var flags byte | ||
|  | 		if sig.FlagCertify { | ||
|  | 			flags |= KeyFlagCertify | ||
|  | 		} | ||
|  | 		if sig.FlagSign { | ||
|  | 			flags |= KeyFlagSign | ||
|  | 		} | ||
|  | 		if sig.FlagEncryptCommunications { | ||
|  | 			flags |= KeyFlagEncryptCommunications | ||
|  | 		} | ||
|  | 		if sig.FlagEncryptStorage { | ||
|  | 			flags |= KeyFlagEncryptStorage | ||
|  | 		} | ||
|  | 		subpackets = append(subpackets, outputSubpacket{true, keyFlagsSubpacket, false, []byte{flags}}) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// The following subpackets may only appear in self-signatures | ||
|  | 
 | ||
|  | 	if sig.KeyLifetimeSecs != nil && *sig.KeyLifetimeSecs != 0 { | ||
|  | 		keyLifetime := make([]byte, 4) | ||
|  | 		binary.BigEndian.PutUint32(keyLifetime, *sig.KeyLifetimeSecs) | ||
|  | 		subpackets = append(subpackets, outputSubpacket{true, keyExpirationSubpacket, true, keyLifetime}) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if sig.IsPrimaryId != nil && *sig.IsPrimaryId { | ||
|  | 		subpackets = append(subpackets, outputSubpacket{true, primaryUserIdSubpacket, false, []byte{1}}) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if len(sig.PreferredSymmetric) > 0 { | ||
|  | 		subpackets = append(subpackets, outputSubpacket{true, prefSymmetricAlgosSubpacket, false, sig.PreferredSymmetric}) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if len(sig.PreferredHash) > 0 { | ||
|  | 		subpackets = append(subpackets, outputSubpacket{true, prefHashAlgosSubpacket, false, sig.PreferredHash}) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if len(sig.PreferredCompression) > 0 { | ||
|  | 		subpackets = append(subpackets, outputSubpacket{true, prefCompressionSubpacket, false, sig.PreferredCompression}) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return | ||
|  | } |