Skip to content

Commit

Permalink
Drop support for MD5, RIPEMD160, deprecate SHA1, CAST5, 3DES
Browse files Browse the repository at this point in the history
  • Loading branch information
wussler committed Jan 25, 2023
1 parent 0acdc8a commit 16dfff0
Show file tree
Hide file tree
Showing 18 changed files with 157 additions and 93 deletions.
10 changes: 0 additions & 10 deletions openpgp/clearsign/clearsign.go
Original file line number Diff line number Diff line change
Expand Up @@ -421,12 +421,6 @@ func (b *Block) VerifySignature(keyring openpgp.KeyRing, config *packet.Config)
// if the name isn't known. See RFC 4880, section 9.4.
func nameOfHash(h crypto.Hash) string {
switch h {
case crypto.MD5:
return "MD5"
case crypto.SHA1:
return "SHA1"
case crypto.RIPEMD160:
return "RIPEMD160"
case crypto.SHA224:
return "SHA224"
case crypto.SHA256:
Expand All @@ -447,12 +441,8 @@ func nameOfHash(h crypto.Hash) string {
// if the name isn't known. See RFC 4880, section 9.4.
func nameToHash(h string) crypto.Hash {
switch h {
case "MD5":
return crypto.MD5
case "SHA1":
return crypto.SHA1
case "RIPEMD160":
return crypto.RIPEMD160
case "SHA224":
return crypto.SHA224
case "SHA256":
Expand Down
67 changes: 59 additions & 8 deletions openpgp/internal/algorithm/hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@ type Hash interface {

// The following vars mirror the crypto/Hash supported hash functions.
var (
MD5 Hash = cryptoHash{1, crypto.MD5}
SHA1 Hash = cryptoHash{2, crypto.SHA1}
RIPEMD160 Hash = cryptoHash{3, crypto.RIPEMD160}
SHA256 Hash = cryptoHash{8, crypto.SHA256}
SHA384 Hash = cryptoHash{9, crypto.SHA384}
SHA512 Hash = cryptoHash{10, crypto.SHA512}
Expand All @@ -47,9 +45,6 @@ var (
// http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-14
var (
HashById = map[uint8]Hash{
MD5.Id(): MD5,
SHA1.Id(): SHA1,
RIPEMD160.Id(): RIPEMD160,
SHA256.Id(): SHA256,
SHA384.Id(): SHA384,
SHA512.Id(): SHA512,
Expand All @@ -72,9 +67,6 @@ func (h cryptoHash) Id() uint8 {
}

var hashNames = map[uint8]string{
MD5.Id(): "MD5",
SHA1.Id(): "SHA1",
RIPEMD160.Id(): "RIPEMD160",
SHA256.Id(): "SHA256",
SHA384.Id(): "SHA384",
SHA512.Id(): "SHA512",
Expand All @@ -90,3 +82,62 @@ func (h cryptoHash) String() string {
}
return s
}

// HashIdToHash returns a crypto.Hash which corresponds to the given OpenPGP
// hash id.
func HashIdToHash(id byte) (h crypto.Hash, ok bool) {
if hash, ok := HashById[id]; ok {
return hash.HashFunc(), true
}
return 0, false
}

// HashIdToHashWithSha1 returns a crypto.Hash which corresponds to the given OpenPGP
// hash id, allowing sha1.
func HashIdToHashWithSha1(id byte) (h crypto.Hash, ok bool) {
if hash, ok := HashById[id]; ok {
return hash.HashFunc(), true
}

if id == SHA1.Id() {
return SHA1.HashFunc(), true
}

return 0, false
}

// HashIdToString returns the name of the hash function corresponding to the
// given OpenPGP hash id.
func HashIdToString(id byte) (name string, ok bool) {
if hash, ok := HashById[id]; ok {
return hash.String(), true
}
return "", false
}

// HashToHashId returns an OpenPGP hash id which corresponds the given Hash.
func HashToHashId(h crypto.Hash) (id byte, ok bool) {
for id, hash := range HashById {
if hash.HashFunc() == h {
return id, true
}
}

return 0, false
}

// HashToHashIdWithSha1 returns an OpenPGP hash id which corresponds the given Hash,
// allowing instances of SHA1
func HashToHashIdWithSha1(h crypto.Hash) (id byte, ok bool) {
for id, hash := range HashById {
if hash.HashFunc() == h {
return id, true
}
}

if h == SHA1.HashFunc() {
return SHA1.Id(), true
}

return 0, false
}
7 changes: 6 additions & 1 deletion openpgp/key_generation.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,12 @@ func (t *Entity) addUserId(name, comment, email string, config *packet.Config, c

// Set the PreferredHash for the SelfSignature from the packet.Config.
// If it is not the must-implement algorithm from rfc4880bis, append that.
selfSignature.PreferredHash = []uint8{hashToHashId(config.Hash())}
hash, ok := algorithm.HashToHashId(config.Hash())
if !ok {
return errors.UnsupportedError("unsupported preferred hash function")
}

selfSignature.PreferredHash = []uint8{hash}
if config.Hash() != crypto.SHA256 {
selfSignature.PreferredHash = append(selfSignature.PreferredHash, hashToHashId(crypto.SHA256))
}
Expand Down
7 changes: 7 additions & 0 deletions openpgp/keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,13 @@ func TestNewEntityWithDefaultHash(t *testing.T) {
DefaultHash: hash,
}
entity, err := NewEntity("Golang Gopher", "Test Key", "[email protected]", c)
if hash == crypto.SHA1 {
if err == nil {
t.Fatal("should fail on SHA1 key creation")
}
continue
}

if err != nil {
t.Fatal(err)
}
Expand Down
8 changes: 6 additions & 2 deletions openpgp/packet/encrypted_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const encryptedKeyVersion = 3
type EncryptedKey struct {
KeyId uint64
Algo PublicKeyAlgorithm
CipherFunc CipherFunction // only valid after a successful Decrypt for a v4 packet
CipherFunc CipherFunction // only valid after a successful Decrypt for a v3 packet
Key []byte // only valid after a successful Decrypt

encryptedMPI1, encryptedMPI2 encoding.Field
Expand Down Expand Up @@ -123,6 +123,10 @@ func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error {
}

e.CipherFunc = CipherFunction(b[0])
if !e.CipherFunc.IsSupported() {
return errors.UnsupportedError("unsupported encryption function")
}

e.Key = b[1 : len(b)-2]
expectedChecksum := uint16(b[len(b)-2])<<8 | uint16(b[len(b)-1])
checksum := checksumKeyMaterial(e.Key)
Expand Down Expand Up @@ -279,4 +283,4 @@ func serializeEncryptedKeyECDH(w io.Writer, rand io.Reader, header [10]byte, pub
}
_, err = w.Write(m.EncodedBytes())
return err
}
}
6 changes: 3 additions & 3 deletions openpgp/packet/one_pass_signature.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"crypto"
"encoding/binary"
"github.com/ProtonMail/go-crypto/openpgp/errors"
"github.com/ProtonMail/go-crypto/openpgp/s2k"
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
"io"
"strconv"
)
Expand Down Expand Up @@ -37,7 +37,7 @@ func (ops *OnePassSignature) parse(r io.Reader) (err error) {
}

var ok bool
ops.Hash, ok = s2k.HashIdToHash(buf[2])
ops.Hash, ok = algorithm.HashIdToHashWithSha1(buf[2])
if !ok {
return errors.UnsupportedError("hash function: " + strconv.Itoa(int(buf[2])))
}
Expand All @@ -55,7 +55,7 @@ func (ops *OnePassSignature) Serialize(w io.Writer) error {
buf[0] = onePassSignatureVersion
buf[1] = uint8(ops.SigType)
var ok bool
buf[2], ok = s2k.HashToHashId(ops.Hash)
buf[2], ok = algorithm.HashToHashId(ops.Hash)
if !ok {
return errors.UnsupportedError("hash type: " + strconv.Itoa(int(ops.Hash)))
}
Expand Down
10 changes: 10 additions & 0 deletions openpgp/packet/packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,16 @@ func (cipher CipherFunction) KeySize() int {
return algorithm.CipherFunction(cipher).KeySize()
}

// IsSupported returns true if the cipher is supported from the library
func (cipher CipherFunction) IsSupported() bool {
return algorithm.CipherFunction(cipher).KeySize() > 0
}

// IsAes returns true if the cipher is AES
func (cipher CipherFunction) IsAes() bool {
return cipher.IsSupported() && cipher >= CipherAES128
}

// blockSize returns the block size, in bytes, of cipher.
func (cipher CipherFunction) blockSize() int {
return algorithm.CipherFunction(cipher).BlockSize()
Expand Down
3 changes: 3 additions & 0 deletions openpgp/packet/private_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) {
return
}
pk.cipher = CipherFunction(buf[0])
if !pk.cipher.IsSupported() {
return errors.UnsupportedError("unsupported cipher function in private key")
}
pk.s2kParams, err = s2k.ParseIntoParams(r)
if err != nil {
return
Expand Down
28 changes: 23 additions & 5 deletions openpgp/packet/signature.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"crypto"
"crypto/dsa"
"encoding/binary"
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
"hash"
"io"
"strconv"
Expand All @@ -18,7 +19,6 @@ import (
"github.com/ProtonMail/go-crypto/openpgp/eddsa"
"github.com/ProtonMail/go-crypto/openpgp/errors"
"github.com/ProtonMail/go-crypto/openpgp/internal/encoding"
"github.com/ProtonMail/go-crypto/openpgp/s2k"
)

const (
Expand Down Expand Up @@ -138,7 +138,13 @@ func (sig *Signature) parse(r io.Reader) (err error) {
}

var ok bool
sig.Hash, ok = s2k.HashIdToHash(buf[2])

if sig.Version < 5 {
sig.Hash, ok = algorithm.HashIdToHashWithSha1(buf[2])
} else {
sig.Hash, ok = algorithm.HashIdToHash(buf[2])
}

if !ok {
return errors.UnsupportedError("hash function " + strconv.Itoa(int(buf[2])))
}
Expand All @@ -149,7 +155,11 @@ func (sig *Signature) parse(r io.Reader) (err error) {
if err != nil {
return
}
sig.buildHashSuffix(hashedSubpackets)
err = sig.buildHashSuffix(hashedSubpackets)
if err != nil {
return
}

err = parseSignatureSubpackets(sig, hashedSubpackets, true)
if err != nil {
return
Expand Down Expand Up @@ -597,7 +607,15 @@ func (sig *Signature) SigExpired(currentTime time.Time) bool {

// buildHashSuffix constructs the HashSuffix member of sig in preparation for signing.
func (sig *Signature) buildHashSuffix(hashedSubpackets []byte) (err error) {
hash, ok := s2k.HashToHashId(sig.Hash)
var hashId byte
var ok bool

if sig.Version < 5 {
hashId, ok = algorithm.HashToHashIdWithSha1(sig.Hash)
} else {
hashId, ok = algorithm.HashToHashId(sig.Hash)
}

if !ok {
sig.HashSuffix = nil
return errors.InvalidArgumentError("hash cannot be represented in OpenPGP: " + strconv.Itoa(int(sig.Hash)))
Expand All @@ -607,7 +625,7 @@ func (sig *Signature) buildHashSuffix(hashedSubpackets []byte) (err error) {
uint8(sig.Version),
uint8(sig.SigType),
uint8(sig.PubKeyAlgo),
uint8(hash),
uint8(hashId),
uint8(len(hashedSubpackets) >> 8),
uint8(len(hashedSubpackets)),
})
Expand Down
15 changes: 7 additions & 8 deletions openpgp/packet/symmetric_key_encrypted.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func (ske *SymmetricKeyEncrypted) parse(r io.Reader) error {
return err
}
ske.CipherFunc = CipherFunction(buf[0])
if ske.CipherFunc.KeySize() == 0 {
if !ske.CipherFunc.IsSupported() {
return errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(buf[0])))
}

Expand Down Expand Up @@ -168,12 +168,11 @@ func (ske *SymmetricKeyEncrypted) decryptV5(key []byte) ([]byte, error) {
// If config is nil, sensible defaults will be used.
func SerializeSymmetricKeyEncrypted(w io.Writer, passphrase []byte, config *Config) (key []byte, err error) {
cipherFunc := config.Cipher()
keySize := cipherFunc.KeySize()
if keySize == 0 {
return nil, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc)))
if !cipherFunc.IsAes() {
return nil, errors.UnsupportedError("unsupported cipher: " + strconv.Itoa(int(cipherFunc)))
}

sessionKey := make([]byte, keySize)
sessionKey := make([]byte, cipherFunc.KeySize())
_, err = io.ReadFull(config.Random(), sessionKey)
if err != nil {
return
Expand Down Expand Up @@ -201,11 +200,11 @@ func SerializeSymmetricKeyEncryptedReuseKey(w io.Writer, sessionKey []byte, pass
version = 4
}
cipherFunc := config.Cipher()
keySize := cipherFunc.KeySize()
if keySize == 0 {
return errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc)))
if !cipherFunc.IsAes() {
return errors.UnsupportedError("unsupported cipher: " + strconv.Itoa(int(cipherFunc)))
}

keySize := cipherFunc.KeySize()
s2kBuf := new(bytes.Buffer)
keyEncryptingKey := make([]byte, keySize)
// s2k.Serialize salts and stretches the passphrase, and writes the
Expand Down
2 changes: 0 additions & 2 deletions openpgp/packet/symmetric_key_encrypted_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,6 @@ func TestSerializeSymmetricKeyEncryptedV5RandomizeSlow(t *testing.T) {

func TestSerializeSymmetricKeyEncryptedCiphersV4(t *testing.T) {
tests := map[string] CipherFunction {
"3DES": Cipher3DES,
"CAST5": CipherCAST5,
"AES128": CipherAES128,
"AES192": CipherAES192,
"AES256": CipherAES256,
Expand Down
6 changes: 5 additions & 1 deletion openpgp/packet/symmetrically_encrypted_aead.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func (se *SymmetricallyEncrypted) parseAead(r io.Reader) error {

// Cipher
se.cipher = CipherFunction(headerData[0])
if se.cipher.KeySize() == 0 {
if !se.cipher.IsAes() {
return errors.UnsupportedError("unknown cipher: " + string(se.cipher))
}

Expand Down Expand Up @@ -87,6 +87,10 @@ func (se *SymmetricallyEncrypted) decryptAead(inputKey []byte) (io.ReadCloser, e
// serializeSymmetricallyEncryptedAead encrypts to a writer a V2 SEIPD packet (AEAD) as specified in
// https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#section-5.13.2
func serializeSymmetricallyEncryptedAead(ciphertext io.WriteCloser, cipherSuite CipherSuite, chunkSizeByte byte, rand io.Reader, inputKey []byte) (Contents io.WriteCloser, err error) {
if !cipherSuite.Cipher.IsAes() {
return nil, errors.InvalidArgumentError("invalid aead cipher function")
}

if cipherSuite.Cipher.KeySize() != len(inputKey) {
return nil, errors.InvalidArgumentError("error in aead serialization: bad key length")
}
Expand Down
12 changes: 8 additions & 4 deletions openpgp/packet/symmetrically_encrypted_mdc.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ func (ser seMdcReader) Close() error {
}

func (se *SymmetricallyEncrypted) decryptMdc(c CipherFunction, key []byte) (io.ReadCloser, error) {
keySize := c.KeySize()
if keySize == 0 {
return nil, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(c)))
if !c.IsSupported() {
return nil, errors.UnsupportedError("unsupported cipher: " + strconv.Itoa(int(c)))
}
if len(key) != keySize {

if len(key) != c.KeySize() {
return nil, errors.InvalidArgumentError("SymmetricallyEncrypted: incorrect key length")
}

Expand Down Expand Up @@ -220,6 +220,10 @@ func (c noOpCloser) Close() error {
}

func serializeSymmetricallyEncryptedMdc(ciphertext io.WriteCloser, c CipherFunction, key []byte, config *Config) (Contents io.WriteCloser, err error) {
if !c.IsAes() {
return nil, errors.InvalidArgumentError("invalid mdc cipher function")
}

if c.KeySize() != len(key) {
return nil, errors.InvalidArgumentError("error in mdc serialization: bad key length")
}
Expand Down
Loading

0 comments on commit 16dfff0

Please sign in to comment.