go.crypto/openpgp: check for revoked keys.

R=agl
CC=golang-codereviews
https://golang.org/cl/95050043
This commit is contained in:
Adam Langley
2014-05-05 11:41:50 -07:00
parent df2851958a
commit aa3adaf1aa
7 changed files with 290 additions and 41 deletions
+8
View File
@@ -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
View File
@@ -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)
File diff suppressed because one or more lines are too long
+10 -7
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}