This commit is contained in:
didier amyot committed 2014-11-29 23:13:42 -08:00
1 parent f464823c87
commit 3625be471c
4 files changed
+322

No files matched your search

+4
View File
@@ -0,0 +1,4 @@
---
language: go
go:
- tip
+26
View File
@@ -2,3 +2,29 @@ elgamal
=======
Pure elgamal cryptosystem implemented in Go
[![travis](https://travis-ci.org/didiercrunch/elgamal.svg)](https://travis-ci.org/didiercrunch/elgamal/)
#### introduction
Implementation of the El Gamal cryptosystem. See
http://en.wikipedia.org/wiki/ElGamal_encryption for an introduction.
The difference between this implementation and the one at golang.org/x/crypto/openpgp/elgamal
is the use of absolutly no padding here.
The absance of padding is cool if you want to encrypt numbers and need to make algebra
on the cypher texts. The use of padding is needed when you encrypt data and you
do not want to allow algebraic operations on the cypher texts.
Another important feature of the library is the ease of serilisation of all
the structs in JSON and BSON.
#### caveat
As any cryptographic library, if you use it, you need to *trust* it. Unfortunatly,
this library as not been verified by a third party. There might be some bugs and
vulnerabilities. If you find one, please fill a bug.
+133
View File
@@ -0,0 +1,133 @@
package elgamal
import (
"crypto/rand"
"encoding/json"
"errors"
"fmt"
"io"
"math/big"
"gopkg.in/mgo.v2/bson"
)
// PublicKey represents an ElGamal public key.
type PublicKey struct {
G, P, Y *big.Int
}
func (pub *PublicKey) Encrypt(msg []byte, random io.Reader) (cypher *Cypher, err error) {
m := new(big.Int).SetBytes(msg)
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)
cypher = &Cypher{c1, c2, pub.P}
return
}
func (this *PublicKey) GetBSON() (interface{}, error) {
return bson.D{
{"g", fmt.Sprintf("%x", this.G)},
{"p", fmt.Sprintf("%x", this.P)},
{"y", fmt.Sprintf("%x", this.Y)},
}, nil
}
// PrivateKey represents an ElGamal private key.
type PrivateKey struct {
PublicKey
X *big.Int
}
func (this *PrivateKey) GetBSON() (interface{}, error) {
return bson.D{
{"x", fmt.Sprintf("%x", this.X)},
{"g", fmt.Sprintf("%x", this.G)},
{"p", fmt.Sprintf("%x", this.P)},
{"y", fmt.Sprintf("%x", this.Y)},
}, nil
}
func (priv *PrivateKey) Decrypt(cypher *Cypher) (msg []byte) {
s := new(big.Int).Exp(cypher.C1, priv.X, priv.P)
s.ModInverse(s, priv.P)
s.Mul(s, cypher.C2)
s.Mod(s, priv.P)
em := s.Bytes()
return em
}
type Cypher struct {
C1, C2, P *big.Int
}
func (this *Cypher) GetBSON() (interface{}, error) {
return bson.D{
{"c1", fmt.Sprintf("%x", this.C1)},
{"c2", fmt.Sprintf("%x", this.C2)},
{"p", fmt.Sprintf("%x", this.P)},
}, nil
}
func (this *Cypher) Mul(cypher1, cypher2 *Cypher) *Cypher {
this.C1 = new(big.Int).Mod(new(big.Int).Mul(cypher1.C1, cypher2.C1), cypher1.P)
this.C2 = new(big.Int).Mod(new(big.Int).Mul(cypher1.C2, cypher2.C2), cypher1.P)
this.P = cypher1.P
return this
}
func (this *Cypher) ToJSON() map[string]string {
return map[string]string{
"C1": fmt.Sprintf("%x", this.C1),
"C2": fmt.Sprintf("%x", this.C2),
"P": fmt.Sprintf("%x", this.P),
}
}
func (this *Cypher) MarshalJSON() ([]byte, error) {
return json.Marshal(this.ToJSON())
}
func (this *Cypher) UnmarshalJSON(bytes []byte) error {
m := make(map[string]string)
err := json.Unmarshal(bytes, &m)
if err != nil {
return err
}
this.FromJSON(m)
return nil
}
func (this *Cypher) FromJSON(json map[string]string) (*Cypher, error) {
var err error
this.C1, err = fromHex(json["C1"])
if err != nil {
return nil, err
}
this.C2, err = fromHex(json["C2"])
if err != nil {
return nil, err
}
this.P, err = fromHex(json["P"])
if err != nil {
return nil, err
}
return this, err
}
func fromHex(hex string) (*big.Int, error) {
n, err := new(big.Int).SetString(hex, 16)
if !err {
msg := fmt.Sprintf("Cannot convert %s to int as hexadecimal", hex)
return nil, errors.New(msg)
}
return n, nil
}
+159
View File
@@ -0,0 +1,159 @@
package elgamal
import (
"bytes"
"crypto/rand"
"math/big"
"reflect"
"testing"
"bitbucket.org/ustraca/crypto"
)
// This is the 1024-bit MODP group from RFC 5114, section 2.1:
const primeHex = "B10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C69A6A9DCA52D" +
"23B616073E28675A23D189838EF1E2EE652C013ECB4AEA906112324975C3CD49B83BFACC" +
"BDD7D90C4BD7098488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0A151AF5F0DC" +
"8B4BD45BF37DF365C1A65E68CFDA76D4DA708DF1FB2BC2E4A4371"
const generatorHex = "A4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507FD6406CF" +
"F14266D31266FEA1E5C41564B777E690F5504F213160217B4B01B886A5E91547F9E2749F" +
"4D7FBD7D3B9A92EE1909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28AD662A4D" +
"18E73AFA32D779D5918D08BC8858F4DCEF97C2A24855E6EEB22B3B2E5"
func fromSafeHex(s string) *big.Int {
ret, err := crypto.FromHex(s)
if err != nil {
panic(err)
}
return ret
}
func TestEncryptDecrypt(t *testing.T) {
priv := &PrivateKey{
PublicKey: PublicKey{
G: fromSafeHex(generatorHex),
P: fromSafeHex(primeHex),
},
X: fromSafeHex("42"),
}
priv.Y = new(big.Int).Exp(priv.G, priv.X, priv.P)
message := []byte("hello world")
cypher, err := priv.PublicKey.Encrypt(message, rand.Reader)
if err != nil {
t.Errorf("error encrypting: %s", err)
}
message2 := priv.Decrypt(cypher)
if !bytes.Equal(message2, message) {
t.Errorf("decryption failed, got: '%x', want: '%x'", message2, message)
}
}
func TestEncryptDecryptWithSmallNumbers(t *testing.T) {
priv := &PrivateKey{
PublicKey: PublicKey{
G: big.NewInt(33),
P: big.NewInt(71),
},
X: big.NewInt(42),
}
priv.Y = new(big.Int).Exp(priv.G, priv.X, priv.P)
message := big.NewInt(15).Bytes()
cypher, err := priv.PublicKey.Encrypt(message, rand.Reader)
if err != nil {
t.Errorf("error encrypting: %s", err)
}
message2 := priv.Decrypt(cypher)
if !bytes.Equal(message2, message) {
t.Errorf("decryption failed, got: %x, want: %x", message2, message)
}
}
func TestHomomorphicProperties(t *testing.T) {
priv := PrivateKey{
PublicKey: PublicKey{
G: big.NewInt(33),
P: big.NewInt(71),
},
X: big.NewInt(42),
}
priv.Y = new(big.Int).Exp(priv.G, priv.X, priv.P)
message1 := big.NewInt(11).Bytes()
cypher1, _ := priv.PublicKey.Encrypt(message1, rand.Reader)
message2 := big.NewInt(12).Bytes()
cypher2, _ := priv.PublicKey.Encrypt(message2, rand.Reader)
decyphered := priv.Decrypt(new(Cypher).Mul(cypher1, cypher2))
expected := big.NewInt((12 * 11) % 71).Bytes()
if !bytes.Equal(expected, decyphered) {
t.Errorf("decryption failed, got: %x, want: %x", decyphered, expected)
}
}
func TestCypherToJSON(t *testing.T) {
cypher := &Cypher{
C1: big.NewInt(33),
C2: big.NewInt(99),
P: big.NewInt(192),
}
exp := make(map[string]string)
exp["C1"] = "21"
exp["C2"] = "63"
exp["P"] = "c0"
if !reflect.DeepEqual(exp, cypher.ToJSON()) {
t.Error(exp, "is not ", cypher.ToJSON())
}
}
func TestCypherFromJSON(t *testing.T) {
m := make(map[string]string)
m["C1"] = "21"
m["C2"] = "63"
m["P"] = "c0"
cypher, err := new(Cypher).FromJSON(m)
if err != nil {
t.Error(err)
}
if !reflect.DeepEqual(cypher.P, big.NewInt(192)) {
t.Error("P not okay")
}
if !reflect.DeepEqual(cypher.C1, big.NewInt(33)) {
t.Error("C1 not okay")
}
if !reflect.DeepEqual(cypher.C2, big.NewInt(99)) {
t.Error("C2 not okay")
}
}
func TestMarshalling(t *testing.T) {
cypher := &Cypher{
C1: big.NewInt(33),
C2: big.NewInt(99),
P: big.NewInt(192),
}
s, err := cypher.MarshalJSON()
if err != nil {
t.Fail()
}
cypher2 := new(Cypher)
err = cypher2.UnmarshalJSON(s)
if err != nil {
t.Error("could not unmarshal", err)
}
if !reflect.DeepEqual(cypher, cypher2) {
t.Error("the two objects do not match!")
}
}