1
2
3
4
5 package openpgp
6
7 import (
8 "crypto"
9 "hash"
10 "io"
11 "strconv"
12 "time"
13
14 "golang.org/x/crypto/openpgp/armor"
15 "golang.org/x/crypto/openpgp/errors"
16 "golang.org/x/crypto/openpgp/packet"
17 "golang.org/x/crypto/openpgp/s2k"
18 )
19
20
21
22
23 func DetachSign(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error {
24 return detachSign(w, signer, message, packet.SigTypeBinary, config)
25 }
26
27
28
29
30 func ArmoredDetachSign(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) (err error) {
31 return armoredDetachSign(w, signer, message, packet.SigTypeBinary, config)
32 }
33
34
35
36
37
38 func DetachSignText(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error {
39 return detachSign(w, signer, message, packet.SigTypeText, config)
40 }
41
42
43
44
45
46 func ArmoredDetachSignText(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error {
47 return armoredDetachSign(w, signer, message, packet.SigTypeText, config)
48 }
49
50 func armoredDetachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) {
51 out, err := armor.Encode(w, SignatureType, nil)
52 if err != nil {
53 return
54 }
55 err = detachSign(out, signer, message, sigType, config)
56 if err != nil {
57 return
58 }
59 return out.Close()
60 }
61
62 func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) {
63 if signer.PrivateKey == nil {
64 return errors.InvalidArgumentError("signing key doesn't have a private key")
65 }
66 if signer.PrivateKey.Encrypted {
67 return errors.InvalidArgumentError("signing key is encrypted")
68 }
69
70 sig := new(packet.Signature)
71 sig.SigType = sigType
72 sig.PubKeyAlgo = signer.PrivateKey.PubKeyAlgo
73 sig.Hash = config.Hash()
74 sig.CreationTime = config.Now()
75 sig.IssuerKeyId = &signer.PrivateKey.KeyId
76
77 h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType)
78 if err != nil {
79 return
80 }
81 io.Copy(wrappedHash, message)
82
83 err = sig.Sign(h, signer.PrivateKey, config)
84 if err != nil {
85 return
86 }
87
88 return sig.Serialize(w)
89 }
90
91
92
93 type FileHints struct {
94
95 IsBinary bool
96
97
98
99
100 FileName string
101
102 ModTime time.Time
103 }
104
105
106
107
108
109 func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) {
110 if hints == nil {
111 hints = &FileHints{}
112 }
113
114 key, err := packet.SerializeSymmetricKeyEncrypted(ciphertext, passphrase, config)
115 if err != nil {
116 return
117 }
118 w, err := packet.SerializeSymmetricallyEncrypted(ciphertext, config.Cipher(), key, config)
119 if err != nil {
120 return
121 }
122
123 literaldata := w
124 if algo := config.Compression(); algo != packet.CompressionNone {
125 var compConfig *packet.CompressionConfig
126 if config != nil {
127 compConfig = config.CompressionConfig
128 }
129 literaldata, err = packet.SerializeCompressed(w, algo, compConfig)
130 if err != nil {
131 return
132 }
133 }
134
135 var epochSeconds uint32
136 if !hints.ModTime.IsZero() {
137 epochSeconds = uint32(hints.ModTime.Unix())
138 }
139 return packet.SerializeLiteral(literaldata, hints.IsBinary, hints.FileName, epochSeconds)
140 }
141
142
143
144 func intersectPreferences(a []uint8, b []uint8) (intersection []uint8) {
145 var j int
146 for _, v := range a {
147 for _, v2 := range b {
148 if v == v2 {
149 a[j] = v
150 j++
151 break
152 }
153 }
154 }
155
156 return a[:j]
157 }
158
159 func hashToHashId(h crypto.Hash) uint8 {
160 v, ok := s2k.HashToHashId(h)
161 if !ok {
162 panic("tried to convert unknown hash")
163 }
164 return v
165 }
166
167
168
169
170
171
172 func writeAndSign(payload io.WriteCloser, candidateHashes []uint8, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) {
173 var signer *packet.PrivateKey
174 if signed != nil {
175 signKey, ok := signed.signingKey(config.Now())
176 if !ok {
177 return nil, errors.InvalidArgumentError("no valid signing keys")
178 }
179 signer = signKey.PrivateKey
180 if signer == nil {
181 return nil, errors.InvalidArgumentError("no private key in signing key")
182 }
183 if signer.Encrypted {
184 return nil, errors.InvalidArgumentError("signing key must be decrypted")
185 }
186 }
187
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
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
235
236
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
256
257
258
259
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
265
266 candidateCiphers := []uint8{
267 uint8(packet.CipherAES128),
268 uint8(packet.CipherAES256),
269 uint8(packet.CipherCAST5),
270 }
271
272 candidateHashes := []uint8{
273 hashToHashId(crypto.SHA256),
274 hashToHashId(crypto.SHA384),
275 hashToHashId(crypto.SHA512),
276 hashToHashId(crypto.SHA1),
277 hashToHashId(crypto.RIPEMD160),
278 }
279
280
281
282 defaultCiphers := candidateCiphers[len(candidateCiphers)-1:]
283 defaultHashes := candidateHashes[len(candidateHashes)-1:]
284
285 encryptKeys := make([]Key, len(to))
286 for i := range to {
287 var ok bool
288 encryptKeys[i], ok = to[i].encryptionKey(config.Now())
289 if !ok {
290 return nil, errors.InvalidArgumentError("cannot encrypt a message to key id " + strconv.FormatUint(to[i].PrimaryKey.KeyId, 16) + " because it has no encryption keys")
291 }
292
293 sig := to[i].primaryIdentity().SelfSignature
294
295 preferredSymmetric := sig.PreferredSymmetric
296 if len(preferredSymmetric) == 0 {
297 preferredSymmetric = defaultCiphers
298 }
299 preferredHashes := sig.PreferredHash
300 if len(preferredHashes) == 0 {
301 preferredHashes = defaultHashes
302 }
303 candidateCiphers = intersectPreferences(candidateCiphers, preferredSymmetric)
304 candidateHashes = intersectPreferences(candidateHashes, preferredHashes)
305 }
306
307 if len(candidateCiphers) == 0 || len(candidateHashes) == 0 {
308 return nil, errors.InvalidArgumentError("cannot encrypt because recipient set shares no common algorithms")
309 }
310
311 cipher := packet.CipherFunction(candidateCiphers[0])
312
313 configuredCipher := config.Cipher()
314 for _, c := range candidateCiphers {
315 cipherFunc := packet.CipherFunction(c)
316 if cipherFunc == configuredCipher {
317 cipher = cipherFunc
318 break
319 }
320 }
321
322 symKey := make([]byte, cipher.KeySize())
323 if _, err := io.ReadFull(config.Random(), symKey); err != nil {
324 return nil, err
325 }
326
327 for _, key := range encryptKeys {
328 if err := packet.SerializeEncryptedKey(ciphertext, key.PublicKey, cipher, symKey, config); err != nil {
329 return nil, err
330 }
331 }
332
333 payload, err := packet.SerializeSymmetricallyEncrypted(ciphertext, cipher, symKey, config)
334 if err != nil {
335 return
336 }
337
338 return writeAndSign(payload, candidateHashes, signed, hints, config)
339 }
340
341
342
343
344
345 func Sign(output io.Writer, signed *Entity, hints *FileHints, config *packet.Config) (input io.WriteCloser, err error) {
346 if signed == nil {
347 return nil, errors.InvalidArgumentError("no signer provided")
348 }
349
350
351 candidateHashes := []uint8{
352 hashToHashId(crypto.SHA256),
353 hashToHashId(crypto.SHA384),
354 hashToHashId(crypto.SHA512),
355 hashToHashId(crypto.SHA1),
356 hashToHashId(crypto.RIPEMD160),
357 }
358 defaultHashes := candidateHashes[len(candidateHashes)-1:]
359 preferredHashes := signed.primaryIdentity().SelfSignature.PreferredHash
360 if len(preferredHashes) == 0 {
361 preferredHashes = defaultHashes
362 }
363 candidateHashes = intersectPreferences(candidateHashes, preferredHashes)
364 return writeAndSign(noOpCloser{output}, candidateHashes, signed, hints, config)
365 }
366
367
368
369
370 type signatureWriter struct {
371 encryptedData io.WriteCloser
372 literalData io.WriteCloser
373 hashType crypto.Hash
374 h hash.Hash
375 signer *packet.PrivateKey
376 config *packet.Config
377 }
378
379 func (s signatureWriter) Write(data []byte) (int, error) {
380 s.h.Write(data)
381 return s.literalData.Write(data)
382 }
383
384 func (s signatureWriter) Close() error {
385 sig := &packet.Signature{
386 SigType: packet.SigTypeBinary,
387 PubKeyAlgo: s.signer.PubKeyAlgo,
388 Hash: s.hashType,
389 CreationTime: s.config.Now(),
390 IssuerKeyId: &s.signer.KeyId,
391 }
392
393 if err := sig.Sign(s.h, s.signer, s.config); err != nil {
394 return err
395 }
396 if err := s.literalData.Close(); err != nil {
397 return err
398 }
399 if err := sig.Serialize(s.encryptedData); err != nil {
400 return err
401 }
402 return s.encryptedData.Close()
403 }
404
405
406
407
408 type noOpCloser struct {
409 w io.Writer
410 }
411
412 func (c noOpCloser) Write(data []byte) (n int, err error) {
413 return c.w.Write(data)
414 }
415
416 func (c noOpCloser) Close() error {
417 return nil
418 }
419
View as plain text