You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

write.go 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. // Copyright 2011 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package openpgp
  5. import (
  6. "crypto"
  7. "hash"
  8. "io"
  9. "strconv"
  10. "time"
  11. "github.com/keybase/go-crypto/openpgp/armor"
  12. "github.com/keybase/go-crypto/openpgp/errors"
  13. "github.com/keybase/go-crypto/openpgp/packet"
  14. "github.com/keybase/go-crypto/openpgp/s2k"
  15. )
  16. // DetachSign signs message with the private key from signer (which must
  17. // already have been decrypted) and writes the signature to w.
  18. // If config is nil, sensible defaults will be used.
  19. func DetachSign(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error {
  20. return detachSign(w, signer, message, packet.SigTypeBinary, config)
  21. }
  22. // ArmoredDetachSign signs message with the private key from signer (which
  23. // must already have been decrypted) and writes an armored signature to w.
  24. // If config is nil, sensible defaults will be used.
  25. func ArmoredDetachSign(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) (err error) {
  26. return armoredDetachSign(w, signer, message, packet.SigTypeBinary, config)
  27. }
  28. // DetachSignText signs message (after canonicalising the line endings) with
  29. // the private key from signer (which must already have been decrypted) and
  30. // writes the signature to w.
  31. // If config is nil, sensible defaults will be used.
  32. func DetachSignText(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error {
  33. return detachSign(w, signer, message, packet.SigTypeText, config)
  34. }
  35. // ArmoredDetachSignText signs message (after canonicalising the line endings)
  36. // with the private key from signer (which must already have been decrypted)
  37. // and writes an armored signature to w.
  38. // If config is nil, sensible defaults will be used.
  39. func ArmoredDetachSignText(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error {
  40. return armoredDetachSign(w, signer, message, packet.SigTypeText, config)
  41. }
  42. func armoredDetachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) {
  43. out, err := armor.Encode(w, SignatureType, nil)
  44. if err != nil {
  45. return
  46. }
  47. err = detachSign(out, signer, message, sigType, config)
  48. if err != nil {
  49. return
  50. }
  51. return out.Close()
  52. }
  53. // SignWithSigner signs the message of type sigType with s and writes the
  54. // signature to w.
  55. // If config is nil, sensible defaults will be used.
  56. func SignWithSigner(s packet.Signer, w io.Writer, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) {
  57. keyId := s.KeyId()
  58. sig := new(packet.Signature)
  59. sig.SigType = sigType
  60. sig.PubKeyAlgo = s.PublicKeyAlgo()
  61. sig.Hash = config.Hash()
  62. sig.CreationTime = config.Now()
  63. sig.IssuerKeyId = &keyId
  64. s.Reset()
  65. wrapped := s.(hash.Hash)
  66. if sigType == packet.SigTypeText {
  67. wrapped = NewCanonicalTextHash(s)
  68. }
  69. io.Copy(wrapped, message)
  70. err = sig.Sign(s, nil, config)
  71. if err != nil {
  72. return
  73. }
  74. err = sig.Serialize(w)
  75. return
  76. }
  77. func detachSign(w io.Writer, signer *Entity, message io.Reader, sigType packet.SignatureType, config *packet.Config) (err error) {
  78. signerSubkey, ok := signer.signingKey(config.Now())
  79. if !ok {
  80. err = errors.InvalidArgumentError("no valid signing keys")
  81. return
  82. }
  83. if signerSubkey.PrivateKey == nil {
  84. return errors.InvalidArgumentError("signing key doesn't have a private key")
  85. }
  86. if signerSubkey.PrivateKey.Encrypted {
  87. return errors.InvalidArgumentError("signing key is encrypted")
  88. }
  89. sig := new(packet.Signature)
  90. sig.SigType = sigType
  91. sig.PubKeyAlgo = signerSubkey.PrivateKey.PubKeyAlgo
  92. sig.Hash = config.Hash()
  93. sig.CreationTime = config.Now()
  94. sig.IssuerKeyId = &signerSubkey.PrivateKey.KeyId
  95. h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType)
  96. if err != nil {
  97. return
  98. }
  99. io.Copy(wrappedHash, message)
  100. err = sig.Sign(h, signerSubkey.PrivateKey, config)
  101. if err != nil {
  102. return
  103. }
  104. return sig.Serialize(w)
  105. }
  106. // FileHints contains metadata about encrypted files. This metadata is, itself,
  107. // encrypted.
  108. type FileHints struct {
  109. // IsBinary can be set to hint that the contents are binary data.
  110. IsBinary bool
  111. // FileName hints at the name of the file that should be written. It's
  112. // truncated to 255 bytes if longer. It may be empty to suggest that the
  113. // file should not be written to disk. It may be equal to "_CONSOLE" to
  114. // suggest the data should not be written to disk.
  115. FileName string
  116. // ModTime contains the modification time of the file, or the zero time if not applicable.
  117. ModTime time.Time
  118. }
  119. // SymmetricallyEncrypt acts like gpg -c: it encrypts a file with a passphrase.
  120. // The resulting WriteCloser must be closed after the contents of the file have
  121. // been written.
  122. // If config is nil, sensible defaults will be used.
  123. func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) {
  124. if hints == nil {
  125. hints = &FileHints{}
  126. }
  127. key, err := packet.SerializeSymmetricKeyEncrypted(ciphertext, passphrase, config)
  128. if err != nil {
  129. return
  130. }
  131. w, err := packet.SerializeSymmetricallyEncrypted(ciphertext, config.Cipher(), key, config)
  132. if err != nil {
  133. return
  134. }
  135. literaldata := w
  136. if algo := config.Compression(); algo != packet.CompressionNone {
  137. var compConfig *packet.CompressionConfig
  138. if config != nil {
  139. compConfig = config.CompressionConfig
  140. }
  141. literaldata, err = packet.SerializeCompressed(w, algo, compConfig)
  142. if err != nil {
  143. return
  144. }
  145. }
  146. var epochSeconds uint32
  147. if !hints.ModTime.IsZero() {
  148. epochSeconds = uint32(hints.ModTime.Unix())
  149. }
  150. return packet.SerializeLiteral(literaldata, hints.IsBinary, hints.FileName, epochSeconds)
  151. }
  152. // intersectPreferences mutates and returns a prefix of a that contains only
  153. // the values in the intersection of a and b. The order of a is preserved.
  154. func intersectPreferences(a []uint8, b []uint8) (intersection []uint8) {
  155. var j int
  156. for _, v := range a {
  157. for _, v2 := range b {
  158. if v == v2 {
  159. a[j] = v
  160. j++
  161. break
  162. }
  163. }
  164. }
  165. return a[:j]
  166. }
  167. func hashToHashId(h crypto.Hash) uint8 {
  168. v, ok := s2k.HashToHashId(h)
  169. if !ok {
  170. panic("tried to convert unknown hash")
  171. }
  172. return v
  173. }
  174. // Encrypt encrypts a message to a number of recipients and, optionally, signs
  175. // it. hints contains optional information, that is also encrypted, that aids
  176. // the recipients in processing the message. The resulting WriteCloser must
  177. // be closed after the contents of the file have been written.
  178. // If config is nil, sensible defaults will be used.
  179. func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints, config *packet.Config) (plaintext io.WriteCloser, err error) {
  180. var signer *packet.PrivateKey
  181. if signed != nil {
  182. signKey, ok := signed.signingKey(config.Now())
  183. if !ok {
  184. return nil, errors.InvalidArgumentError("no valid signing keys")
  185. }
  186. signer = signKey.PrivateKey
  187. if signer == nil {
  188. return nil, errors.InvalidArgumentError("no private key in signing key")
  189. }
  190. if signer.Encrypted {
  191. return nil, errors.InvalidArgumentError("signing key must be decrypted")
  192. }
  193. }
  194. // These are the possible ciphers that we'll use for the message.
  195. candidateCiphers := []uint8{
  196. uint8(packet.CipherAES128),
  197. uint8(packet.CipherAES256),
  198. uint8(packet.CipherCAST5),
  199. }
  200. // These are the possible hash functions that we'll use for the signature.
  201. candidateHashes := []uint8{
  202. hashToHashId(crypto.SHA256),
  203. hashToHashId(crypto.SHA512),
  204. hashToHashId(crypto.SHA1),
  205. hashToHashId(crypto.RIPEMD160),
  206. }
  207. // If no preferences were specified, assume something safe and reasonable.
  208. defaultCiphers := []uint8{
  209. uint8(packet.CipherAES128),
  210. uint8(packet.CipherAES192),
  211. uint8(packet.CipherAES256),
  212. uint8(packet.CipherCAST5),
  213. }
  214. defaultHashes := []uint8{
  215. hashToHashId(crypto.SHA256),
  216. hashToHashId(crypto.SHA512),
  217. hashToHashId(crypto.RIPEMD160),
  218. }
  219. encryptKeys := make([]Key, len(to))
  220. for i := range to {
  221. var ok bool
  222. encryptKeys[i], ok = to[i].encryptionKey(config.Now())
  223. if !ok {
  224. return nil, errors.InvalidArgumentError("cannot encrypt a message to key id " + strconv.FormatUint(to[i].PrimaryKey.KeyId, 16) + " because it has no encryption keys")
  225. }
  226. sig := to[i].primaryIdentity().SelfSignature
  227. preferredSymmetric := sig.PreferredSymmetric
  228. if len(preferredSymmetric) == 0 {
  229. preferredSymmetric = defaultCiphers
  230. }
  231. preferredHashes := sig.PreferredHash
  232. if len(preferredHashes) == 0 {
  233. preferredHashes = defaultHashes
  234. }
  235. candidateCiphers = intersectPreferences(candidateCiphers, preferredSymmetric)
  236. candidateHashes = intersectPreferences(candidateHashes, preferredHashes)
  237. }
  238. if len(candidateCiphers) == 0 {
  239. return nil, errors.InvalidArgumentError("cannot encrypt because recipient set shares no common ciphers")
  240. }
  241. if len(candidateHashes) == 0 {
  242. return nil, errors.InvalidArgumentError("cannot encrypt because recipient set shares no common hashes")
  243. }
  244. cipher := packet.CipherFunction(candidateCiphers[0])
  245. // If the cipher specifed by config is a candidate, we'll use that.
  246. configuredCipher := config.Cipher()
  247. for _, c := range candidateCiphers {
  248. cipherFunc := packet.CipherFunction(c)
  249. if cipherFunc == configuredCipher {
  250. cipher = cipherFunc
  251. break
  252. }
  253. }
  254. var hash crypto.Hash
  255. for _, hashId := range candidateHashes {
  256. if h, ok := s2k.HashIdToHash(hashId); ok && h.Available() {
  257. hash = h
  258. break
  259. }
  260. }
  261. // If the hash specified by config is a candidate, we'll use that.
  262. if configuredHash := config.Hash(); configuredHash.Available() {
  263. for _, hashId := range candidateHashes {
  264. if h, ok := s2k.HashIdToHash(hashId); ok && h == configuredHash {
  265. hash = h
  266. break
  267. }
  268. }
  269. }
  270. if hash == 0 {
  271. hashId := candidateHashes[0]
  272. name, ok := s2k.HashIdToString(hashId)
  273. if !ok {
  274. name = "#" + strconv.Itoa(int(hashId))
  275. }
  276. return nil, errors.InvalidArgumentError("cannot encrypt because no candidate hash functions are compiled in. (Wanted " + name + " in this case.)")
  277. }
  278. symKey := make([]byte, cipher.KeySize())
  279. if _, err := io.ReadFull(config.Random(), symKey); err != nil {
  280. return nil, err
  281. }
  282. for _, key := range encryptKeys {
  283. if err := packet.SerializeEncryptedKey(ciphertext, key.PublicKey, cipher, symKey, config); err != nil {
  284. return nil, err
  285. }
  286. }
  287. encryptedData, err := packet.SerializeSymmetricallyEncrypted(ciphertext, cipher, symKey, config)
  288. if err != nil {
  289. return
  290. }
  291. if signer != nil {
  292. ops := &packet.OnePassSignature{
  293. SigType: packet.SigTypeBinary,
  294. Hash: hash,
  295. PubKeyAlgo: signer.PubKeyAlgo,
  296. KeyId: signer.KeyId,
  297. IsLast: true,
  298. }
  299. if err := ops.Serialize(encryptedData); err != nil {
  300. return nil, err
  301. }
  302. }
  303. if hints == nil {
  304. hints = &FileHints{}
  305. }
  306. w := encryptedData
  307. if signer != nil {
  308. // If we need to write a signature packet after the literal
  309. // data then we need to stop literalData from closing
  310. // encryptedData.
  311. w = noOpCloser{encryptedData}
  312. }
  313. var epochSeconds uint32
  314. if !hints.ModTime.IsZero() {
  315. epochSeconds = uint32(hints.ModTime.Unix())
  316. }
  317. literalData, err := packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, epochSeconds)
  318. if err != nil {
  319. return nil, err
  320. }
  321. if signer != nil {
  322. return signatureWriter{encryptedData, literalData, hash, hash.New(), signer, config}, nil
  323. }
  324. return literalData, nil
  325. }
  326. // signatureWriter hashes the contents of a message while passing it along to
  327. // literalData. When closed, it closes literalData, writes a signature packet
  328. // to encryptedData and then also closes encryptedData.
  329. type signatureWriter struct {
  330. encryptedData io.WriteCloser
  331. literalData io.WriteCloser
  332. hashType crypto.Hash
  333. h hash.Hash
  334. signer *packet.PrivateKey
  335. config *packet.Config
  336. }
  337. func (s signatureWriter) Write(data []byte) (int, error) {
  338. s.h.Write(data)
  339. return s.literalData.Write(data)
  340. }
  341. func (s signatureWriter) Close() error {
  342. sig := &packet.Signature{
  343. SigType: packet.SigTypeBinary,
  344. PubKeyAlgo: s.signer.PubKeyAlgo,
  345. Hash: s.hashType,
  346. CreationTime: s.config.Now(),
  347. IssuerKeyId: &s.signer.KeyId,
  348. }
  349. if err := sig.Sign(s.h, s.signer, s.config); err != nil {
  350. return err
  351. }
  352. if err := s.literalData.Close(); err != nil {
  353. return err
  354. }
  355. if err := sig.Serialize(s.encryptedData); err != nil {
  356. return err
  357. }
  358. return s.encryptedData.Close()
  359. }
  360. // noOpCloser is like an ioutil.NopCloser, but for an io.Writer.
  361. // TODO: we have two of these in OpenPGP packages alone. This probably needs
  362. // to be promoted somewhere more common.
  363. type noOpCloser struct {
  364. w io.Writer
  365. }
  366. func (c noOpCloser) Write(data []byte) (n int, err error) {
  367. return c.w.Write(data)
  368. }
  369. func (c noOpCloser) Close() error {
  370. return nil
  371. }
  372. // AttachedSign is like openpgp.Encrypt (as in p.crypto/openpgp/write.go), but
  373. // don't encrypt at all, just sign the literal unencrypted data.
  374. // Unfortunately we need to duplicate some code here that's already
  375. // in write.go
  376. func AttachedSign(out io.WriteCloser, signed Entity, hints *FileHints,
  377. config *packet.Config) (in io.WriteCloser, err error) {
  378. if hints == nil {
  379. hints = &FileHints{}
  380. }
  381. if config == nil {
  382. config = &packet.Config{}
  383. }
  384. var signer *packet.PrivateKey
  385. signKey, ok := signed.signingKey(config.Now())
  386. if !ok {
  387. err = errors.InvalidArgumentError("no valid signing keys")
  388. return
  389. }
  390. signer = signKey.PrivateKey
  391. if signer == nil {
  392. err = errors.InvalidArgumentError("no valid signing keys")
  393. return
  394. }
  395. if signer.Encrypted {
  396. err = errors.InvalidArgumentError("signing key must be decrypted")
  397. return
  398. }
  399. if algo := config.Compression(); algo != packet.CompressionNone {
  400. var compConfig *packet.CompressionConfig
  401. if config != nil {
  402. compConfig = config.CompressionConfig
  403. }
  404. out, err = packet.SerializeCompressed(out, algo, compConfig)
  405. if err != nil {
  406. return
  407. }
  408. }
  409. hasher := config.Hash() // defaults to SHA-256
  410. ops := &packet.OnePassSignature{
  411. SigType: packet.SigTypeBinary,
  412. Hash: hasher,
  413. PubKeyAlgo: signer.PubKeyAlgo,
  414. KeyId: signer.KeyId,
  415. IsLast: true,
  416. }
  417. if err = ops.Serialize(out); err != nil {
  418. return
  419. }
  420. var epochSeconds uint32
  421. if !hints.ModTime.IsZero() {
  422. epochSeconds = uint32(hints.ModTime.Unix())
  423. }
  424. // We don't want the literal serializer to closer the output stream
  425. // since we're going to need to write to it when we finish up the
  426. // signature stuff.
  427. in, err = packet.SerializeLiteral(noOpCloser{out}, hints.IsBinary, hints.FileName, epochSeconds)
  428. if err != nil {
  429. return
  430. }
  431. // If we need to write a signature packet after the literal
  432. // data then we need to stop literalData from closing
  433. // encryptedData.
  434. in = signatureWriter{out, in, hasher, hasher.New(), signer, config}
  435. return
  436. }