@@ -206,7 +206,7 @@ func Decode(data []byte) (b *Block, rest []byte) {
206
206
type dashEscaper struct {
207
207
buffered * bufio.Writer
208
208
hashers []hash.Hash // one per key in privateKeys
209
- hashType crypto.Hash
209
+ hashTypes [] crypto.Hash
210
210
toHash io.Writer // writes to all the hashes in hashers
211
211
salts [][]byte // salts for the signatures if v6
212
212
armorHeader map [string ]string // Armor headers
@@ -328,7 +328,7 @@ func (d *dashEscaper) Close() (err error) {
328
328
sig .Version = k .Version
329
329
sig .SigType = packet .SigTypeText
330
330
sig .PubKeyAlgo = k .PubKeyAlgo
331
- sig .Hash = d .hashType
331
+ sig .Hash = d .hashTypes [ i ]
332
332
sig .CreationTime = t
333
333
sig .IssuerKeyId = & k .KeyId
334
334
sig .IssuerFingerprint = k .Fingerprint
@@ -390,19 +390,22 @@ func EncodeMultiWithHeader(w io.Writer, privateKeys []*packet.PrivateKey, config
390
390
}
391
391
392
392
hashType := config .Hash ()
393
- name := nameOfHash (hashType )
394
- if len (name ) == 0 {
395
- return nil , errors .UnsupportedError ("unknown hash type: " + strconv .Itoa (int (hashType )))
396
- }
397
393
398
- if ! hashType .Available () {
399
- return nil , errors .UnsupportedError ("unsupported hash type: " + strconv .Itoa (int (hashType )))
400
- }
401
394
var hashers []hash.Hash
395
+ var hashTypes []crypto.Hash
402
396
var ws []io.Writer
403
397
var salts [][]byte
404
398
for _ , sk := range privateKeys {
405
- h := hashType .New ()
399
+ acceptedHashes := acceptableHashesToWrite (& sk .PublicKey )
400
+ // acceptedHashes contains at least one hash
401
+ selectedHashType := acceptedHashes [0 ]
402
+ for _ , acceptedHash := range acceptedHashes {
403
+ if hashType == acceptedHash {
404
+ selectedHashType = hashType
405
+ break
406
+ }
407
+ }
408
+ h := selectedHashType .New ()
406
409
if sk .Version == 6 {
407
410
// generate salt
408
411
var salt []byte
@@ -416,6 +419,7 @@ func EncodeMultiWithHeader(w io.Writer, privateKeys []*packet.PrivateKey, config
416
419
salts = append (salts , salt )
417
420
}
418
421
hashers = append (hashers , h )
422
+ hashTypes = append (hashTypes , selectedHashType )
419
423
ws = append (ws , h )
420
424
}
421
425
toHash := io .MultiWriter (ws ... )
@@ -432,11 +436,8 @@ func EncodeMultiWithHeader(w io.Writer, privateKeys []*packet.PrivateKey, config
432
436
nonV6 := len (salts ) < len (hashers )
433
437
// Crypto refresh: Headers SHOULD NOT be emitted
434
438
if nonV6 { // Emit header if non v6 signatures are present for compatibility
435
- if _ , err = buffered .WriteString (fmt .Sprintf ("%s: %s" , hashHeader , name )); err != nil {
436
- return
437
- }
438
- if err = buffered .WriteByte (lf ); err != nil {
439
- return
439
+ if err := writeHashHeader (buffered , hashTypes ); err != nil {
440
+ return nil , err
440
441
}
441
442
}
442
443
if err = buffered .WriteByte (lf ); err != nil {
@@ -446,7 +447,7 @@ func EncodeMultiWithHeader(w io.Writer, privateKeys []*packet.PrivateKey, config
446
447
plaintext = & dashEscaper {
447
448
buffered : buffered ,
448
449
hashers : hashers ,
449
- hashType : hashType ,
450
+ hashTypes : hashTypes ,
450
451
toHash : toHash ,
451
452
salts : salts ,
452
453
armorHeader : headers ,
@@ -470,6 +471,40 @@ func (b *Block) VerifySignature(keyring openpgp.KeyRing, config *packet.Config)
470
471
return
471
472
}
472
473
474
+ // writeHashHeader writes the legacy cleartext hash header to buffered.
475
+ func writeHashHeader (buffered * bufio.Writer , hashTypes []crypto.Hash ) error {
476
+ seen := make (map [string ]bool , len (hashTypes ))
477
+ if _ , err := buffered .WriteString (fmt .Sprintf ("%s: " , hashHeader )); err != nil {
478
+ return err
479
+ }
480
+
481
+ for index , sigHashType := range hashTypes {
482
+ first := index == 0
483
+ name := nameOfHash (sigHashType )
484
+ if len (name ) == 0 {
485
+ return errors .UnsupportedError ("unknown hash type: " + strconv .Itoa (int (sigHashType )))
486
+ }
487
+
488
+ switch {
489
+ case ! seen [name ] && first :
490
+ if _ , err := buffered .WriteString (name ); err != nil {
491
+ return err
492
+ }
493
+ case ! seen [name ]:
494
+ if _ , err := buffered .WriteString (fmt .Sprintf (",%s" , name )); err != nil {
495
+ return err
496
+ }
497
+ }
498
+ seen [name ] = true
499
+ }
500
+
501
+ if err := buffered .WriteByte (lf ); err != nil {
502
+ return err
503
+ }
504
+
505
+ return nil
506
+ }
507
+
473
508
// nameOfHash returns the OpenPGP name for the given hash, or the empty string
474
509
// if the name isn't known. See RFC 4880, section 9.4.
475
510
func nameOfHash (h crypto.Hash ) string {
@@ -489,3 +524,38 @@ func nameOfHash(h crypto.Hash) string {
489
524
}
490
525
return ""
491
526
}
527
+
528
+ func acceptableHashesToWrite (singingKey * packet.PublicKey ) []crypto.Hash {
529
+ switch singingKey .PubKeyAlgo {
530
+ case packet .PubKeyAlgoEd448 :
531
+ return []crypto.Hash {
532
+ crypto .SHA512 ,
533
+ crypto .SHA3_512 ,
534
+ }
535
+ case packet .PubKeyAlgoECDSA , packet .PubKeyAlgoEdDSA :
536
+ if curve , err := singingKey .Curve (); err == nil {
537
+ if curve == packet .Curve448 ||
538
+ curve == packet .CurveNistP521 ||
539
+ curve == packet .CurveBrainpoolP512 {
540
+ return []crypto.Hash {
541
+ crypto .SHA512 ,
542
+ crypto .SHA3_512 ,
543
+ }
544
+ } else if curve == packet .CurveBrainpoolP384 ||
545
+ curve == packet .CurveNistP384 {
546
+ return []crypto.Hash {
547
+ crypto .SHA384 ,
548
+ crypto .SHA512 ,
549
+ crypto .SHA3_512 ,
550
+ }
551
+ }
552
+ }
553
+ }
554
+ return []crypto.Hash {
555
+ crypto .SHA256 ,
556
+ crypto .SHA384 ,
557
+ crypto .SHA512 ,
558
+ crypto .SHA3_256 ,
559
+ crypto .SHA3_512 ,
560
+ }
561
+ }
0 commit comments