OSDN Git Service

Hulk did something
[bytom/vapor.git] / vendor / golang.org / x / crypto / openpgp / elgamal / elgamal.go
diff --git a/vendor/golang.org/x/crypto/openpgp/elgamal/elgamal.go b/vendor/golang.org/x/crypto/openpgp/elgamal/elgamal.go
new file mode 100644 (file)
index 0000000..73f4fe3
--- /dev/null
@@ -0,0 +1,122 @@
+// 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
+}