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 5package openpgp 6 7import ( 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// DetachSign signs message with the private key from signer (which must 21// already have been decrypted) and writes the signature to w. 22// If config is nil, sensible defaults will be used. 23func 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// ArmoredDetachSign signs message with the private key from signer (which 28// must already have been decrypted) and writes an armored signature to w. 29// If config is nil, sensible defaults will be used. 30func 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// DetachSignText signs message (after canonicalising the line endings) with 35// the private key from signer (which must already have been decrypted) and 36// writes the signature to w. 37// If config is nil, sensible defaults will be used. 38func 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// ArmoredDetachSignText signs message (after canonicalising the line endings) 43// with the private key from signer (which must already have been decrypted) 44// and writes an armored signature to w. 45// If config is nil, sensible defaults will be used. 46func ArmoredDetachSignText(w io.Writer, signer *Entity, message io.Reader, config *packet.Config) error { 47 return armoredDetachSign(w, signer, message, packet.SigTypeText, config) 48} 49 50func 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 62func 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// FileHints contains metadata about encrypted files. This metadata is, itself, 92// encrypted. 93type FileHints struct { 94 // IsBinary can be set to hint that the contents are binary data. 95 IsBinary bool 96 // FileName hints at the name of the file that should be written. It's 97 // truncated to 255 bytes if longer. It may be empty to suggest that the 98 // file should not be written to disk. It may be equal to "_CONSOLE" to 99 // suggest the data should not be written to disk. 100 FileName string 101 // ModTime contains the modification time of the file, or the zero time if not applicable. 102 ModTime time.Time 103} 104 105// SymmetricallyEncrypt acts like gpg -c: it encrypts a file with a passphrase. 106// The resulting WriteCloser must be closed after the contents of the file have 107// been written. 108// If config is nil, sensible defaults will be used. 109func 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// intersectPreferences mutates and returns a prefix of a that contains only 143// the values in the intersection of a and b. The order of a is preserved. 144func 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 159func 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// 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. 172func Encrypt(ciphertext io.Writer, to []*Entity, 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 // These are the possible ciphers that we'll use for the message. 189 candidateCiphers := []uint8{ 190 uint8(packet.CipherAES128), 191 uint8(packet.CipherAES256), 192 uint8(packet.CipherCAST5), 193 } 194 // These are the possible hash functions that we'll use for the signature. 195 candidateHashes := []uint8{ 196 hashToHashId(crypto.SHA256), 197 hashToHashId(crypto.SHA512), 198 hashToHashId(crypto.SHA1), 199 hashToHashId(crypto.RIPEMD160), 200 } 201 // In the event that a recipient doesn't specify any supported ciphers 202 // or hash functions, these are the ones that we assume that every 203 // implementation supports. 204 defaultCiphers := candidateCiphers[len(candidateCiphers)-1:] 205 defaultHashes := candidateHashes[len(candidateHashes)-1:] 206 207 encryptKeys := make([]Key, len(to)) 208 for i := range to { 209 var ok bool 210 encryptKeys[i], ok = to[i].encryptionKey(config.Now()) 211 if !ok { 212 return nil, errors.InvalidArgumentError("cannot encrypt a message to key id " + strconv.FormatUint(to[i].PrimaryKey.KeyId, 16) + " because it has no encryption keys") 213 } 214 215 sig := to[i].primaryIdentity().SelfSignature 216 217 preferredSymmetric := sig.PreferredSymmetric 218 if len(preferredSymmetric) == 0 { 219 preferredSymmetric = defaultCiphers 220 } 221 preferredHashes := sig.PreferredHash 222 if len(preferredHashes) == 0 { 223 preferredHashes = defaultHashes 224 } 225 candidateCiphers = intersectPreferences(candidateCiphers, preferredSymmetric) 226 candidateHashes = intersectPreferences(candidateHashes, preferredHashes) 227 } 228 229 if len(candidateCiphers) == 0 || len(candidateHashes) == 0 { 230 return nil, errors.InvalidArgumentError("cannot encrypt because recipient set shares no common algorithms") 231 } 232 233 cipher := packet.CipherFunction(candidateCiphers[0]) 234 // If the cipher specified by config is a candidate, we'll use that. 235 configuredCipher := config.Cipher() 236 for _, c := range candidateCiphers { 237 cipherFunc := packet.CipherFunction(c) 238 if cipherFunc == configuredCipher { 239 cipher = cipherFunc 240 break 241 } 242 } 243 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 271 symKey := make([]byte, cipher.KeySize()) 272 if _, err := io.ReadFull(config.Random(), symKey); err != nil { 273 return nil, err 274 } 275 276 for _, key := range encryptKeys { 277 if err := packet.SerializeEncryptedKey(ciphertext, key.PublicKey, cipher, symKey, config); err != nil { 278 return nil, err 279 } 280 } 281 282 encryptedData, err := packet.SerializeSymmetricallyEncrypted(ciphertext, cipher, symKey, config) 283 if err != nil { 284 return 285 } 286 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 } 299 300 if hints == nil { 301 hints = &FileHints{} 302 } 303 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 319 } 320 321 if signer != nil { 322 return signatureWriter{encryptedData, literalData, hash, hash.New(), signer, config}, nil 323 } 324 return literalData, nil 325} 326 327// signatureWriter hashes the contents of a message while passing it along to 328// literalData. When closed, it closes literalData, writes a signature packet 329// to encryptedData and then also closes encryptedData. 330type signatureWriter struct { 331 encryptedData io.WriteCloser 332 literalData io.WriteCloser 333 hashType crypto.Hash 334 h hash.Hash 335 signer *packet.PrivateKey 336 config *packet.Config 337} 338 339func (s signatureWriter) Write(data []byte) (int, error) { 340 s.h.Write(data) 341 return s.literalData.Write(data) 342} 343 344func (s signatureWriter) Close() error { 345 sig := &packet.Signature{ 346 SigType: packet.SigTypeBinary, 347 PubKeyAlgo: s.signer.PubKeyAlgo, 348 Hash: s.hashType, 349 CreationTime: s.config.Now(), 350 IssuerKeyId: &s.signer.KeyId, 351 } 352 353 if err := sig.Sign(s.h, s.signer, s.config); err != nil { 354 return err 355 } 356 if err := s.literalData.Close(); err != nil { 357 return err 358 } 359 if err := sig.Serialize(s.encryptedData); err != nil { 360 return err 361 } 362 return s.encryptedData.Close() 363} 364 365// noOpCloser is like an ioutil.NopCloser, but for an io.Writer. 366// TODO: we have two of these in OpenPGP packages alone. This probably needs 367// to be promoted somewhere more common. 368type noOpCloser struct { 369 w io.Writer 370} 371 372func (c noOpCloser) Write(data []byte) (n int, err error) { 373 return c.w.Write(data) 374} 375 376func (c noOpCloser) Close() error { 377 return nil 378} 379