@@ -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+
561559func 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