@@ -13,6 +13,7 @@ import (
1313	"bufio" 
1414	"bytes" 
1515	"crypto" 
16+ 	"fmt" 
1617	"hash" 
1718	"io" 
1819	"net/textproto" 
@@ -177,17 +178,18 @@ func Decode(data []byte) (b *Block, rest []byte) {
177178// message. 
178179type  dashEscaper  struct  {
179180	buffered  * bufio.Writer 
180- 	h         hash. Hash 
181+ 	hashers   []hash. Hash   // one per key in privateKeys 
181182	hashType  crypto.Hash 
183+ 	toHash    io.Writer  // writes to all the hashes in hashers 
182184
183185	atBeginningOfLine  bool 
184186	isFirstLine        bool 
185187
186188	whitespace  []byte 
187189	byteBuf     []byte  // a one byte buffer to save allocations 
188190
189- 	privateKey   * packet.PrivateKey 
190- 	config      * packet.Config 
191+ 	privateKeys  [] * packet.PrivateKey 
192+ 	config        * packet.Config 
191193}
192194
193195func  (d  * dashEscaper ) Write (data  []byte ) (n  int , err  error ) {
@@ -198,7 +200,7 @@ func (d *dashEscaper) Write(data []byte) (n int, err error) {
198200			// The final CRLF isn't included in the hash so we have to wait 
199201			// until this point (the start of the next line) before writing it. 
200202			if  ! d .isFirstLine  {
201- 				d .h .Write (crlf )
203+ 				d .toHash .Write (crlf )
202204			}
203205			d .isFirstLine  =  false 
204206		}
@@ -219,12 +221,12 @@ func (d *dashEscaper) Write(data []byte) (n int, err error) {
219221				if  _ , err  =  d .buffered .Write (dashEscape ); err  !=  nil  {
220222					return 
221223				}
222- 				d .h .Write (d .byteBuf )
224+ 				d .toHash .Write (d .byteBuf )
223225				d .atBeginningOfLine  =  false 
224226			} else  if  b  ==  '\n'  {
225227				// Nothing to do because we delay writing CRLF to the hash. 
226228			} else  {
227- 				d .h .Write (d .byteBuf )
229+ 				d .toHash .Write (d .byteBuf )
228230				d .atBeginningOfLine  =  false 
229231			}
230232			if  err  =  d .buffered .WriteByte (b ); err  !=  nil  {
@@ -245,13 +247,13 @@ func (d *dashEscaper) Write(data []byte) (n int, err error) {
245247				// Any buffered whitespace wasn't at the end of the line so 
246248				// we need to write it out. 
247249				if  len (d .whitespace ) >  0  {
248- 					d .h .Write (d .whitespace )
250+ 					d .toHash .Write (d .whitespace )
249251					if  _ , err  =  d .buffered .Write (d .whitespace ); err  !=  nil  {
250252						return 
251253					}
252254					d .whitespace  =  d .whitespace [:0 ]
253255				}
254- 				d .h .Write (d .byteBuf )
256+ 				d .toHash .Write (d .byteBuf )
255257				if  err  =  d .buffered .WriteByte (b ); err  !=  nil  {
256258					return 
257259				}
@@ -269,25 +271,29 @@ func (d *dashEscaper) Close() (err error) {
269271			return 
270272		}
271273	}
272- 	sig  :=  new (packet.Signature )
273- 	sig .SigType  =  packet .SigTypeText 
274- 	sig .PubKeyAlgo  =  d .privateKey .PubKeyAlgo 
275- 	sig .Hash  =  d .hashType 
276- 	sig .CreationTime  =  d .config .Now ()
277- 	sig .IssuerKeyId  =  & d .privateKey .KeyId 
278- 
279- 	if  err  =  sig .Sign (d .h , d .privateKey , d .config ); err  !=  nil  {
280- 		return 
281- 	}
282274
283275	out , err  :=  armor .Encode (d .buffered , "PGP SIGNATURE" , nil )
284276	if  err  !=  nil  {
285277		return 
286278	}
287279
288- 	if  err  =  sig .Serialize (out ); err  !=  nil  {
289- 		return 
280+ 	t  :=  d .config .Now ()
281+ 	for  i , k  :=  range  d .privateKeys  {
282+ 		sig  :=  new (packet.Signature )
283+ 		sig .SigType  =  packet .SigTypeText 
284+ 		sig .PubKeyAlgo  =  k .PubKeyAlgo 
285+ 		sig .Hash  =  d .hashType 
286+ 		sig .CreationTime  =  t 
287+ 		sig .IssuerKeyId  =  & k .KeyId 
288+ 
289+ 		if  err  =  sig .Sign (d .hashers [i ], k , d .config ); err  !=  nil  {
290+ 			return 
291+ 		}
292+ 		if  err  =  sig .Serialize (out ); err  !=  nil  {
293+ 			return 
294+ 		}
290295	}
296+ 
291297	if  err  =  out .Close (); err  !=  nil  {
292298		return 
293299	}
@@ -300,8 +306,17 @@ func (d *dashEscaper) Close() (err error) {
300306// Encode returns a WriteCloser which will clear-sign a message with privateKey 
301307// and write it to w. If config is nil, sensible defaults are used. 
302308func  Encode (w  io.Writer , privateKey  * packet.PrivateKey , config  * packet.Config ) (plaintext  io.WriteCloser , err  error ) {
303- 	if  privateKey .Encrypted  {
304- 		return  nil , errors .InvalidArgumentError ("signing key is encrypted" )
309+ 	return  EncodeMulti (w , []* packet.PrivateKey {privateKey }, config )
310+ }
311+ 
312+ // EncodeMulti returns a WriteCloser which will clear-sign a message with all the 
313+ // private keys indicated and write it to w. If config is nil, sensible defaults 
314+ // are used. 
315+ func  EncodeMulti (w  io.Writer , privateKeys  []* packet.PrivateKey , config  * packet.Config ) (plaintext  io.WriteCloser , err  error ) {
316+ 	for  _ , k  :=  range  privateKeys  {
317+ 		if  k .Encrypted  {
318+ 			return  nil , errors .InvalidArgumentError (fmt .Sprintf ("signing key %s is encrypted" , k .KeyIdString ()))
319+ 		}
305320	}
306321
307322	hashType  :=  config .Hash ()
@@ -313,7 +328,14 @@ func Encode(w io.Writer, privateKey *packet.PrivateKey, config *packet.Config) (
313328	if  ! hashType .Available () {
314329		return  nil , errors .UnsupportedError ("unsupported hash type: "  +  strconv .Itoa (int (hashType )))
315330	}
316- 	h  :=  hashType .New ()
331+ 	var  hashers  []hash.Hash 
332+ 	var  ws  []io.Writer 
333+ 	for  range  privateKeys  {
334+ 		h  :=  hashType .New ()
335+ 		hashers  =  append (hashers , h )
336+ 		ws  =  append (ws , h )
337+ 	}
338+ 	toHash  :=  io .MultiWriter (ws ... )
317339
318340	buffered  :=  bufio .NewWriter (w )
319341	// start has a \n at the beginning that we don't want here. 
@@ -338,16 +360,17 @@ func Encode(w io.Writer, privateKey *packet.PrivateKey, config *packet.Config) (
338360
339361	plaintext  =  & dashEscaper {
340362		buffered : buffered ,
341- 		h :         h ,
363+ 		hashers :  hashers ,
342364		hashType : hashType ,
365+ 		toHash :   toHash ,
343366
344367		atBeginningOfLine : true ,
345368		isFirstLine :       true ,
346369
347370		byteBuf : make ([]byte , 1 ),
348371
349- 		privateKey :  privateKey ,
350- 		config :     config ,
372+ 		privateKeys :  privateKeys ,
373+ 		config :       config ,
351374	}
352375
353376	return 
0 commit comments