mirror of
https://github.com/golang/go
synced 2024-11-22 03:34:40 -07:00
crypto/openpgp: add ability to encrypt messages.
R=bradfitz, r CC=golang-dev https://golang.org/cl/4581051
This commit is contained in:
parent
b5071e92b4
commit
f0d21a773f
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,17 +52,7 @@ func TestEncryptedKey(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
pub := rsa.PublicKey{
|
||||
E: 65537,
|
||||
N: bigFromBase10("115804063926007623305902631768113868327816898845124614648849934718568541074358183759250136204762053879858102352159854352727097033322663029387610959884180306668628526686121021235757016368038585212410610742029286439607686208110250133174279811431933746643015923132833417396844716207301518956640020862630546868823"),
|
||||
}
|
||||
|
||||
priv := &rsa.PrivateKey{
|
||||
PublicKey: pub,
|
||||
D: bigFromBase10("32355588668219869544751561565313228297765464314098552250409557267371233892496951383426602439009993875125222579159850054973310859166139474359774543943714622292329487391199285040721944491839695981199720170366763547754915493640685849961780092241140181198779299712578774460837139360803883139311171713302987058393"),
|
||||
}
|
||||
|
||||
err = ek.DecryptRSA(priv)
|
||||
err = ek.DecryptRSA(encryptedKeyPriv)
|
||||
if err != nil {
|
||||
t.Errorf("error from DecryptRSA: %s", err)
|
||||
return
|
||||
@ -63,5 +69,52 @@ func TestEncryptedKey(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
const encryptedKeyHex = "c18c032a67d68660df41c70104005789d0de26b6a50c985a02a13131ca829c413a35d0e6fa8d6842599252162808ac7439c72151c8c6183e76923fe3299301414d0c25a2f06a2257db3839e7df0ec964773f6e4c4ac7ff3b48c444237166dd46ba8ff443a5410dc670cb486672fdbe7c9dfafb75b4fea83af3a204fe2a7dfa86bd20122b4f3d2646cbeecb8f7be8"
|
||||
const expectedKeyHex = "d930363f7e0308c333b9618617ea728963d8df993665ae7be1092d4926fd864b"
|
||||
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
|
||||
}
|
||||
|
||||
if ek.KeyId != keyId || ek.Algo != PubKeyAlgoRSAEncryptOnly {
|
||||
t.Errorf("unexpected EncryptedKey contents: %#v", ek)
|
||||
return
|
||||
}
|
||||
|
||||
err = ek.DecryptRSA(encryptedKeyPriv)
|
||||
if err != nil {
|
||||
t.Errorf("error from DecryptRSA: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
if ek.CipherFunc != CipherAES128 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)))
|
||||
}
|
||||
|
@ -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}
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user