소스 검색

go.crypto/openpgp: check for revoked keys.

R=agl
CC=golang-codereviews
https://golang.org/cl/95050043
Adam Langley 11 년 전
부모
커밋
aa3adaf1aa
7개의 변경된 파일287개의 추가작업 그리고 41개의 파일을 삭제
  1. 8 0
      openpgp/errors/errors.go
  2. 68 7
      openpgp/keys.go
  3. 136 0
      openpgp/keys_test.go
  4. 10 7
      openpgp/packet/packet.go
  5. 23 0
      openpgp/packet/public_key.go
  6. 34 8
      openpgp/packet/signature.go
  7. 8 19
      openpgp/read.go

+ 8 - 0
openpgp/errors/errors.go

@@ -57,6 +57,14 @@ func (unknownIssuerError) Error() string {
 
 var ErrUnknownIssuer error = unknownIssuerError(0)
 
+type keyRevokedError int
+
+func (keyRevokedError) Error() string {
+	return "openpgp: signature made by revoked key"
+}
+
+var ErrKeyRevoked error = keyRevokedError(0)
+
 type UnknownPacketTypeError uint8
 
 func (upte UnknownPacketTypeError) Error() string {

+ 68 - 7
openpgp/keys.go

@@ -23,10 +23,11 @@ var PrivateKeyType = "PGP PRIVATE KEY BLOCK"
 // (which must be a signing key), one or more identities claimed by that key,
 // and zero or more subkeys, which may be encryption keys.
 type Entity struct {
-	PrimaryKey *packet.PublicKey
-	PrivateKey *packet.PrivateKey
-	Identities map[string]*Identity // indexed by Identity.Name
-	Subkeys    []Subkey
+	PrimaryKey  *packet.PublicKey
+	PrivateKey  *packet.PrivateKey
+	Identities  map[string]*Identity // indexed by Identity.Name
+	Revocations []*packet.Signature
+	Subkeys     []Subkey
 }
 
 // An Identity represents an identity claimed by an Entity and zero or more
@@ -59,6 +60,11 @@ type Key struct {
 type KeyRing interface {
 	// KeysById returns the set of keys that have the given key id.
 	KeysById(id uint64) []Key
+	// KeysByIdAndUsage returns the set of keys with the given id
+	// that also meet the key usage given by requiredUsage.
+	// The requiredUsage is expressed as the bitwise-OR of
+	// packet.KeyFlag* values.
+	KeysByIdUsage(id uint64, requiredUsage byte) []Key
 	// DecryptionKeys returns all private keys that are valid for
 	// decryption.
 	DecryptionKeys() []Key
@@ -173,6 +179,43 @@ func (el EntityList) KeysById(id uint64) (keys []Key) {
 	return
 }
 
+// KeysByIdAndUsage returns the set of keys with the given id that also meet
+// the key usage given by requiredUsage.  The requiredUsage is expressed as
+// the bitwise-OR of packet.KeyFlag* values.
+func (el EntityList) KeysByIdUsage(id uint64, requiredUsage byte) (keys []Key) {
+	for _, key := range el.KeysById(id) {
+		if len(key.Entity.Revocations) > 0 {
+			continue
+		}
+
+		if key.SelfSignature.RevocationReason != nil {
+			continue
+		}
+
+		if key.SelfSignature.FlagsValid && requiredUsage != 0 {
+			var usage byte
+			if key.SelfSignature.FlagCertify {
+				usage |= packet.KeyFlagCertify
+			}
+			if key.SelfSignature.FlagSign {
+				usage |= packet.KeyFlagSign
+			}
+			if key.SelfSignature.FlagEncryptCommunications {
+				usage |= packet.KeyFlagEncryptCommunications
+			}
+			if key.SelfSignature.FlagEncryptStorage {
+				usage |= packet.KeyFlagEncryptStorage
+			}
+			if usage&requiredUsage != requiredUsage {
+				continue
+			}
+		}
+
+		keys = append(keys, key)
+	}
+	return
+}
+
 // DecryptionKeys returns all private keys that are valid for decryption.
 func (el EntityList) DecryptionKeys() (keys []Key) {
 	for _, e := range el {
@@ -290,6 +333,7 @@ func ReadEntity(packets *packet.Reader) (*Entity, error) {
 	}
 
 	var current *Identity
+	var revocations []*packet.Signature
 EachPacket:
 	for {
 		p, err := packets.Next()
@@ -329,10 +373,17 @@ EachPacket:
 				current.Signatures = append(current.Signatures, sig)
 			}
 		case *packet.Signature:
-			if current == nil {
+			if pkt.SigType == packet.SigTypeKeyRevocation {
+				revocations = append(revocations, pkt)
+			} else if pkt.SigType == packet.SigTypeDirectSignature {
+				// TODO: RFC4880 5.2.1 permits signatures
+				// directly on keys (eg. to bind additional
+				// revocation keys).
+			} else if current == nil {
 				return nil, errors.StructuralError("signature packet found before user id packet")
+			} else {
+				current.Signatures = append(current.Signatures, pkt)
 			}
-			current.Signatures = append(current.Signatures, pkt)
 		case *packet.PrivateKey:
 			if pkt.IsSubkey == false {
 				packets.Unread(p)
@@ -360,6 +411,16 @@ EachPacket:
 		return nil, errors.StructuralError("entity without any identities")
 	}
 
+	for _, revocation := range revocations {
+		err = e.PrimaryKey.VerifyRevocationSignature(revocation)
+		if err == nil {
+			e.Revocations = append(e.Revocations, revocation)
+		} else {
+			// TODO: RFC 4880 5.2.3.15 defines revocation keys.
+			return nil, errors.StructuralError("revocation signature signed by alternate key")
+		}
+	}
+
 	return e, nil
 }
 
@@ -379,7 +440,7 @@ func addSubkey(e *Entity, packets *packet.Reader, pub *packet.PublicKey, priv *p
 	if !ok {
 		return errors.StructuralError("subkey packet not followed by signature")
 	}
-	if subKey.Sig.SigType != packet.SigTypeSubkeyBinding {
+	if subKey.Sig.SigType != packet.SigTypeSubkeyBinding && subKey.Sig.SigType != packet.SigTypeSubkeyRevocation {
 		return errors.StructuralError("subkey signature with wrong type")
 	}
 	err = e.PrimaryKey.VerifyKeySignature(subKey.PublicKey, subKey.Sig)

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 136 - 0
openpgp/keys_test.go


+ 10 - 7
openpgp/packet/packet.go

@@ -385,13 +385,16 @@ func Read(r io.Reader) (p Packet, err error) {
 type SignatureType uint8
 
 const (
-	SigTypeBinary        SignatureType = 0
-	SigTypeText                        = 1
-	SigTypeGenericCert                 = 0x10
-	SigTypePersonaCert                 = 0x11
-	SigTypeCasualCert                  = 0x12
-	SigTypePositiveCert                = 0x13
-	SigTypeSubkeyBinding               = 0x18
+	SigTypeBinary           SignatureType = 0
+	SigTypeText                           = 1
+	SigTypeGenericCert                    = 0x10
+	SigTypePersonaCert                    = 0x11
+	SigTypeCasualCert                     = 0x12
+	SigTypePositiveCert                   = 0x13
+	SigTypeSubkeyBinding                  = 0x18
+	SigTypeDirectSignature                = 0x1F
+	SigTypeKeyRevocation                  = 0x20
+	SigTypeSubkeyRevocation               = 0x28
 )
 
 // PublicKeyAlgorithm represents the different public key system specified for

+ 23 - 0
openpgp/packet/public_key.go

@@ -572,6 +572,29 @@ func (pk *PublicKey) VerifyKeySignature(signed *PublicKey, sig *Signature) (err
 	return pk.VerifySignature(h, sig)
 }
 
+func keyRevocationHash(pk signingKey, hashFunc crypto.Hash) (h hash.Hash, err error) {
+	if !hashFunc.Available() {
+		return nil, errors.UnsupportedError("hash function")
+	}
+	h = hashFunc.New()
+
+	// RFC 4880, section 5.2.4
+	pk.SerializeSignaturePrefix(h)
+	pk.serializeWithoutHeaders(h)
+
+	return
+}
+
+// VerifyRevocationSignature returns nil iff sig is a valid signature, made by this
+// public key.
+func (pk *PublicKey) VerifyRevocationSignature(sig *Signature) (err error) {
+	h, err := keyRevocationHash(pk, sig.Hash)
+	if err != nil {
+		return err
+	}
+	return pk.VerifySignature(h, sig)
+}
+
 // userIdSignatureHash returns a Hash of the message that needs to be signed
 // to assert that pk is a valid key for id.
 func userIdSignatureHash(id string, pk *PublicKey, hashFunc crypto.Hash) (h hash.Hash, err error) {

+ 34 - 8
openpgp/packet/signature.go

@@ -17,6 +17,14 @@ import (
 	"time"
 )
 
+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
@@ -50,6 +58,11 @@ type Signature struct {
 	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
+
 	outSubpackets []outputSubpacket
 }
 
@@ -176,6 +189,7 @@ const (
 	prefCompressionSubpacket     signatureSubpacketType = 22
 	primaryUserIdSubpacket       signatureSubpacketType = 25
 	keyFlagsSubpacket            signatureSubpacketType = 27
+	reasonForRevocationSubpacket signatureSubpacketType = 29
 )
 
 // parseSignatureSubpacket parses a single subpacket. len(subpacket) is >= 1.
@@ -305,18 +319,30 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
 			return
 		}
 		sig.FlagsValid = true
-		if subpacket[0]&1 != 0 {
+		if subpacket[0]&KeyFlagCertify != 0 {
 			sig.FlagCertify = true
 		}
-		if subpacket[0]&2 != 0 {
+		if subpacket[0]&KeyFlagSign != 0 {
 			sig.FlagSign = true
 		}
-		if subpacket[0]&4 != 0 {
+		if subpacket[0]&KeyFlagEncryptCommunications != 0 {
 			sig.FlagEncryptCommunications = true
 		}
-		if subpacket[0]&8 != 0 {
+		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:])
 
 	default:
 		if isCritical {
@@ -595,16 +621,16 @@ func (sig *Signature) buildSubpackets() (subpackets []outputSubpacket) {
 	if sig.FlagsValid {
 		var flags byte
 		if sig.FlagCertify {
-			flags |= 1
+			flags |= KeyFlagCertify
 		}
 		if sig.FlagSign {
-			flags |= 2
+			flags |= KeyFlagSign
 		}
 		if sig.FlagEncryptCommunications {
-			flags |= 4
+			flags |= KeyFlagEncryptCommunications
 		}
 		if sig.FlagEncryptStorage {
-			flags |= 8
+			flags |= KeyFlagEncryptStorage
 		}
 		subpackets = append(subpackets, outputSubpacket{true, keyFlagsSubpacket, false, []byte{flags}})
 	}

+ 8 - 19
openpgp/read.go

@@ -249,13 +249,9 @@ FindLiteralData:
 
 			md.IsSigned = true
 			md.SignedByKeyId = p.KeyId
-			keys := keyring.KeysById(p.KeyId)
-			for i, key := range keys {
-				if key.SelfSignature.FlagsValid && !key.SelfSignature.FlagSign {
-					continue
-				}
-				md.SignedBy = &keys[i]
-				break
+			keys := keyring.KeysByIdUsage(p.KeyId, packet.KeyFlagSign)
+			if len(keys) > 0 {
+				md.SignedBy = &keys[0]
 			}
 		case *packet.LiteralData:
 			md.LiteralData = p
@@ -382,11 +378,6 @@ func CheckDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signe
 		return nil, errors.StructuralError("non signature packet found")
 	}
 
-	keys := keyring.KeysById(issuerKeyId)
-	if len(keys) == 0 {
-		return nil, errors.ErrUnknownIssuer
-	}
-
 	h, wrappedHash, err := hashForSignature(hashFunc, sigType)
 	if err != nil {
 		return
@@ -397,10 +388,12 @@ func CheckDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signe
 		return
 	}
 
+	keys := keyring.KeysByIdUsage(issuerKeyId, packet.KeyFlagSign)
+	if len(keys) == 0 {
+		return nil, errors.ErrUnknownIssuer
+	}
+
 	for _, key := range keys {
-		if key.SelfSignature.FlagsValid && !key.SelfSignature.FlagSign {
-			continue
-		}
 		switch sig := p.(type) {
 		case *packet.Signature:
 			err = key.PublicKey.VerifySignature(h, sig)
@@ -412,10 +405,6 @@ func CheckDetachedSignature(keyring KeyRing, signed, signature io.Reader) (signe
 		}
 	}
 
-	if err != nil {
-		return
-	}
-
 	return nil, errors.ErrUnknownIssuer
 }
 

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.