@@ -164,12 +164,12 @@ func hashToHashId(h crypto.Hash) uint8 {
164164 return v
165165}
166166
167- // Encrypt encrypts a message to a number of recipients and, optionally, signs
168- // it. hints contains optional information, that is also encrypted, that aids
169- // the recipients in processing the message. The resulting WriteCloser must
170- // be closed after the contents of the file have been written.
171- // If config is nil, sensible defaults will be used.
172- func Encrypt ( ciphertext io.Writer , to []* Entity , signed * Entity , hints * FileHints , config * packet.Config ) (plaintext io.WriteCloser , err error ) {
167+ // writeAndSign writes the data as a payload package and, optionally, signs
168+ // it. hints contains optional information, that is also encrypted,
169+ // that aids the recipients in processing the message. The resulting
170+ // WriteCloser must be closed after the contents of the file have been
171+ // written. If config is nil, sensible defaults will be used.
172+ func writeAndSign ( payload io.WriteCloser , candidateHashes []uint8 , signed * Entity , hints * FileHints , config * packet.Config ) (plaintext io.WriteCloser , err error ) {
173173 var signer * packet.PrivateKey
174174 if signed != nil {
175175 signKey , ok := signed .signingKey (config .Now ())
@@ -185,6 +185,83 @@ func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHint
185185 }
186186 }
187187
188+ var hash crypto.Hash
189+ for _ , hashId := range candidateHashes {
190+ if h , ok := s2k .HashIdToHash (hashId ); ok && h .Available () {
191+ hash = h
192+ break
193+ }
194+ }
195+
196+ // If the hash specified by config is a candidate, we'll use that.
197+ if configuredHash := config .Hash (); configuredHash .Available () {
198+ for _ , hashId := range candidateHashes {
199+ if h , ok := s2k .HashIdToHash (hashId ); ok && h == configuredHash {
200+ hash = h
201+ break
202+ }
203+ }
204+ }
205+
206+ if hash == 0 {
207+ hashId := candidateHashes [0 ]
208+ name , ok := s2k .HashIdToString (hashId )
209+ if ! ok {
210+ name = "#" + strconv .Itoa (int (hashId ))
211+ }
212+ return nil , errors .InvalidArgumentError ("cannot encrypt because no candidate hash functions are compiled in. (Wanted " + name + " in this case.)" )
213+ }
214+
215+ if signer != nil {
216+ ops := & packet.OnePassSignature {
217+ SigType : packet .SigTypeBinary ,
218+ Hash : hash ,
219+ PubKeyAlgo : signer .PubKeyAlgo ,
220+ KeyId : signer .KeyId ,
221+ IsLast : true ,
222+ }
223+ if err := ops .Serialize (payload ); err != nil {
224+ return nil , err
225+ }
226+ }
227+
228+ if hints == nil {
229+ hints = & FileHints {}
230+ }
231+
232+ w := payload
233+ if signer != nil {
234+ // If we need to write a signature packet after the literal
235+ // data then we need to stop literalData from closing
236+ // encryptedData.
237+ w = noOpCloser {w }
238+
239+ }
240+ var epochSeconds uint32
241+ if ! hints .ModTime .IsZero () {
242+ epochSeconds = uint32 (hints .ModTime .Unix ())
243+ }
244+ literalData , err := packet .SerializeLiteral (w , hints .IsBinary , hints .FileName , epochSeconds )
245+ if err != nil {
246+ return nil , err
247+ }
248+
249+ if signer != nil {
250+ return signatureWriter {payload , literalData , hash , hash .New (), signer , config }, nil
251+ }
252+ return literalData , nil
253+ }
254+
255+ // Encrypt encrypts a message to a number of recipients and, optionally, signs
256+ // it. hints contains optional information, that is also encrypted, that aids
257+ // the recipients in processing the message. The resulting WriteCloser must
258+ // be closed after the contents of the file have been written.
259+ // If config is nil, sensible defaults will be used.
260+ func Encrypt (ciphertext io.Writer , to []* Entity , signed * Entity , hints * FileHints , config * packet.Config ) (plaintext io.WriteCloser , err error ) {
261+ if len (to ) == 0 {
262+ return nil , errors .InvalidArgumentError ("no encryption recipient provided" )
263+ }
264+
188265 // These are the possible ciphers that we'll use for the message.
189266 candidateCiphers := []uint8 {
190267 uint8 (packet .CipherAES128 ),
@@ -241,33 +318,6 @@ func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHint
241318 }
242319 }
243320
244- var hash crypto.Hash
245- for _ , hashId := range candidateHashes {
246- if h , ok := s2k .HashIdToHash (hashId ); ok && h .Available () {
247- hash = h
248- break
249- }
250- }
251-
252- // If the hash specified by config is a candidate, we'll use that.
253- if configuredHash := config .Hash (); configuredHash .Available () {
254- for _ , hashId := range candidateHashes {
255- if h , ok := s2k .HashIdToHash (hashId ); ok && h == configuredHash {
256- hash = h
257- break
258- }
259- }
260- }
261-
262- if hash == 0 {
263- hashId := candidateHashes [0 ]
264- name , ok := s2k .HashIdToString (hashId )
265- if ! ok {
266- name = "#" + strconv .Itoa (int (hashId ))
267- }
268- return nil , errors .InvalidArgumentError ("cannot encrypt because no candidate hash functions are compiled in. (Wanted " + name + " in this case.)" )
269- }
270-
271321 symKey := make ([]byte , cipher .KeySize ())
272322 if _ , err := io .ReadFull (config .Random (), symKey ); err != nil {
273323 return nil , err
@@ -279,49 +329,37 @@ func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHint
279329 }
280330 }
281331
282- encryptedData , err := packet .SerializeSymmetricallyEncrypted (ciphertext , cipher , symKey , config )
332+ payload , err := packet .SerializeSymmetricallyEncrypted (ciphertext , cipher , symKey , config )
283333 if err != nil {
284334 return
285335 }
286336
287- if signer != nil {
288- ops := & packet.OnePassSignature {
289- SigType : packet .SigTypeBinary ,
290- Hash : hash ,
291- PubKeyAlgo : signer .PubKeyAlgo ,
292- KeyId : signer .KeyId ,
293- IsLast : true ,
294- }
295- if err := ops .Serialize (encryptedData ); err != nil {
296- return nil , err
297- }
298- }
337+ return writeAndSign (payload , candidateHashes , signed , hints , config )
338+ }
299339
300- if hints == nil {
301- hints = & FileHints {}
340+ // Sign signs a message. The resulting WriteCloser must be closed after the
341+ // contents of the file have been written. hints contains optional information
342+ // that aids the recipients in processing the message.
343+ // If config is nil, sensible defaults will be used.
344+ func Sign (output io.Writer , signed * Entity , hints * FileHints , config * packet.Config ) (input io.WriteCloser , err error ) {
345+ if signed == nil {
346+ return nil , errors .InvalidArgumentError ("no signer provided" )
302347 }
303348
304- w := encryptedData
305- if signer != nil {
306- // If we need to write a signature packet after the literal
307- // data then we need to stop literalData from closing
308- // encryptedData.
309- w = noOpCloser {encryptedData }
310-
311- }
312- var epochSeconds uint32
313- if ! hints .ModTime .IsZero () {
314- epochSeconds = uint32 (hints .ModTime .Unix ())
315- }
316- literalData , err := packet .SerializeLiteral (w , hints .IsBinary , hints .FileName , epochSeconds )
317- if err != nil {
318- return nil , err
349+ // These are the possible hash functions that we'll use for the signature.
350+ candidateHashes := []uint8 {
351+ hashToHashId (crypto .SHA256 ),
352+ hashToHashId (crypto .SHA512 ),
353+ hashToHashId (crypto .SHA1 ),
354+ hashToHashId (crypto .RIPEMD160 ),
319355 }
320-
321- if signer != nil {
322- return signatureWriter {encryptedData , literalData , hash , hash .New (), signer , config }, nil
356+ defaultHashes := candidateHashes [len (candidateHashes )- 1 :]
357+ preferredHashes := signed .primaryIdentity ().SelfSignature .PreferredHash
358+ if len (preferredHashes ) == 0 {
359+ preferredHashes = defaultHashes
323360 }
324- return literalData , nil
361+ candidateHashes = intersectPreferences (candidateHashes , preferredHashes )
362+ return writeAndSign (noOpCloser {output }, candidateHashes , signed , hints , config )
325363}
326364
327365// signatureWriter hashes the contents of a message while passing it along to
0 commit comments