| // 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 "golang.org/x/crypto/openpgp/elgamal" |
| |
| import ( |
| "crypto/rand" |
| "crypto/subtle" |
| "errors" |
| "io" |
| "math/big" |
| ) |
| |
| // 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 error) { |
| pLen := (pub.P.BitLen() + 7) / 8 |
| if len(msg) > pLen-11 { |
| err = errors.New("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 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, errors.New("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 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 |
| } |