]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/openpgp: add ability to encrypt messages.
authorAdam Langley <agl@golang.org>
Fri, 10 Jun 2011 16:58:14 +0000 (12:58 -0400)
committerAdam Langley <agl@golang.org>
Fri, 10 Jun 2011 16:58:14 +0000 (12:58 -0400)
R=bradfitz, r
CC=golang-dev
https://golang.org/cl/4581051

src/pkg/crypto/openpgp/keys.go
src/pkg/crypto/openpgp/packet/encrypted_key.go
src/pkg/crypto/openpgp/packet/encrypted_key_test.go
src/pkg/crypto/openpgp/packet/packet.go
src/pkg/crypto/openpgp/packet/private_key.go
src/pkg/crypto/openpgp/packet/symmetric_key_encrypted.go
src/pkg/crypto/openpgp/packet/symmetrically_encrypted.go
src/pkg/crypto/openpgp/read.go
src/pkg/crypto/openpgp/write.go
src/pkg/crypto/openpgp/write_test.go

index 2acb7e6123e1e98cbaf67aa4b8b06a9a1f4cfbb0..f467cc0117538046f933667953f967e552951d53 100644 (file)
@@ -64,6 +64,78 @@ type KeyRing interface {
        DecryptionKeys() []Key
 }
 
+// primaryIdentity returns the Identity marked as primary or the first identity
+// if none are so marked.
+func (e *Entity) primaryIdentity() *Identity {
+       var firstIdentity *Identity
+       for _, ident := range e.Identities {
+               if firstIdentity == nil {
+                       firstIdentity = ident
+               }
+               if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId {
+                       return ident
+               }
+       }
+       return firstIdentity
+}
+
+// encryptionKey returns the best candidate Key for encrypting a message to the
+// given Entity.
+func (e *Entity) encryptionKey() Key {
+       candidateSubkey := -1
+
+       for i, subkey := range e.Subkeys {
+               if subkey.Sig.FlagsValid && subkey.Sig.FlagEncryptCommunications && subkey.PublicKey.PubKeyAlgo.CanEncrypt() {
+                       candidateSubkey = i
+                       break
+               }
+       }
+
+       i := e.primaryIdentity()
+
+       if e.PrimaryKey.PubKeyAlgo.CanEncrypt() {
+               // If we don't have any candidate subkeys for encryption and
+               // the primary key doesn't have any usage metadata then we
+               // assume that the primary key is ok. Or, if the primary key is
+               // marked as ok to encrypt to, then we can obviously use it.
+               if candidateSubkey == -1 && !i.SelfSignature.FlagsValid || i.SelfSignature.FlagEncryptCommunications && i.SelfSignature.FlagsValid {
+                       return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}
+               }
+       }
+
+       if candidateSubkey != -1 {
+               subkey := e.Subkeys[candidateSubkey]
+               return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig}
+       }
+
+       // This Entity appears to be signing only.
+       return Key{}
+}
+
+// signingKey return the best candidate Key for signing a message with this
+// Entity.
+func (e *Entity) signingKey() Key {
+       candidateSubkey := -1
+
+       for i, subkey := range e.Subkeys {
+               if subkey.Sig.FlagsValid && subkey.Sig.FlagSign && subkey.PublicKey.PubKeyAlgo.CanSign() {
+                       candidateSubkey = i
+                       break
+               }
+       }
+
+       i := e.primaryIdentity()
+
+       // If we have no candidate subkey then we assume that it's ok to sign
+       // with the primary key.
+       if candidateSubkey == -1 || i.SelfSignature.FlagsValid && i.SelfSignature.FlagSign {
+               return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature}
+       }
+
+       subkey := e.Subkeys[candidateSubkey]
+       return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig}
+}
+
 // An EntityList contains one or more Entities.
 type EntityList []*Entity
 
@@ -199,6 +271,10 @@ func readEntity(packets *packet.Reader) (*Entity, os.Error) {
                }
        }
 
+       if !e.PrimaryKey.PubKeyAlgo.CanSign() {
+               return nil, error.StructuralError("primary key cannot be used for signatures")
+       }
+
        var current *Identity
 EachPacket:
        for {
index b11a9b8301af696910e2ac5d1db9f72a45ad4c2a..329493c08fb2cc877184902d61d5907668ad3022 100644 (file)
@@ -14,6 +14,8 @@ import (
        "strconv"
 )
 
+const encryptedKeyVersion = 3
+
 // EncryptedKey represents a public-key encrypted session key. See RFC 4880,
 // section 5.1.
 type EncryptedKey struct {
@@ -30,7 +32,7 @@ func (e *EncryptedKey) parse(r io.Reader) (err os.Error) {
        if err != nil {
                return
        }
-       if buf[0] != 3 {
+       if buf[0] != encryptedKeyVersion {
                return error.UnsupportedError("unknown EncryptedKey version " + strconv.Itoa(int(buf[0])))
        }
        e.KeyId = binary.BigEndian.Uint64(buf[1:9])
@@ -42,6 +44,14 @@ func (e *EncryptedKey) parse(r io.Reader) (err os.Error) {
        return
 }
 
+func checksumKeyMaterial(key []byte) uint16 {
+       var checksum uint16
+       for _, v := range key {
+               checksum += uint16(v)
+       }
+       return checksum
+}
+
 // DecryptRSA decrypts an RSA encrypted session key with the given private key.
 func (e *EncryptedKey) DecryptRSA(priv *rsa.PrivateKey) (err os.Error) {
        if e.Algo != PubKeyAlgoRSA && e.Algo != PubKeyAlgoRSAEncryptOnly {
@@ -54,13 +64,54 @@ func (e *EncryptedKey) DecryptRSA(priv *rsa.PrivateKey) (err os.Error) {
        e.CipherFunc = CipherFunction(b[0])
        e.Key = b[1 : len(b)-2]
        expectedChecksum := uint16(b[len(b)-2])<<8 | uint16(b[len(b)-1])
-       var checksum uint16
-       for _, v := range e.Key {
-               checksum += uint16(v)
-       }
+       checksum := checksumKeyMaterial(e.Key)
        if checksum != expectedChecksum {
                return error.StructuralError("EncryptedKey checksum incorrect")
        }
 
        return
 }
+
+// SerializeEncryptedKey serializes an encrypted key packet to w that contains
+// key, encrypted to pub.
+func SerializeEncryptedKey(w io.Writer, rand io.Reader, pub *PublicKey, cipherFunc CipherFunction, key []byte) os.Error {
+       var buf [10]byte
+       buf[0] = encryptedKeyVersion
+       binary.BigEndian.PutUint64(buf[1:9], pub.KeyId)
+       buf[9] = byte(pub.PubKeyAlgo)
+
+       keyBlock := make([]byte, 1 /* cipher type */ +len(key)+2 /* checksum */ )
+       keyBlock[0] = byte(cipherFunc)
+       copy(keyBlock[1:], key)
+       checksum := checksumKeyMaterial(key)
+       keyBlock[1+len(key)] = byte(checksum >> 8)
+       keyBlock[1+len(key)+1] = byte(checksum)
+
+       switch pub.PubKeyAlgo {
+       case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
+               return serializeEncryptedKeyRSA(w, rand, buf, pub.PublicKey.(*rsa.PublicKey), keyBlock)
+       case PubKeyAlgoDSA, PubKeyAlgoRSASignOnly:
+               return error.InvalidArgumentError("cannot encrypt to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo)))
+       }
+
+       return error.UnsupportedError("encrypting a key to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo)))
+}
+
+func serializeEncryptedKeyRSA(w io.Writer, rand io.Reader, header [10]byte, pub *rsa.PublicKey, keyBlock []byte) os.Error {
+       cipherText, err := rsa.EncryptPKCS1v15(rand, pub, keyBlock)
+       if err != nil {
+               return error.InvalidArgumentError("RSA encryption failed: " + err.String())
+       }
+
+       packetLen := 10 /* header length */ + 2 /* mpi size */ + len(cipherText)
+
+       err = serializeHeader(w, packetTypeEncryptedKey, packetLen)
+       if err != nil {
+               return err
+       }
+       _, err = w.Write(header[:])
+       if err != nil {
+               return err
+       }
+       return writeMPI(w, 8*uint16(len(cipherText)), cipherText)
+}
index 755ae7a3074c8b2d4313276f260961c6bf549da2..d4e147c0ef683bce106a124d316cbedc7710bdd2 100644 (file)
@@ -6,6 +6,8 @@ package packet
 
 import (
        "big"
+       "bytes"
+       "crypto/rand"
        "crypto/rsa"
        "fmt"
        "testing"
@@ -19,7 +21,21 @@ func bigFromBase10(s string) *big.Int {
        return b
 }
 
-func TestEncryptedKey(t *testing.T) {
+
+var encryptedKeyPub = rsa.PublicKey{
+       E: 65537,
+       N: bigFromBase10("115804063926007623305902631768113868327816898845124614648849934718568541074358183759250136204762053879858102352159854352727097033322663029387610959884180306668628526686121021235757016368038585212410610742029286439607686208110250133174279811431933746643015923132833417396844716207301518956640020862630546868823"),
+}
+
+var encryptedKeyPriv = &rsa.PrivateKey{
+       PublicKey: encryptedKeyPub,
+       D:         bigFromBase10("32355588668219869544751561565313228297765464314098552250409557267371233892496951383426602439009993875125222579159850054973310859166139474359774543943714622292329487391199285040721944491839695981199720170366763547754915493640685849961780092241140181198779299712578774460837139360803883139311171713302987058393"),
+}
+
+func TestDecryptingEncryptedKey(t *testing.T) {
+       const encryptedKeyHex = "c18c032a67d68660df41c70104005789d0de26b6a50c985a02a13131ca829c413a35d0e6fa8d6842599252162808ac7439c72151c8c6183e76923fe3299301414d0c25a2f06a2257db3839e7df0ec964773f6e4c4ac7ff3b48c444237166dd46ba8ff443a5410dc670cb486672fdbe7c9dfafb75b4fea83af3a204fe2a7dfa86bd20122b4f3d2646cbeecb8f7be8"
+       const expectedKeyHex = "d930363f7e0308c333b9618617ea728963d8df993665ae7be1092d4926fd864b"
+
        p, err := Read(readerFromHex(encryptedKeyHex))
        if err != nil {
                t.Errorf("error from Read: %s", err)
@@ -36,23 +52,63 @@ func TestEncryptedKey(t *testing.T) {
                return
        }
 
-       pub := rsa.PublicKey{
-               E: 65537,
-               N: bigFromBase10("115804063926007623305902631768113868327816898845124614648849934718568541074358183759250136204762053879858102352159854352727097033322663029387610959884180306668628526686121021235757016368038585212410610742029286439607686208110250133174279811431933746643015923132833417396844716207301518956640020862630546868823"),
+       err = ek.DecryptRSA(encryptedKeyPriv)
+       if err != nil {
+               t.Errorf("error from DecryptRSA: %s", err)
+               return
+       }
+
+       if ek.CipherFunc != CipherAES256 {
+               t.Errorf("unexpected EncryptedKey contents: %#v", ek)
+               return
+       }
+
+       keyHex := fmt.Sprintf("%x", ek.Key)
+       if keyHex != expectedKeyHex {
+               t.Errorf("bad key, got %s want %x", keyHex, expectedKeyHex)
+       }
+}
+
+func TestEncryptingEncryptedKey(t *testing.T) {
+       key := []byte{1, 2, 3, 4}
+       const expectedKeyHex = "01020304"
+       const keyId = 42
+
+       pub := &PublicKey{
+               PublicKey:  &encryptedKeyPub,
+               KeyId:      keyId,
+               PubKeyAlgo: PubKeyAlgoRSAEncryptOnly,
+       }
+
+       buf := new(bytes.Buffer)
+       err := SerializeEncryptedKey(buf, rand.Reader, pub, CipherAES128, key)
+       if err != nil {
+               t.Errorf("error writing encrypted key packet: %s", err)
+       }
+
+       p, err := Read(buf)
+       if err != nil {
+               t.Errorf("error from Read: %s", err)
+               return
+       }
+       ek, ok := p.(*EncryptedKey)
+       if !ok {
+               t.Errorf("didn't parse an EncryptedKey, got %#v", p)
+               return
        }
 
-       priv := &rsa.PrivateKey{
-               PublicKey: pub,
-               D:         bigFromBase10("32355588668219869544751561565313228297765464314098552250409557267371233892496951383426602439009993875125222579159850054973310859166139474359774543943714622292329487391199285040721944491839695981199720170366763547754915493640685849961780092241140181198779299712578774460837139360803883139311171713302987058393"),
+       if ek.KeyId != keyId || ek.Algo != PubKeyAlgoRSAEncryptOnly {
+               t.Errorf("unexpected EncryptedKey contents: %#v", ek)
+               return
        }
 
-       err = ek.DecryptRSA(priv)
+       err = ek.DecryptRSA(encryptedKeyPriv)
        if err != nil {
                t.Errorf("error from DecryptRSA: %s", err)
                return
        }
 
-       if ek.CipherFunc != CipherAES256 {
+       if ek.CipherFunc != CipherAES128 {
                t.Errorf("unexpected EncryptedKey contents: %#v", ek)
                return
        }
@@ -62,6 +118,3 @@ func TestEncryptedKey(t *testing.T) {
                t.Errorf("bad key, got %s want %x", keyHex, expectedKeyHex)
        }
 }
-
-const encryptedKeyHex = "c18c032a67d68660df41c70104005789d0de26b6a50c985a02a13131ca829c413a35d0e6fa8d6842599252162808ac7439c72151c8c6183e76923fe3299301414d0c25a2f06a2257db3839e7df0ec964773f6e4c4ac7ff3b48c444237166dd46ba8ff443a5410dc670cb486672fdbe7c9dfafb75b4fea83af3a204fe2a7dfa86bd20122b4f3d2646cbeecb8f7be8"
-const expectedKeyHex = "d930363f7e0308c333b9618617ea728963d8df993665ae7be1092d4926fd864b"
index 640a5b76f3b1c239bd9d181dac9914b38aba010d..60bd067e90f0805b42a4306de03f8d8738ab10f4 100644 (file)
@@ -376,6 +376,26 @@ const (
        PubKeyAlgoDSA            PublicKeyAlgorithm = 17
 )
 
+// CanEncrypt returns true if it's possible to encrypt a message to a public
+// key of the given type.
+func (pka PublicKeyAlgorithm) CanEncrypt() bool {
+       switch pka {
+       case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElgamal:
+               return true
+       }
+       return false
+}
+
+// CanSign returns true if it's possible for a public key of the given type to
+// sign a message.
+func (pka PublicKeyAlgorithm) CanSign() bool {
+       switch pka {
+       case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA:
+               return true
+       }
+       return false
+}
+
 // CipherFunction represents the different block ciphers specified for OpenPGP. See
 // http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-13
 type CipherFunction uint8
@@ -387,8 +407,8 @@ const (
        CipherAES256 CipherFunction = 9
 )
 
-// keySize returns the key size, in bytes, of cipher.
-func (cipher CipherFunction) keySize() int {
+// KeySize returns the key size, in bytes, of cipher.
+func (cipher CipherFunction) KeySize() int {
        switch cipher {
        case CipherCAST5:
                return cast5.KeySize
index 92e7ee422636b8e5c5061b06f802298fcf1c4393..6244661320476b489bbf63f5dcd616c04e163047 100644 (file)
@@ -181,7 +181,7 @@ func (pk *PrivateKey) Decrypt(passphrase []byte) os.Error {
                return nil
        }
 
-       key := make([]byte, pk.cipher.keySize())
+       key := make([]byte, pk.cipher.KeySize())
        pk.s2k(key, passphrase)
        block := pk.cipher.new(key)
        cfb := cipher.NewCFBDecrypter(block, pk.iv)
index 25d264acf9f67acb2c561e911785eefa6b5f07b1..ad4f1d6212a0bb7158fedfc666deed05d95f7bad 100644 (file)
@@ -42,7 +42,7 @@ func (ske *SymmetricKeyEncrypted) parse(r io.Reader) (err os.Error) {
        }
        ske.CipherFunc = CipherFunction(buf[1])
 
-       if ske.CipherFunc.keySize() == 0 {
+       if ske.CipherFunc.KeySize() == 0 {
                return error.UnsupportedError("unknown cipher: " + strconv.Itoa(int(buf[1])))
        }
 
@@ -78,7 +78,7 @@ func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) os.Error {
                return nil
        }
 
-       key := make([]byte, ske.CipherFunc.keySize())
+       key := make([]byte, ske.CipherFunc.KeySize())
        ske.s2k(key, passphrase)
 
        if len(ske.encryptedKey) == 0 {
@@ -109,7 +109,7 @@ func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) os.Error {
 // given passphrase. The session key is returned and must be passed to
 // SerializeSymmetricallyEncrypted.
 func SerializeSymmetricKeyEncrypted(w io.Writer, rand io.Reader, passphrase []byte, cipherFunc CipherFunction) (key []byte, err os.Error) {
-       keySize := cipherFunc.keySize()
+       keySize := cipherFunc.KeySize()
        if keySize == 0 {
                return nil, error.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc)))
        }
index 236c3677453887fde09a25c6db4013d6816f1e46..e33c9f3a060b4f0bfa8ba8860189e5eeb07df4e9 100644 (file)
@@ -47,7 +47,7 @@ func (se *SymmetricallyEncrypted) parse(r io.Reader) os.Error {
 // packet can be read. An incorrect key can, with high probability, be detected
 // immediately and this will result in a KeyIncorrect error being returned.
 func (se *SymmetricallyEncrypted) Decrypt(c CipherFunction, key []byte) (io.ReadCloser, os.Error) {
-       keySize := c.keySize()
+       keySize := c.KeySize()
        if keySize == 0 {
                return nil, error.UnsupportedError("unknown cipher: " + strconv.Itoa(int(c)))
        }
@@ -255,7 +255,7 @@ func (c noOpCloser) Close() os.Error {
 // to w and returns a WriteCloser to which the to-be-encrypted packets can be
 // written.
 func SerializeSymmetricallyEncrypted(w io.Writer, c CipherFunction, key []byte) (contents io.WriteCloser, err os.Error) {
-       if c.keySize() != len(key) {
+       if c.KeySize() != len(key) {
                return nil, error.InvalidArgumentError("SymmetricallyEncrypted.Serialize: bad key length")
        }
        writeCloser := noOpCloser{w}
index 46fcde3630a23b71b0e08c94119f8e41b569dc02..9d9eaec36a49b99052ab980a593ad657568f1b7f 100644 (file)
@@ -57,7 +57,6 @@ type MessageDetails struct {
        // been consumed. Once EOF has been seen, the following fields are
        // valid. (An authentication code failure is reported as a
        // SignatureError error when reading from UnverifiedBody.)
-
        SignatureError os.Error          // nil if the signature is good.
        Signature      *packet.Signature // the signature packet itself.
 
index 48c86f604ee18176048b4373312d0545403533b8..a7e9332c1321baa8747301c6c06d5ca582cef28c 100644 (file)
@@ -9,10 +9,12 @@ import (
        "crypto/openpgp/armor"
        "crypto/openpgp/error"
        "crypto/openpgp/packet"
+       "crypto/openpgp/s2k"
        "crypto/rand"
        _ "crypto/sha256"
        "io"
        "os"
+       "strconv"
        "time"
 )
 
@@ -98,7 +100,7 @@ type FileHints struct {
 }
 
 // SymmetricallyEncrypt acts like gpg -c: it encrypts a file with a passphrase.
-// The resulting WriteCloser MUST be closed after the contents of the file have
+// The resulting WriteCloser must be closed after the contents of the file have
 // been written.
 func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHints) (plaintext io.WriteCloser, err os.Error) {
        if hints == nil {
@@ -115,3 +117,102 @@ func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHi
        }
        return packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, hints.EpochSeconds)
 }
+
+// intersectPreferences mutates and returns a prefix of a that contains only
+// the values in the intersection of a and b. The order of a is preserved.
+func intersectPreferences(a []uint8, b []uint8) (intersection []uint8) {
+       var j int
+       for _, v := range a {
+               for _, v2 := range b {
+                       if v == v2 {
+                               a[j] = v
+                               j++
+                               break
+                       }
+               }
+       }
+
+       return a[:j]
+}
+
+func hashToHashId(h crypto.Hash) uint8 {
+       v, ok := s2k.HashToHashId(h)
+       if !ok {
+               panic("tried to convert unknown hash")
+       }
+       return v
+}
+
+// Encrypt encrypts a message to a number of recipients and, optionally, signs
+// it. (Note: signing is not yet implemented.) hints contains optional
+// information, that is also encrypted, that aids the recipients in processing
+// the message. The resulting WriteCloser must be closed after the contents of
+// the file have been written.
+func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints) (plaintext io.WriteCloser, err os.Error) {
+       // These are the possible ciphers that we'll use for the message.
+       candidateCiphers := []uint8{
+               uint8(packet.CipherAES128),
+               uint8(packet.CipherAES256),
+               uint8(packet.CipherCAST5),
+       }
+       // These are the possible hash functions that we'll use for the signature.
+       candidateHashes := []uint8{
+               hashToHashId(crypto.SHA256),
+               hashToHashId(crypto.SHA512),
+               hashToHashId(crypto.SHA1),
+               hashToHashId(crypto.RIPEMD160),
+       }
+       // In the event that a recipient doesn't specify any supported ciphers
+       // or hash functions, these are the ones that we assume that every
+       // implementation supports.
+       defaultCiphers := candidateCiphers[len(candidateCiphers)-1:]
+       defaultHashes := candidateHashes[len(candidateHashes)-1:]
+
+       encryptKeys := make([]Key, len(to))
+       for i := range to {
+               encryptKeys[i] = to[i].encryptionKey()
+               if encryptKeys[i].PublicKey == nil {
+                       return nil, error.InvalidArgumentError("cannot encrypt a message to key id " + strconv.Uitob64(to[i].PrimaryKey.KeyId, 16) + " because it has no encryption keys")
+               }
+
+               sig := to[i].primaryIdentity().SelfSignature
+
+               preferredSymmetric := sig.PreferredSymmetric
+               if len(preferredSymmetric) == 0 {
+                       preferredSymmetric = defaultCiphers
+               }
+               preferredHashes := sig.PreferredHash
+               if len(preferredHashes) == 0 {
+                       preferredHashes = defaultHashes
+               }
+               candidateCiphers = intersectPreferences(candidateCiphers, preferredSymmetric)
+               candidateHashes = intersectPreferences(candidateHashes, preferredHashes)
+       }
+
+       if len(candidateCiphers) == 0 || len(candidateHashes) == 0 {
+               return nil, error.InvalidArgumentError("cannot encrypt because recipient set shares no common algorithms")
+       }
+
+       cipher := packet.CipherFunction(candidateCiphers[0])
+       // hash := s2k.HashIdToHash(candidateHashes[0])
+       symKey := make([]byte, cipher.KeySize())
+       if _, err := io.ReadFull(rand.Reader, symKey); err != nil {
+               return nil, err
+       }
+
+       for _, key := range encryptKeys {
+               if err := packet.SerializeEncryptedKey(ciphertext, rand.Reader, key.PublicKey, cipher, symKey); err != nil {
+                       return nil, err
+               }
+       }
+
+       w, err := packet.SerializeSymmetricallyEncrypted(ciphertext, cipher, symKey)
+       if err != nil {
+               return
+       }
+
+       if hints == nil {
+               hints = &FileHints{}
+       }
+       return packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, hints.EpochSeconds)
+}
index 8551aeb638e28a3ce09269c0e7eaa72b9f5bc4f2..cfa131418469ec5e43878cca0dc277bf54e3ee70 100644 (file)
@@ -9,6 +9,7 @@ import (
        "crypto/rand"
        "os"
        "io"
+       "io/ioutil"
        "testing"
        "time"
 )
@@ -120,3 +121,47 @@ func TestSymmetricEncryption(t *testing.T) {
                t.Errorf("recovered message incorrect got '%s', want '%s'", messageBuf.Bytes(), message)
        }
 }
+
+func TestEncryption(t *testing.T) {
+       kring, _ := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex))
+
+       buf := new(bytes.Buffer)
+       w, err := Encrypt(buf, kring[:1], nil, /* not signed */ nil /* no hints */ )
+       if err != nil {
+               t.Errorf("error in Encrypt: %s", err)
+               return
+       }
+
+       const message = "testing"
+       _, err = w.Write([]byte(message))
+       if err != nil {
+               t.Errorf("error writing plaintext: %s", err)
+               return
+       }
+       err = w.Close()
+       if err != nil {
+               t.Errorf("error closing WriteCloser: %s", err)
+               return
+       }
+
+       md, err := ReadMessage(buf, kring, nil /* no prompt */ )
+       if err != nil {
+               t.Errorf("error reading message: %s", err)
+               return
+       }
+
+       plaintext, err := ioutil.ReadAll(md.UnverifiedBody)
+       if err != nil {
+               t.Errorf("error reading encrypted contents: %s", err)
+               return
+       }
+
+       expectedKeyId := kring[0].encryptionKey().PublicKey.KeyId
+       if len(md.EncryptedToKeyIds) != 1 || md.EncryptedToKeyIds[0] != expectedKeyId {
+               t.Errorf("expected message to be encrypted to %v, but got %#v", expectedKeyId, md.EncryptedToKeyIds)
+       }
+
+       if string(plaintext) != message {
+               t.Errorf("got: %s, want: %s", string(plaintext), message)
+       }
+}