Commit f2e94de6 authored by Adam Langley's avatar Adam Langley

crypto/openpgp: add ElGamal support.

R=bradfitz, r
CC=golang-dev
https://golang.org/cl/4639049
parent 10b5519d
...@@ -45,6 +45,7 @@ DIRS=\ ...@@ -45,6 +45,7 @@ DIRS=\
crypto/ocsp\ crypto/ocsp\
crypto/openpgp\ crypto/openpgp\
crypto/openpgp/armor\ crypto/openpgp/armor\
crypto/openpgp/elgamal\
crypto/openpgp/error\ crypto/openpgp/error\
crypto/openpgp/packet\ crypto/openpgp/packet\
crypto/openpgp/s2k\ crypto/openpgp/s2k\
......
# Copyright 2011 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
include ../../../../Make.inc
TARG=crypto/openpgp/elgamal
GOFILES=\
elgamal.go\
include ../../../../Make.pkg
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package elgamal implements ElGamal encryption, suitable for OpenPGP,
// as specified in "A Public-Key Cryptosystem and a Signature Scheme Based on
// Discrete Logarithms," IEEE Transactions on Information Theory, v. IT-31,
// n. 4, 1985, pp. 469-472.
//
// This form of ElGamal embeds PKCS#1 v1.5 padding, which may make it
// unsuitable for other protocols. RSA should be used in preference in any
// case.
package elgamal
import (
"big"
"crypto/rand"
"crypto/subtle"
"io"
"os"
)
// PublicKey represents an ElGamal public key.
type PublicKey struct {
G, P, Y *big.Int
}
// PrivateKey represents an ElGamal private key.
type PrivateKey struct {
PublicKey
X *big.Int
}
// Encrypt encrypts the given message to the given public key. The result is a
// pair of integers. Errors can result from reading random, or because msg is
// too large to be encrypted to the public key.
func Encrypt(random io.Reader, pub *PublicKey, msg []byte) (c1, c2 *big.Int, err os.Error) {
pLen := (pub.P.BitLen() + 7) / 8
if len(msg) > pLen-11 {
err = os.ErrorString("elgamal: message too long")
return
}
// EM = 0x02 || PS || 0x00 || M
em := make([]byte, pLen-1)
em[0] = 2
ps, mm := em[1:len(em)-len(msg)-1], em[len(em)-len(msg):]
err = nonZeroRandomBytes(ps, random)
if err != nil {
return
}
em[len(em)-len(msg)-1] = 0
copy(mm, msg)
m := new(big.Int).SetBytes(em)
k, err := rand.Int(random, pub.P)
if err != nil {
return
}
c1 = new(big.Int).Exp(pub.G, k, pub.P)
s := new(big.Int).Exp(pub.Y, k, pub.P)
c2 = s.Mul(s, m)
c2.Mod(c2, pub.P)
return
}
// Decrypt takes two integers, resulting from an ElGamal encryption, and
// returns the plaintext of the message. An error can result only if the
// ciphertext is invalid. Users should keep in mind that this is a padding
// oracle and thus, if exposed to an adaptive chosen ciphertext attack, can
// be used to break the cryptosystem. See ``Chosen Ciphertext Attacks
// Against Protocols Based on the RSA Encryption Standard PKCS #1'', Daniel
// Bleichenbacher, Advances in Cryptology (Crypto '98),
func Decrypt(priv *PrivateKey, c1, c2 *big.Int) (msg []byte, err os.Error) {
s := new(big.Int).Exp(c1, priv.X, priv.P)
s.ModInverse(s, priv.P)
s.Mul(s, c2)
s.Mod(s, priv.P)
em := s.Bytes()
firstByteIsTwo := subtle.ConstantTimeByteEq(em[0], 2)
// The remainder of the plaintext must be a string of non-zero random
// octets, followed by a 0, followed by the message.
// lookingForIndex: 1 iff we are still looking for the zero.
// index: the offset of the first zero byte.
var lookingForIndex, index int
lookingForIndex = 1
for i := 1; i < len(em); i++ {
equals0 := subtle.ConstantTimeByteEq(em[i], 0)
index = subtle.ConstantTimeSelect(lookingForIndex&equals0, i, index)
lookingForIndex = subtle.ConstantTimeSelect(equals0, 0, lookingForIndex)
}
if firstByteIsTwo != 1 || lookingForIndex != 0 || index < 9 {
return nil, os.ErrorString("elgamal: decryption error")
}
return em[index+1:], nil
}
// nonZeroRandomBytes fills the given slice with non-zero random octets.
func nonZeroRandomBytes(s []byte, rand io.Reader) (err os.Error) {
_, err = io.ReadFull(rand, s)
if err != nil {
return
}
for i := 0; i < len(s); i++ {
for s[i] == 0 {
_, err = io.ReadFull(rand, s[i:i+1])
if err != nil {
return
}
}
}
return
}
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package elgamal
import (
"big"
"bytes"
"crypto/rand"
"testing"
)
// This is the 1024-bit MODP group from RFC 5114, section 2.1:
const primeHex = "B10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C69A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C013ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD7098488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708DF1FB2BC2E4A4371"
const generatorHex = "A4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507FD6406CFF14266D31266FEA1E5C41564B777E690F5504F213160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28AD662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24855E6EEB22B3B2E5"
func fromHex(hex string) *big.Int {
n, ok := new(big.Int).SetString(hex, 16)
if !ok {
panic("failed to parse hex number")
}
return n
}
func TestEncryptDecrypt(t *testing.T) {
priv := &PrivateKey{
PublicKey: PublicKey{
G: fromHex(generatorHex),
P: fromHex(primeHex),
},
X: fromHex("42"),
}
priv.Y = new(big.Int).Exp(priv.G, priv.X, priv.P)
message := []byte("hello world")
c1, c2, err := Encrypt(rand.Reader, &priv.PublicKey, message)
if err != nil {
t.Errorf("error encrypting: %s", err)
}
message2, err := Decrypt(priv, c1, c2)
if err != nil {
t.Errorf("error decrypting: %s", err)
}
if !bytes.Equal(message2, message) {
t.Errorf("decryption failed, got: %x, want: %x", message2, message)
}
}
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
package packet package packet
import ( import (
"big"
"crypto/openpgp/elgamal"
"crypto/openpgp/error" "crypto/openpgp/error"
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
...@@ -21,9 +23,10 @@ const encryptedKeyVersion = 3 ...@@ -21,9 +23,10 @@ const encryptedKeyVersion = 3
type EncryptedKey struct { type EncryptedKey struct {
KeyId uint64 KeyId uint64
Algo PublicKeyAlgorithm Algo PublicKeyAlgorithm
Encrypted []byte
CipherFunc CipherFunction // only valid after a successful Decrypt CipherFunc CipherFunction // only valid after a successful Decrypt
Key []byte // only valid after a successful Decrypt Key []byte // only valid after a successful Decrypt
encryptedMPI1, encryptedMPI2 []byte
} }
func (e *EncryptedKey) parse(r io.Reader) (err os.Error) { func (e *EncryptedKey) parse(r io.Reader) (err os.Error) {
...@@ -37,8 +40,15 @@ func (e *EncryptedKey) parse(r io.Reader) (err os.Error) { ...@@ -37,8 +40,15 @@ func (e *EncryptedKey) parse(r io.Reader) (err os.Error) {
} }
e.KeyId = binary.BigEndian.Uint64(buf[1:9]) e.KeyId = binary.BigEndian.Uint64(buf[1:9])
e.Algo = PublicKeyAlgorithm(buf[9]) e.Algo = PublicKeyAlgorithm(buf[9])
if e.Algo == PubKeyAlgoRSA || e.Algo == PubKeyAlgoRSAEncryptOnly { switch e.Algo {
e.Encrypted, _, err = readMPI(r) case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
e.encryptedMPI1, _, err = readMPI(r)
case PubKeyAlgoElGamal:
e.encryptedMPI1, _, err = readMPI(r)
if err != nil {
return
}
e.encryptedMPI2, _, err = readMPI(r)
} }
_, err = consumeAll(r) _, err = consumeAll(r)
return return
...@@ -52,15 +62,29 @@ func checksumKeyMaterial(key []byte) uint16 { ...@@ -52,15 +62,29 @@ func checksumKeyMaterial(key []byte) uint16 {
return checksum return checksum
} }
// DecryptRSA decrypts an RSA encrypted session key with the given private key. // Decrypt decrypts an encrypted session key with the given private key. The
func (e *EncryptedKey) DecryptRSA(priv *rsa.PrivateKey) (err os.Error) { // private key must have been decrypted first.
if e.Algo != PubKeyAlgoRSA && e.Algo != PubKeyAlgoRSAEncryptOnly { func (e *EncryptedKey) Decrypt(priv *PrivateKey) os.Error {
return error.InvalidArgumentError("EncryptedKey not RSA encrypted") var err os.Error
var b []byte
// TODO(agl): use session key decryption routines here to avoid
// padding oracle attacks.
switch priv.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
b, err = rsa.DecryptPKCS1v15(rand.Reader, priv.PrivateKey.(*rsa.PrivateKey), e.encryptedMPI1)
case PubKeyAlgoElGamal:
c1 := new(big.Int).SetBytes(e.encryptedMPI1)
c2 := new(big.Int).SetBytes(e.encryptedMPI2)
b, err = elgamal.Decrypt(priv.PrivateKey.(*elgamal.PrivateKey), c1, c2)
default:
err = error.InvalidArgumentError("cannot decrypted encrypted session key with private key of type " + strconv.Itoa(int(priv.PubKeyAlgo)))
} }
b, err := rsa.DecryptPKCS1v15(rand.Reader, priv, e.Encrypted)
if err != nil { if err != nil {
return return err
} }
e.CipherFunc = CipherFunction(b[0]) e.CipherFunc = CipherFunction(b[0])
e.Key = b[1 : len(b)-2] e.Key = b[1 : len(b)-2]
expectedChecksum := uint16(b[len(b)-2])<<8 | uint16(b[len(b)-1]) expectedChecksum := uint16(b[len(b)-2])<<8 | uint16(b[len(b)-1])
...@@ -69,7 +93,7 @@ func (e *EncryptedKey) DecryptRSA(priv *rsa.PrivateKey) (err os.Error) { ...@@ -69,7 +93,7 @@ func (e *EncryptedKey) DecryptRSA(priv *rsa.PrivateKey) (err os.Error) {
return error.StructuralError("EncryptedKey checksum incorrect") return error.StructuralError("EncryptedKey checksum incorrect")
} }
return return nil
} }
// SerializeEncryptedKey serializes an encrypted key packet to w that contains // SerializeEncryptedKey serializes an encrypted key packet to w that contains
...@@ -90,6 +114,8 @@ func SerializeEncryptedKey(w io.Writer, rand io.Reader, pub *PublicKey, cipherFu ...@@ -90,6 +114,8 @@ func SerializeEncryptedKey(w io.Writer, rand io.Reader, pub *PublicKey, cipherFu
switch pub.PubKeyAlgo { switch pub.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
return serializeEncryptedKeyRSA(w, rand, buf, pub.PublicKey.(*rsa.PublicKey), keyBlock) return serializeEncryptedKeyRSA(w, rand, buf, pub.PublicKey.(*rsa.PublicKey), keyBlock)
case PubKeyAlgoElGamal:
return serializeEncryptedKeyElGamal(w, rand, buf, pub.PublicKey.(*elgamal.PublicKey), keyBlock)
case PubKeyAlgoDSA, PubKeyAlgoRSASignOnly: case PubKeyAlgoDSA, PubKeyAlgoRSASignOnly:
return error.InvalidArgumentError("cannot encrypt to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo))) return error.InvalidArgumentError("cannot encrypt to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo)))
} }
...@@ -115,3 +141,28 @@ func serializeEncryptedKeyRSA(w io.Writer, rand io.Reader, header [10]byte, pub ...@@ -115,3 +141,28 @@ func serializeEncryptedKeyRSA(w io.Writer, rand io.Reader, header [10]byte, pub
} }
return writeMPI(w, 8*uint16(len(cipherText)), cipherText) return writeMPI(w, 8*uint16(len(cipherText)), cipherText)
} }
func serializeEncryptedKeyElGamal(w io.Writer, rand io.Reader, header [10]byte, pub *elgamal.PublicKey, keyBlock []byte) os.Error {
c1, c2, err := elgamal.Encrypt(rand, pub, keyBlock)
if err != nil {
return error.InvalidArgumentError("ElGamal encryption failed: " + err.String())
}
packetLen := 10 /* header length */
packetLen += 2 /* mpi size */ + (c1.BitLen()+7)/8
packetLen += 2 /* mpi size */ + (c2.BitLen()+7)/8
err = serializeHeader(w, packetTypeEncryptedKey, packetLen)
if err != nil {
return err
}
_, err = w.Write(header[:])
if err != nil {
return err
}
err = writeBig(w, c1)
if err != nil {
return err
}
return writeBig(w, c2)
}
...@@ -27,11 +27,18 @@ var encryptedKeyPub = rsa.PublicKey{ ...@@ -27,11 +27,18 @@ var encryptedKeyPub = rsa.PublicKey{
N: bigFromBase10("115804063926007623305902631768113868327816898845124614648849934718568541074358183759250136204762053879858102352159854352727097033322663029387610959884180306668628526686121021235757016368038585212410610742029286439607686208110250133174279811431933746643015923132833417396844716207301518956640020862630546868823"), N: bigFromBase10("115804063926007623305902631768113868327816898845124614648849934718568541074358183759250136204762053879858102352159854352727097033322663029387610959884180306668628526686121021235757016368038585212410610742029286439607686208110250133174279811431933746643015923132833417396844716207301518956640020862630546868823"),
} }
var encryptedKeyPriv = &rsa.PrivateKey{ var encryptedKeyRSAPriv = &rsa.PrivateKey{
PublicKey: encryptedKeyPub, PublicKey: encryptedKeyPub,
D: bigFromBase10("32355588668219869544751561565313228297765464314098552250409557267371233892496951383426602439009993875125222579159850054973310859166139474359774543943714622292329487391199285040721944491839695981199720170366763547754915493640685849961780092241140181198779299712578774460837139360803883139311171713302987058393"), D: bigFromBase10("32355588668219869544751561565313228297765464314098552250409557267371233892496951383426602439009993875125222579159850054973310859166139474359774543943714622292329487391199285040721944491839695981199720170366763547754915493640685849961780092241140181198779299712578774460837139360803883139311171713302987058393"),
} }
var encryptedKeyPriv = &PrivateKey{
PublicKey: PublicKey{
PubKeyAlgo: PubKeyAlgoRSA,
},
PrivateKey: encryptedKeyRSAPriv,
}
func TestDecryptingEncryptedKey(t *testing.T) { func TestDecryptingEncryptedKey(t *testing.T) {
const encryptedKeyHex = "c18c032a67d68660df41c70104005789d0de26b6a50c985a02a13131ca829c413a35d0e6fa8d6842599252162808ac7439c72151c8c6183e76923fe3299301414d0c25a2f06a2257db3839e7df0ec964773f6e4c4ac7ff3b48c444237166dd46ba8ff443a5410dc670cb486672fdbe7c9dfafb75b4fea83af3a204fe2a7dfa86bd20122b4f3d2646cbeecb8f7be8" const encryptedKeyHex = "c18c032a67d68660df41c70104005789d0de26b6a50c985a02a13131ca829c413a35d0e6fa8d6842599252162808ac7439c72151c8c6183e76923fe3299301414d0c25a2f06a2257db3839e7df0ec964773f6e4c4ac7ff3b48c444237166dd46ba8ff443a5410dc670cb486672fdbe7c9dfafb75b4fea83af3a204fe2a7dfa86bd20122b4f3d2646cbeecb8f7be8"
const expectedKeyHex = "d930363f7e0308c333b9618617ea728963d8df993665ae7be1092d4926fd864b" const expectedKeyHex = "d930363f7e0308c333b9618617ea728963d8df993665ae7be1092d4926fd864b"
...@@ -52,9 +59,9 @@ func TestDecryptingEncryptedKey(t *testing.T) { ...@@ -52,9 +59,9 @@ func TestDecryptingEncryptedKey(t *testing.T) {
return return
} }
err = ek.DecryptRSA(encryptedKeyPriv) err = ek.Decrypt(encryptedKeyPriv)
if err != nil { if err != nil {
t.Errorf("error from DecryptRSA: %s", err) t.Errorf("error from Decrypt: %s", err)
return return
} }
...@@ -102,9 +109,9 @@ func TestEncryptingEncryptedKey(t *testing.T) { ...@@ -102,9 +109,9 @@ func TestEncryptingEncryptedKey(t *testing.T) {
return return
} }
err = ek.DecryptRSA(encryptedKeyPriv) err = ek.Decrypt(encryptedKeyPriv)
if err != nil { if err != nil {
t.Errorf("error from DecryptRSA: %s", err) t.Errorf("error from Decrypt: %s", err)
return return
} }
......
...@@ -372,7 +372,7 @@ const ( ...@@ -372,7 +372,7 @@ const (
PubKeyAlgoRSA PublicKeyAlgorithm = 1 PubKeyAlgoRSA PublicKeyAlgorithm = 1
PubKeyAlgoRSAEncryptOnly PublicKeyAlgorithm = 2 PubKeyAlgoRSAEncryptOnly PublicKeyAlgorithm = 2
PubKeyAlgoRSASignOnly PublicKeyAlgorithm = 3 PubKeyAlgoRSASignOnly PublicKeyAlgorithm = 3
PubKeyAlgoElgamal PublicKeyAlgorithm = 16 PubKeyAlgoElGamal PublicKeyAlgorithm = 16
PubKeyAlgoDSA PublicKeyAlgorithm = 17 PubKeyAlgoDSA PublicKeyAlgorithm = 17
) )
...@@ -380,7 +380,7 @@ const ( ...@@ -380,7 +380,7 @@ const (
// key of the given type. // key of the given type.
func (pka PublicKeyAlgorithm) CanEncrypt() bool { func (pka PublicKeyAlgorithm) CanEncrypt() bool {
switch pka { switch pka {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElgamal: case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElGamal:
return true return true
} }
return false return false
......
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"bytes" "bytes"
"crypto/cipher" "crypto/cipher"
"crypto/dsa" "crypto/dsa"
"crypto/openpgp/elgamal"
"crypto/openpgp/error" "crypto/openpgp/error"
"crypto/openpgp/s2k" "crypto/openpgp/s2k"
"crypto/rsa" "crypto/rsa"
...@@ -224,6 +225,8 @@ func (pk *PrivateKey) parsePrivateKey(data []byte) (err os.Error) { ...@@ -224,6 +225,8 @@ func (pk *PrivateKey) parsePrivateKey(data []byte) (err os.Error) {
return pk.parseRSAPrivateKey(data) return pk.parseRSAPrivateKey(data)
case PubKeyAlgoDSA: case PubKeyAlgoDSA:
return pk.parseDSAPrivateKey(data) return pk.parseDSAPrivateKey(data)
case PubKeyAlgoElGamal:
return pk.parseElGamalPrivateKey(data)
} }
panic("impossible") panic("impossible")
} }
...@@ -277,3 +280,22 @@ func (pk *PrivateKey) parseDSAPrivateKey(data []byte) (err os.Error) { ...@@ -277,3 +280,22 @@ func (pk *PrivateKey) parseDSAPrivateKey(data []byte) (err os.Error) {
return nil return nil
} }
func (pk *PrivateKey) parseElGamalPrivateKey(data []byte) (err os.Error) {
pub := pk.PublicKey.PublicKey.(*elgamal.PublicKey)
priv := new(elgamal.PrivateKey)
priv.PublicKey = *pub
buf := bytes.NewBuffer(data)
x, _, err := readMPI(buf)
if err != nil {
return
}
priv.X = new(big.Int).SetBytes(x)
pk.PrivateKey = priv
pk.Encrypted = false
pk.encryptedData = nil
return nil
}
...@@ -8,30 +8,50 @@ import ( ...@@ -8,30 +8,50 @@ import (
"testing" "testing"
) )
var privateKeyTests = []struct {
privateKeyHex string
creationTime uint32
}{
{
privKeyRSAHex,
0x4cc349a8,
},
{
privKeyElGamalHex,
0x4df9ee1a,
},
}
func TestPrivateKeyRead(t *testing.T) { func TestPrivateKeyRead(t *testing.T) {
packet, err := Read(readerFromHex(privKeyHex)) for i, test := range privateKeyTests {
packet, err := Read(readerFromHex(test.privateKeyHex))
if err != nil { if err != nil {
t.Error(err) t.Errorf("#%d: failed to parse: %s", i, err)
return continue
} }
privKey := packet.(*PrivateKey) privKey := packet.(*PrivateKey)
if !privKey.Encrypted { if !privKey.Encrypted {
t.Error("private key isn't encrypted") t.Errorf("#%d: private key isn't encrypted", i)
return continue
} }
err = privKey.Decrypt([]byte("testing")) err = privKey.Decrypt([]byte("testing"))
if err != nil { if err != nil {
t.Error(err) t.Errorf("#%d: failed to decrypt: %s", i, err)
return continue
} }
if privKey.CreationTime != 0x4cc349a8 || privKey.Encrypted { if privKey.CreationTime != test.creationTime || privKey.Encrypted {
t.Errorf("failed to parse, got: %#v", privKey) t.Errorf("#%d: bad result, got: %#v", i, privKey)
}
} }
} }
// Generated with `gpg --export-secret-keys "Test Key 2"` // Generated with `gpg --export-secret-keys "Test Key 2"`
const privKeyHex = "9501fe044cc349a8010400b70ca0010e98c090008d45d1ee8f9113bd5861fd57b88bacb7c68658747663f1e1a3b5a98f32fda6472373c024b97359cd2efc88ff60f77751adfbf6af5e615e6a1408cfad8bf0cea30b0d5f53aa27ad59089ba9b15b7ebc2777a25d7b436144027e3bcd203909f147d0e332b240cf63d3395f5dfe0df0a6c04e8655af7eacdf0011010001fe0303024a252e7d475fd445607de39a265472aa74a9320ba2dac395faa687e9e0336aeb7e9a7397e511b5afd9dc84557c80ac0f3d4d7bfec5ae16f20d41c8c84a04552a33870b930420e230e179564f6d19bb153145e76c33ae993886c388832b0fa042ddda7f133924f3854481533e0ede31d51278c0519b29abc3bf53da673e13e3e1214b52413d179d7f66deee35cac8eacb060f78379d70ef4af8607e68131ff529439668fc39c9ce6dfef8a5ac234d234802cbfb749a26107db26406213ae5c06d4673253a3cbee1fcbae58d6ab77e38d6e2c0e7c6317c48e054edadb5a40d0d48acb44643d998139a8a66bb820be1f3f80185bc777d14b5954b60effe2448a036d565c6bc0b915fcea518acdd20ab07bc1529f561c58cd044f723109b93f6fd99f876ff891d64306b5d08f48bab59f38695e9109c4dec34013ba3153488ce070268381ba923ee1eb77125b36afcb4347ec3478c8f2735b06ef17351d872e577fa95d0c397c88c71b59629a36aec" const privKeyRSAHex = "9501fe044cc349a8010400b70ca0010e98c090008d45d1ee8f9113bd5861fd57b88bacb7c68658747663f1e1a3b5a98f32fda6472373c024b97359cd2efc88ff60f77751adfbf6af5e615e6a1408cfad8bf0cea30b0d5f53aa27ad59089ba9b15b7ebc2777a25d7b436144027e3bcd203909f147d0e332b240cf63d3395f5dfe0df0a6c04e8655af7eacdf0011010001fe0303024a252e7d475fd445607de39a265472aa74a9320ba2dac395faa687e9e0336aeb7e9a7397e511b5afd9dc84557c80ac0f3d4d7bfec5ae16f20d41c8c84a04552a33870b930420e230e179564f6d19bb153145e76c33ae993886c388832b0fa042ddda7f133924f3854481533e0ede31d51278c0519b29abc3bf53da673e13e3e1214b52413d179d7f66deee35cac8eacb060f78379d70ef4af8607e68131ff529439668fc39c9ce6dfef8a5ac234d234802cbfb749a26107db26406213ae5c06d4673253a3cbee1fcbae58d6ab77e38d6e2c0e7c6317c48e054edadb5a40d0d48acb44643d998139a8a66bb820be1f3f80185bc777d14b5954b60effe2448a036d565c6bc0b915fcea518acdd20ab07bc1529f561c58cd044f723109b93f6fd99f876ff891d64306b5d08f48bab59f38695e9109c4dec34013ba3153488ce070268381ba923ee1eb77125b36afcb4347ec3478c8f2735b06ef17351d872e577fa95d0c397c88c71b59629a36aec"
// Generated by `gpg --export-secret-keys` followed by a manual extraction of
// the ElGamal subkey from the packets.
const privKeyElGamalHex = "9d0157044df9ee1a100400eb8e136a58ec39b582629cdadf830bc64e0a94ed8103ca8bb247b27b11b46d1d25297ef4bcc3071785ba0c0bedfe89eabc5287fcc0edf81ab5896c1c8e4b20d27d79813c7aede75320b33eaeeaa586edc00fd1036c10133e6ba0ff277245d0d59d04b2b3421b7244aca5f4a8d870c6f1c1fbff9e1c26699a860b9504f35ca1d700030503fd1ededd3b840795be6d9ccbe3c51ee42e2f39233c432b831ddd9c4e72b7025a819317e47bf94f9ee316d7273b05d5fcf2999c3a681f519b1234bbfa6d359b4752bd9c3f77d6b6456cde152464763414ca130f4e91d91041432f90620fec0e6d6b5116076c2985d5aeaae13be492b9b329efcaf7ee25120159a0a30cd976b42d7afe030302dae7eb80db744d4960c4df930d57e87fe81412eaace9f900e6c839817a614ddb75ba6603b9417c33ea7b6c93967dfa2bcff3fa3c74a5ce2c962db65b03aece14c96cbd0038fc"
...@@ -7,6 +7,7 @@ package packet ...@@ -7,6 +7,7 @@ package packet
import ( import (
"big" "big"
"crypto/dsa" "crypto/dsa"
"crypto/openpgp/elgamal"
"crypto/openpgp/error" "crypto/openpgp/error"
"crypto/rsa" "crypto/rsa"
"crypto/sha1" "crypto/sha1"
...@@ -69,6 +70,8 @@ func (pk *PublicKey) parse(r io.Reader) (err os.Error) { ...@@ -69,6 +70,8 @@ func (pk *PublicKey) parse(r io.Reader) (err os.Error) {
err = pk.parseRSA(r) err = pk.parseRSA(r)
case PubKeyAlgoDSA: case PubKeyAlgoDSA:
err = pk.parseDSA(r) err = pk.parseDSA(r)
case PubKeyAlgoElGamal:
err = pk.parseElGamal(r)
default: default:
err = error.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo))) err = error.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo)))
} }
...@@ -117,7 +120,7 @@ func (pk *PublicKey) parseRSA(r io.Reader) (err os.Error) { ...@@ -117,7 +120,7 @@ func (pk *PublicKey) parseRSA(r io.Reader) (err os.Error) {
return return
} }
// parseRSA parses DSA public key material from the given Reader. See RFC 4880, // parseDSA parses DSA public key material from the given Reader. See RFC 4880,
// section 5.5.2. // section 5.5.2.
func (pk *PublicKey) parseDSA(r io.Reader) (err os.Error) { func (pk *PublicKey) parseDSA(r io.Reader) (err os.Error) {
pk.p.bytes, pk.p.bitLength, err = readMPI(r) pk.p.bytes, pk.p.bitLength, err = readMPI(r)
...@@ -146,6 +149,30 @@ func (pk *PublicKey) parseDSA(r io.Reader) (err os.Error) { ...@@ -146,6 +149,30 @@ func (pk *PublicKey) parseDSA(r io.Reader) (err os.Error) {
return return
} }
// parseElGamal parses ElGamal public key material from the given Reader. See
// RFC 4880, section 5.5.2.
func (pk *PublicKey) parseElGamal(r io.Reader) (err os.Error) {
pk.p.bytes, pk.p.bitLength, err = readMPI(r)
if err != nil {
return
}
pk.g.bytes, pk.g.bitLength, err = readMPI(r)
if err != nil {
return
}
pk.y.bytes, pk.y.bitLength, err = readMPI(r)
if err != nil {
return
}
elgamal := new(elgamal.PublicKey)
elgamal.P = new(big.Int).SetBytes(pk.p.bytes)
elgamal.G = new(big.Int).SetBytes(pk.g.bytes)
elgamal.Y = new(big.Int).SetBytes(pk.y.bytes)
pk.PublicKey = elgamal
return
}
// SerializeSignaturePrefix writes the prefix for this public key to the given Writer. // SerializeSignaturePrefix writes the prefix for this public key to the given Writer.
// The prefix is used when calculating a signature over this public key. See // The prefix is used when calculating a signature over this public key. See
// RFC 4880, section 5.2.4. // RFC 4880, section 5.2.4.
...@@ -160,6 +187,10 @@ func (pk *PublicKey) SerializeSignaturePrefix(h hash.Hash) { ...@@ -160,6 +187,10 @@ func (pk *PublicKey) SerializeSignaturePrefix(h hash.Hash) {
pLength += 2 + uint16(len(pk.q.bytes)) pLength += 2 + uint16(len(pk.q.bytes))
pLength += 2 + uint16(len(pk.g.bytes)) pLength += 2 + uint16(len(pk.g.bytes))
pLength += 2 + uint16(len(pk.y.bytes)) pLength += 2 + uint16(len(pk.y.bytes))
case PubKeyAlgoElGamal:
pLength += 2 + uint16(len(pk.p.bytes))
pLength += 2 + uint16(len(pk.g.bytes))
pLength += 2 + uint16(len(pk.y.bytes))
default: default:
panic("unknown public key algorithm") panic("unknown public key algorithm")
} }
...@@ -180,6 +211,12 @@ func (pk *PublicKey) Serialize(w io.Writer) (err os.Error) { ...@@ -180,6 +211,12 @@ func (pk *PublicKey) Serialize(w io.Writer) (err os.Error) {
length += 2 + len(pk.q.bytes) length += 2 + len(pk.q.bytes)
length += 2 + len(pk.g.bytes) length += 2 + len(pk.g.bytes)
length += 2 + len(pk.y.bytes) length += 2 + len(pk.y.bytes)
case PubKeyAlgoElGamal:
length += 2 + len(pk.p.bytes)
length += 2 + len(pk.g.bytes)
length += 2 + len(pk.y.bytes)
default:
panic("unknown public key algorithm")
} }
err = serializeHeader(w, packetTypePublicKey, length) err = serializeHeader(w, packetTypePublicKey, length)
...@@ -210,13 +247,15 @@ func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err os.Error) { ...@@ -210,13 +247,15 @@ func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err os.Error) {
return writeMPIs(w, pk.n, pk.e) return writeMPIs(w, pk.n, pk.e)
case PubKeyAlgoDSA: case PubKeyAlgoDSA:
return writeMPIs(w, pk.p, pk.q, pk.g, pk.y) return writeMPIs(w, pk.p, pk.q, pk.g, pk.y)
case PubKeyAlgoElGamal:
return writeMPIs(w, pk.p, pk.g, pk.y)
} }
return error.InvalidArgumentError("bad public-key algorithm") return error.InvalidArgumentError("bad public-key algorithm")
} }
// CanSign returns true iff this public key can generate signatures // CanSign returns true iff this public key can generate signatures
func (pk *PublicKey) CanSign() bool { func (pk *PublicKey) CanSign() bool {
return pk.PubKeyAlgo != PubKeyAlgoRSAEncryptOnly && pk.PubKeyAlgo != PubKeyAlgoElgamal return pk.PubKeyAlgo != PubKeyAlgoRSAEncryptOnly && pk.PubKeyAlgo != PubKeyAlgoElGamal
} }
// VerifySignature returns nil iff sig is a valid signature, made by this // VerifySignature returns nil iff sig is a valid signature, made by this
......
...@@ -10,7 +10,6 @@ import ( ...@@ -10,7 +10,6 @@ import (
"crypto/openpgp/armor" "crypto/openpgp/armor"
"crypto/openpgp/error" "crypto/openpgp/error"
"crypto/openpgp/packet" "crypto/openpgp/packet"
"crypto/rsa"
_ "crypto/sha256" _ "crypto/sha256"
"hash" "hash"
"io" "io"
...@@ -111,7 +110,10 @@ ParsePackets: ...@@ -111,7 +110,10 @@ ParsePackets:
case *packet.EncryptedKey: case *packet.EncryptedKey:
// This packet contains the decryption key encrypted to a public key. // This packet contains the decryption key encrypted to a public key.
md.EncryptedToKeyIds = append(md.EncryptedToKeyIds, p.KeyId) md.EncryptedToKeyIds = append(md.EncryptedToKeyIds, p.KeyId)
if p.Algo != packet.PubKeyAlgoRSA && p.Algo != packet.PubKeyAlgoRSAEncryptOnly { switch p.Algo {
case packet.PubKeyAlgoRSA, packet.PubKeyAlgoRSAEncryptOnly, packet.PubKeyAlgoElGamal:
break
default:
continue continue
} }
var keys []Key var keys []Key
...@@ -154,7 +156,7 @@ FindKey: ...@@ -154,7 +156,7 @@ FindKey:
} }
if !pk.key.PrivateKey.Encrypted { if !pk.key.PrivateKey.Encrypted {
if len(pk.encryptedKey.Key) == 0 { if len(pk.encryptedKey.Key) == 0 {
pk.encryptedKey.DecryptRSA(pk.key.PrivateKey.PrivateKey.(*rsa.PrivateKey)) pk.encryptedKey.Decrypt(pk.key.PrivateKey)
} }
if len(pk.encryptedKey.Key) == 0 { if len(pk.encryptedKey.Key) == 0 {
continue continue
......
This diff is collapsed.
...@@ -122,78 +122,112 @@ func TestSymmetricEncryption(t *testing.T) { ...@@ -122,78 +122,112 @@ func TestSymmetricEncryption(t *testing.T) {
} }
} }
func testEncryption(t *testing.T, isSigned bool) { var testEncryptionTests = []struct {
kring, _ := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex)) keyRingHex string
isSigned bool
}{
{
testKeys1And2PrivateHex,
false,
},
{
testKeys1And2PrivateHex,
true,
},
{
dsaElGamalTestKeysHex,
false,
},
{
dsaElGamalTestKeysHex,
true,
},
}
func TestEncryption(t *testing.T) {
for i, test := range testEncryptionTests {
kring, _ := ReadKeyRing(readerFromHex(test.keyRingHex))
passphrase := []byte("passphrase")
for _, entity := range kring {
if entity.PrivateKey != nil && entity.PrivateKey.Encrypted {
err := entity.PrivateKey.Decrypt(passphrase)
if err != nil {
t.Errorf("#%d: failed to decrypt key", i)
}
}
for _, subkey := range entity.Subkeys {
if subkey.PrivateKey != nil && subkey.PrivateKey.Encrypted {
err := subkey.PrivateKey.Decrypt(passphrase)
if err != nil {
t.Errorf("#%d: failed to decrypt subkey", i)
}
}
}
}
var signed *Entity var signed *Entity
if isSigned { if test.isSigned {
signed = kring[0] signed = kring[0]
} }
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
w, err := Encrypt(buf, kring[:1], signed, nil /* no hints */ ) w, err := Encrypt(buf, kring[:1], signed, nil /* no hints */ )
if err != nil { if err != nil {
t.Errorf("error in Encrypt: %s", err) t.Errorf("#%d: error in Encrypt: %s", i, err)
return continue
} }
const message = "testing" const message = "testing"
_, err = w.Write([]byte(message)) _, err = w.Write([]byte(message))
if err != nil { if err != nil {
t.Errorf("error writing plaintext: %s", err) t.Errorf("#%d: error writing plaintext: %s", i, err)
return continue
} }
err = w.Close() err = w.Close()
if err != nil { if err != nil {
t.Errorf("error closing WriteCloser: %s", err) t.Errorf("#%d: error closing WriteCloser: %s", i, err)
return continue
} }
md, err := ReadMessage(buf, kring, nil /* no prompt */ ) md, err := ReadMessage(buf, kring, nil /* no prompt */ )
if err != nil { if err != nil {
t.Errorf("error reading message: %s", err) t.Errorf("#%d: error reading message: %s", i, err)
return continue
} }
if isSigned { if test.isSigned {
expectedKeyId := kring[0].signingKey().PublicKey.KeyId expectedKeyId := kring[0].signingKey().PublicKey.KeyId
if md.SignedByKeyId != expectedKeyId { if md.SignedByKeyId != expectedKeyId {
t.Errorf("message signed by wrong key id, got: %d, want: %d", *md.SignedBy, expectedKeyId) t.Errorf("#%d: message signed by wrong key id, got: %d, want: %d", i, *md.SignedBy, expectedKeyId)
} }
if md.SignedBy == nil { if md.SignedBy == nil {
t.Errorf("failed to find the signing Entity") t.Errorf("#%d: failed to find the signing Entity", i)
} }
} }
plaintext, err := ioutil.ReadAll(md.UnverifiedBody) plaintext, err := ioutil.ReadAll(md.UnverifiedBody)
if err != nil { if err != nil {
t.Errorf("error reading encrypted contents: %s", err) t.Errorf("#%d: error reading encrypted contents: %s", i, err)
return continue
} }
expectedKeyId := kring[0].encryptionKey().PublicKey.KeyId expectedKeyId := kring[0].encryptionKey().PublicKey.KeyId
if len(md.EncryptedToKeyIds) != 1 || md.EncryptedToKeyIds[0] != expectedKeyId { if len(md.EncryptedToKeyIds) != 1 || md.EncryptedToKeyIds[0] != expectedKeyId {
t.Errorf("expected message to be encrypted to %v, but got %#v", expectedKeyId, md.EncryptedToKeyIds) t.Errorf("#%d: expected message to be encrypted to %v, but got %#v", i, expectedKeyId, md.EncryptedToKeyIds)
} }
if string(plaintext) != message { if string(plaintext) != message {
t.Errorf("got: %s, want: %s", string(plaintext), message) t.Errorf("#%d: got: %s, want: %s", i, string(plaintext), message)
} }
if isSigned { if test.isSigned {
if md.SignatureError != nil { if md.SignatureError != nil {
t.Errorf("signature error: %s", err) t.Errorf("#%d: signature error: %s", i, err)
} }
if md.Signature == nil { if md.Signature == nil {
t.Error("signature missing") t.Error("signature missing")
} }
} }
} }
func TestEncryption(t *testing.T) {
testEncryption(t, false /* not signed */ )
}
func TestEncryptAndSign(t *testing.T) {
testEncryption(t, true /* signed */ )
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment