Skip to content

Commit a9af95c

Browse files
committed
feat(v1): Only allow acceptable hashes when writing signatures
1 parent 3c60603 commit a9af95c

File tree

2 files changed

+101
-32
lines changed

2 files changed

+101
-32
lines changed

openpgp/v2/write.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -880,7 +880,7 @@ func (s signatureWriter) Close() error {
880880
return s.encryptedData.Close()
881881
}
882882

883-
func adaptHashToSigningKey(config *packet.Config, primary *packet.PublicKey) crypto.Hash {
883+
func selectHashForSigningKey(config *packet.Config, primary *packet.PublicKey) crypto.Hash {
884884
acceptableHashes := acceptableHashesToWrite(primary)
885885
hash, ok := algorithm.HashToHashId(config.Hash())
886886
if !ok {
@@ -893,17 +893,16 @@ func adaptHashToSigningKey(config *packet.Config, primary *packet.PublicKey) cry
893893
}
894894
if len(acceptableHashes) > 0 {
895895
defaultAcceptedHash, ok := algorithm.HashIdToHash(acceptableHashes[0])
896-
if !ok {
897-
return config.Hash()
896+
if ok {
897+
return defaultAcceptedHash
898898
}
899-
return defaultAcceptedHash
900899
}
901900
return config.Hash()
902901
}
903902

904903
func createSignaturePacket(signer *packet.PublicKey, sigType packet.SignatureType, config *packet.Config) *packet.Signature {
905904
sigLifetimeSecs := config.SigLifetime()
906-
hash := adaptHashToSigningKey(config, signer)
905+
hash := selectHashForSigningKey(config, signer)
907906
return &packet.Signature{
908907
Version: signer.Version,
909908
SigType: sigType,

openpgp/write.go

Lines changed: 97 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -253,34 +253,12 @@ func writeAndSign(payload io.WriteCloser, candidateHashes []uint8, signed *Entit
253253
}
254254

255255
var hash crypto.Hash
256-
for _, hashId := range candidateHashes {
257-
if h, ok := algorithm.HashIdToHash(hashId); ok && h.Available() {
258-
hash = h
259-
break
260-
}
261-
}
262-
263-
// If the hash specified by config is a candidate, we'll use that.
264-
if configuredHash := config.Hash(); configuredHash.Available() {
265-
for _, hashId := range candidateHashes {
266-
if h, ok := algorithm.HashIdToHash(hashId); ok && h == configuredHash {
267-
hash = h
268-
break
269-
}
270-
}
271-
}
272-
273-
if hash == 0 {
274-
hashId := candidateHashes[0]
275-
name, ok := algorithm.HashIdToString(hashId)
276-
if !ok {
277-
name = "#" + strconv.Itoa(int(hashId))
278-
}
279-
return nil, errors.InvalidArgumentError("cannot encrypt because no candidate hash functions are compiled in. (Wanted " + name + " in this case.)")
280-
}
281-
282256
var salt []byte
283257
if signer != nil {
258+
if hash, err = selectHash(candidateHashes, config.Hash(), signer); err != nil {
259+
return nil, err
260+
}
261+
284262
var opsVersion = 3
285263
if signer.Version == 6 {
286264
opsVersion = signer.Version
@@ -558,13 +536,34 @@ func (s signatureWriter) Close() error {
558536
return s.encryptedData.Close()
559537
}
560538

539+
func selectHashForSigningKey(config *packet.Config, signer *packet.PublicKey) crypto.Hash {
540+
acceptableHashes := acceptableHashesToWrite(signer)
541+
hash, ok := algorithm.HashToHashId(config.Hash())
542+
if !ok {
543+
return config.Hash()
544+
}
545+
for _, acceptableHashes := range acceptableHashes {
546+
if acceptableHashes == hash {
547+
return config.Hash()
548+
}
549+
}
550+
if len(acceptableHashes) > 0 {
551+
defaultAcceptedHash, ok := algorithm.HashIdToHash(acceptableHashes[0])
552+
if ok {
553+
return defaultAcceptedHash
554+
}
555+
}
556+
return config.Hash()
557+
}
558+
561559
func createSignaturePacket(signer *packet.PublicKey, sigType packet.SignatureType, config *packet.Config) *packet.Signature {
562560
sigLifetimeSecs := config.SigLifetime()
561+
hash := selectHashForSigningKey(config, signer)
563562
return &packet.Signature{
564563
Version: signer.Version,
565564
SigType: sigType,
566565
PubKeyAlgo: signer.PubKeyAlgo,
567-
Hash: config.Hash(),
566+
Hash: hash,
568567
CreationTime: config.Now(),
569568
IssuerKeyId: &signer.KeyId,
570569
IssuerFingerprint: signer.Fingerprint,
@@ -618,3 +617,74 @@ func handleCompression(compressed io.WriteCloser, candidateCompression []uint8,
618617
}
619618
return data, nil
620619
}
620+
621+
// selectHash selects the preferred hash given the candidateHashes and the configuredHash
622+
func selectHash(candidateHashes []byte, configuredHash crypto.Hash, signer *packet.PrivateKey) (hash crypto.Hash, err error) {
623+
acceptableHashes := acceptableHashesToWrite(&signer.PublicKey)
624+
candidateHashes = intersectPreferences(acceptableHashes, candidateHashes)
625+
626+
for _, hashId := range candidateHashes {
627+
if h, ok := algorithm.HashIdToHash(hashId); ok && h.Available() {
628+
hash = h
629+
break
630+
}
631+
}
632+
633+
// If the hash specified by config is a candidate, we'll use that.
634+
if configuredHash.Available() {
635+
for _, hashId := range candidateHashes {
636+
if h, ok := algorithm.HashIdToHash(hashId); ok && h == configuredHash {
637+
hash = h
638+
break
639+
}
640+
}
641+
}
642+
643+
if hash == 0 {
644+
if len(acceptableHashes) > 0 {
645+
if h, ok := algorithm.HashIdToHash(acceptableHashes[0]); ok {
646+
hash = h
647+
} else {
648+
return 0, errors.UnsupportedError("no candidate hash functions are compiled in.")
649+
}
650+
} else {
651+
return 0, errors.UnsupportedError("no candidate hash functions are compiled in.")
652+
}
653+
}
654+
return
655+
}
656+
657+
func acceptableHashesToWrite(singingKey *packet.PublicKey) []uint8 {
658+
switch singingKey.PubKeyAlgo {
659+
case packet.PubKeyAlgoEd448:
660+
return []uint8{
661+
hashToHashId(crypto.SHA512),
662+
hashToHashId(crypto.SHA3_512),
663+
}
664+
case packet.PubKeyAlgoECDSA, packet.PubKeyAlgoEdDSA:
665+
if curve, err := singingKey.Curve(); err == nil {
666+
if curve == packet.Curve448 ||
667+
curve == packet.CurveNistP521 ||
668+
curve == packet.CurveBrainpoolP512 {
669+
return []uint8{
670+
hashToHashId(crypto.SHA512),
671+
hashToHashId(crypto.SHA3_512),
672+
}
673+
} else if curve == packet.CurveBrainpoolP384 ||
674+
curve == packet.CurveNistP384 {
675+
return []uint8{
676+
hashToHashId(crypto.SHA384),
677+
hashToHashId(crypto.SHA512),
678+
hashToHashId(crypto.SHA3_512),
679+
}
680+
}
681+
}
682+
}
683+
return []uint8{
684+
hashToHashId(crypto.SHA256),
685+
hashToHashId(crypto.SHA384),
686+
hashToHashId(crypto.SHA512),
687+
hashToHashId(crypto.SHA3_256),
688+
hashToHashId(crypto.SHA3_512),
689+
}
690+
}

0 commit comments

Comments
 (0)