github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/third_party/code.google.com/p/go.crypto/openpgp/write.go (about) 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 5 package openpgp 6 7 import ( 8 "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/armor" 9 "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/errors" 10 "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/packet" 11 "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/s2k" 12 "crypto" 13 "crypto/rand" 14 _ "crypto/sha256" 15 "hash" 16 "io" 17 "strconv" 18 "time" 19 ) 20 21 // DetachSign signs message with the private key from signer (which must 22 // already have been decrypted) and writes the signature to w. 23 func DetachSign(w io.Writer, signer *Entity, message io.Reader) error { 24 return detachSign(w, signer, message, time.Time{}, packet.SigTypeBinary) 25 } 26 27 // ArmoredDetachSign signs message with the private key from signer (which 28 // must already have been decrypted) and writes an armored signature to w. 29 func ArmoredDetachSign(w io.Writer, signer *Entity, message io.Reader) (err error) { 30 return armoredDetachSign(w, signer, message, time.Time{}, packet.SigTypeBinary) 31 } 32 33 func ArmoredDetachSignAt(w io.Writer, signer *Entity, sigTime time.Time, message io.Reader) (err error) { 34 return armoredDetachSign(w, signer, message, sigTime, packet.SigTypeBinary) 35 } 36 37 // DetachSignText signs message (after canonicalising the line endings) with 38 // the private key from signer (which must already have been decrypted) and 39 // writes the signature to w. 40 func DetachSignText(w io.Writer, signer *Entity, message io.Reader) error { 41 return detachSign(w, signer, message, time.Time{}, packet.SigTypeText) 42 } 43 44 // ArmoredDetachSignText signs message (after canonicalising the line endings) 45 // with the private key from signer (which must already have been decrypted) 46 // and writes an armored signature to w. 47 func ArmoredDetachSignText(w io.Writer, signer *Entity, message io.Reader) error { 48 return armoredDetachSign(w, signer, message, time.Time{}, packet.SigTypeText) 49 } 50 51 func armoredDetachSign(w io.Writer, signer *Entity, message io.Reader, sigTime time.Time, sigType packet.SignatureType) (err error) { 52 out, err := armor.Encode(w, SignatureType, nil) 53 if err != nil { 54 return 55 } 56 err = detachSign(out, signer, message, sigTime, sigType) 57 if err != nil { 58 return 59 } 60 return out.Close() 61 } 62 63 func detachSign(w io.Writer, signer *Entity, message io.Reader, sigTime time.Time, sigType packet.SignatureType) (err error) { 64 if signer.PrivateKey == nil { 65 return errors.InvalidArgumentError("signing key doesn't have a private key") 66 } 67 if signer.PrivateKey.Encrypted { 68 return errors.InvalidArgumentError("signing key is encrypted") 69 } 70 if sigTime.IsZero() { 71 sigTime = time.Now() 72 } 73 74 sig := new(packet.Signature) 75 sig.SigType = sigType 76 sig.PubKeyAlgo = signer.PrivateKey.PubKeyAlgo 77 sig.Hash = crypto.SHA256 78 sig.CreationTime = sigTime 79 sig.IssuerKeyId = &signer.PrivateKey.KeyId 80 81 h, wrappedHash, err := hashForSignature(sig.Hash, sig.SigType) 82 if err != nil { 83 return 84 } 85 io.Copy(wrappedHash, message) 86 87 err = sig.Sign(rand.Reader, h, signer.PrivateKey) 88 if err != nil { 89 return 90 } 91 92 return sig.Serialize(w) 93 } 94 95 // FileHints contains metadata about encrypted files. This metadata is, itself, 96 // encrypted. 97 type FileHints struct { 98 // IsBinary can be set to hint that the contents are binary data. 99 IsBinary bool 100 // FileName hints at the name of the file that should be written. It's 101 // truncated to 255 bytes if longer. It may be empty to suggest that the 102 // file should not be written to disk. It may be equal to "_CONSOLE" to 103 // suggest the data should not be written to disk. 104 FileName string 105 // ModTime contains the modification time of the file, or the zero time if not applicable. 106 ModTime time.Time 107 } 108 109 // SymmetricallyEncrypt acts like gpg -c: it encrypts a file with a passphrase. 110 // The resulting WriteCloser must be closed after the contents of the file have 111 // been written. 112 func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHints) (plaintext io.WriteCloser, err error) { 113 if hints == nil { 114 hints = &FileHints{} 115 } 116 117 key, err := packet.SerializeSymmetricKeyEncrypted(ciphertext, rand.Reader, passphrase, packet.CipherAES128) 118 if err != nil { 119 return 120 } 121 w, err := packet.SerializeSymmetricallyEncrypted(ciphertext, rand.Reader, packet.CipherAES128, key) 122 if err != nil { 123 return 124 } 125 var epochSeconds uint32 126 if !hints.ModTime.IsZero() { 127 epochSeconds = uint32(hints.ModTime.Unix()) 128 } 129 return packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, epochSeconds) 130 } 131 132 // intersectPreferences mutates and returns a prefix of a that contains only 133 // the values in the intersection of a and b. The order of a is preserved. 134 func intersectPreferences(a []uint8, b []uint8) (intersection []uint8) { 135 var j int 136 for _, v := range a { 137 for _, v2 := range b { 138 if v == v2 { 139 a[j] = v 140 j++ 141 break 142 } 143 } 144 } 145 146 return a[:j] 147 } 148 149 func hashToHashId(h crypto.Hash) uint8 { 150 v, ok := s2k.HashToHashId(h) 151 if !ok { 152 panic("tried to convert unknown hash") 153 } 154 return v 155 } 156 157 // Encrypt encrypts a message to a number of recipients and, optionally, signs 158 // it. hints contains optional information, that is also encrypted, that aids 159 // the recipients in processing the message. The resulting WriteCloser must 160 // be closed after the contents of the file have been written. 161 func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints) (plaintext io.WriteCloser, err error) { 162 var signer *packet.PrivateKey 163 if signed != nil { 164 signer = signed.signingKey().PrivateKey 165 if signer == nil || signer.Encrypted { 166 return nil, errors.InvalidArgumentError("signing key must be decrypted") 167 } 168 } 169 170 // These are the possible ciphers that we'll use for the message. 171 candidateCiphers := []uint8{ 172 uint8(packet.CipherAES128), 173 uint8(packet.CipherAES256), 174 uint8(packet.CipherCAST5), 175 } 176 // These are the possible hash functions that we'll use for the signature. 177 candidateHashes := []uint8{ 178 hashToHashId(crypto.SHA256), 179 hashToHashId(crypto.SHA512), 180 hashToHashId(crypto.SHA1), 181 hashToHashId(crypto.RIPEMD160), 182 } 183 // In the event that a recipient doesn't specify any supported ciphers 184 // or hash functions, these are the ones that we assume that every 185 // implementation supports. 186 defaultCiphers := candidateCiphers[len(candidateCiphers)-1:] 187 defaultHashes := candidateHashes[len(candidateHashes)-1:] 188 189 encryptKeys := make([]Key, len(to)) 190 for i := range to { 191 encryptKeys[i] = to[i].encryptionKey() 192 if encryptKeys[i].PublicKey == nil { 193 return nil, errors.InvalidArgumentError("cannot encrypt a message to key id " + strconv.FormatUint(to[i].PrimaryKey.KeyId, 16) + " because it has no encryption keys") 194 } 195 196 sig := to[i].primaryIdentity().SelfSignature 197 198 preferredSymmetric := sig.PreferredSymmetric 199 if len(preferredSymmetric) == 0 { 200 preferredSymmetric = defaultCiphers 201 } 202 preferredHashes := sig.PreferredHash 203 if len(preferredHashes) == 0 { 204 preferredHashes = defaultHashes 205 } 206 candidateCiphers = intersectPreferences(candidateCiphers, preferredSymmetric) 207 candidateHashes = intersectPreferences(candidateHashes, preferredHashes) 208 } 209 210 if len(candidateCiphers) == 0 || len(candidateHashes) == 0 { 211 return nil, errors.InvalidArgumentError("cannot encrypt because recipient set shares no common algorithms") 212 } 213 214 cipher := packet.CipherFunction(candidateCiphers[0]) 215 hash, _ := s2k.HashIdToHash(candidateHashes[0]) 216 symKey := make([]byte, cipher.KeySize()) 217 if _, err := io.ReadFull(rand.Reader, symKey); err != nil { 218 return nil, err 219 } 220 221 for _, key := range encryptKeys { 222 if err := packet.SerializeEncryptedKey(ciphertext, rand.Reader, key.PublicKey, cipher, symKey); err != nil { 223 return nil, err 224 } 225 } 226 227 encryptedData, err := packet.SerializeSymmetricallyEncrypted(ciphertext, rand.Reader, cipher, symKey) 228 if err != nil { 229 return 230 } 231 232 if signer != nil { 233 ops := &packet.OnePassSignature{ 234 SigType: packet.SigTypeBinary, 235 Hash: hash, 236 PubKeyAlgo: signer.PubKeyAlgo, 237 KeyId: signer.KeyId, 238 IsLast: true, 239 } 240 if err := ops.Serialize(encryptedData); err != nil { 241 return nil, err 242 } 243 } 244 245 if hints == nil { 246 hints = &FileHints{} 247 } 248 249 w := encryptedData 250 if signer != nil { 251 // If we need to write a signature packet after the literal 252 // data then we need to stop literalData from closing 253 // encryptedData. 254 w = noOpCloser{encryptedData} 255 256 } 257 var epochSeconds uint32 258 if !hints.ModTime.IsZero() { 259 epochSeconds = uint32(hints.ModTime.Unix()) 260 } 261 literalData, err := packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, epochSeconds) 262 if err != nil { 263 return nil, err 264 } 265 266 if signer != nil { 267 return signatureWriter{encryptedData, literalData, hash, hash.New(), signer}, nil 268 } 269 return literalData, nil 270 } 271 272 // signatureWriter hashes the contents of a message while passing it along to 273 // literalData. When closed, it closes literalData, writes a signature packet 274 // to encryptedData and then also closes encryptedData. 275 type signatureWriter struct { 276 encryptedData io.WriteCloser 277 literalData io.WriteCloser 278 hashType crypto.Hash 279 h hash.Hash 280 signer *packet.PrivateKey 281 } 282 283 func (s signatureWriter) Write(data []byte) (int, error) { 284 s.h.Write(data) 285 return s.literalData.Write(data) 286 } 287 288 func (s signatureWriter) Close() error { 289 sig := &packet.Signature{ 290 SigType: packet.SigTypeBinary, 291 PubKeyAlgo: s.signer.PubKeyAlgo, 292 Hash: s.hashType, 293 CreationTime: time.Now(), 294 IssuerKeyId: &s.signer.KeyId, 295 } 296 297 if err := sig.Sign(rand.Reader, s.h, s.signer); err != nil { 298 return err 299 } 300 if err := s.literalData.Close(); err != nil { 301 return err 302 } 303 if err := sig.Serialize(s.encryptedData); err != nil { 304 return err 305 } 306 return s.encryptedData.Close() 307 } 308 309 // noOpCloser is like an ioutil.NopCloser, but for an io.Writer. 310 // TODO: we have two of these in OpenPGP packages alone. This probably needs 311 // to be promoted somewhere more common. 312 type noOpCloser struct { 313 w io.Writer 314 } 315 316 func (c noOpCloser) Write(data []byte) (n int, err error) { 317 return c.w.Write(data) 318 } 319 320 func (c noOpCloser) Close() error { 321 return nil 322 }