@@ -253,34 +253,12 @@ func writeAndSign(payload io.WriteCloser, candidateHashes []uint8, signed *Entit
253
253
}
254
254
255
255
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
-
282
256
var salt []byte
283
257
if signer != nil {
258
+ if hash , err = selectHash (candidateHashes , config .Hash (), signer ); err != nil {
259
+ return nil , err
260
+ }
261
+
284
262
var opsVersion = 3
285
263
if signer .Version == 6 {
286
264
opsVersion = signer .Version
@@ -558,13 +536,34 @@ func (s signatureWriter) Close() error {
558
536
return s .encryptedData .Close ()
559
537
}
560
538
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
+
561
559
func createSignaturePacket (signer * packet.PublicKey , sigType packet.SignatureType , config * packet.Config ) * packet.Signature {
562
560
sigLifetimeSecs := config .SigLifetime ()
561
+ hash := selectHashForSigningKey (config , signer )
563
562
return & packet.Signature {
564
563
Version : signer .Version ,
565
564
SigType : sigType ,
566
565
PubKeyAlgo : signer .PubKeyAlgo ,
567
- Hash : config . Hash () ,
566
+ Hash : hash ,
568
567
CreationTime : config .Now (),
569
568
IssuerKeyId : & signer .KeyId ,
570
569
IssuerFingerprint : signer .Fingerprint ,
@@ -618,3 +617,74 @@ func handleCompression(compressed io.WriteCloser, candidateCompression []uint8,
618
617
}
619
618
return data , nil
620
619
}
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