From f23aef828f9b715b20ed93bbb1041b215f97a2a2 Mon Sep 17 00:00:00 2001 From: Adam Langley Date: Mon, 5 Aug 2013 14:22:57 -0400 Subject: [PATCH] go.crypto/openpgp: test that hashes are compiled in before using them. Previously, if Encrypt decided to use a hash function that wasn't compiled in then it would panic. R=golang-dev, bradfitz CC=golang-dev https://golang.org/cl/12055043 --- openpgp/s2k/s2k.go | 27 ++++++++++++++++++++------- openpgp/write.go | 30 +++++++++++++++++++++++------- 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/openpgp/s2k/s2k.go b/openpgp/s2k/s2k.go index c466a8d..33462cc 100644 --- a/openpgp/s2k/s2k.go +++ b/openpgp/s2k/s2k.go @@ -151,14 +151,15 @@ func Serialize(w io.Writer, key []byte, rand io.Reader, passphrase []byte) error var hashToHashIdMapping = []struct { id byte hash crypto.Hash + name string }{ - {1, crypto.MD5}, - {2, crypto.SHA1}, - {3, crypto.RIPEMD160}, - {8, crypto.SHA256}, - {9, crypto.SHA384}, - {10, crypto.SHA512}, - {11, crypto.SHA224}, + {1, crypto.MD5, "MD5"}, + {2, crypto.SHA1, "SHA1"}, + {3, crypto.RIPEMD160, "RIPEMD160"}, + {8, crypto.SHA256, "SHA256"}, + {9, crypto.SHA384, "SHA384"}, + {10, crypto.SHA512, "SHA512"}, + {11, crypto.SHA224, "SHA224"}, } // HashIdToHash returns a crypto.Hash which corresponds to the given OpenPGP @@ -172,6 +173,18 @@ func HashIdToHash(id byte) (h crypto.Hash, ok bool) { return 0, false } +// HashIdToString returns the name of the hash function corresponding to the +// given OpenPGP hash id, or panics if id is unknown. +func HashIdToString(id byte) (name string, ok bool) { + for _, m := range hashToHashIdMapping { + if m.id == id { + return m.name, true + } + } + + return "", false +} + // HashIdToHash returns an OpenPGP hash id which corresponds the given Hash. func HashToHashId(h crypto.Hash) (id byte, ok bool) { for _, m := range hashToHashIdMapping { diff --git a/openpgp/write.go b/openpgp/write.go index 2be8c3c..f89aab9 100644 --- a/openpgp/write.go +++ b/openpgp/write.go @@ -224,16 +224,32 @@ func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHint } } - hashFunc := candidateHashes[0] - // If the hash specified by config is a candidate, we'll use that. - configuredHash := config.Hash() - for _, h := range candidateHashes { - if h == uint8(configuredHash) { - hashFunc = h + var hash crypto.Hash + for _, hashId := range candidateHashes { + if h, ok := s2k.HashIdToHash(hashId); ok && h.Available() { + hash = h break } } - hash, _ := s2k.HashIdToHash(hashFunc) + + // If the hash specified by config is a candidate, we'll use that. + if configuredHash := config.Hash(); configuredHash.Available() { + for _, hashId := range candidateHashes { + if h, ok := s2k.HashIdToHash(hashId); ok && h == configuredHash { + hash = h + break + } + } + } + + if hash == 0 { + hashId := candidateHashes[0] + name, ok := s2k.HashIdToString(hashId) + if !ok { + name = "#" + strconv.Itoa(int(hashId)) + } + return nil, errors.InvalidArgumentError("cannot encrypt because no candidate hash functions are compiled in. (Wanted " + name + " in this case.)") + } symKey := make([]byte, cipher.KeySize()) if _, err := io.ReadFull(config.Random(), symKey); err != nil {