...

Source file src/github.com/ProtonMail/go-crypto/openpgp/elgamal/elgamal.go

Documentation: github.com/ProtonMail/go-crypto/openpgp/elgamal

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package elgamal implements ElGamal encryption, suitable for OpenPGP,
     6  // as specified in "A Public-Key Cryptosystem and a Signature Scheme Based on
     7  // Discrete Logarithms," IEEE Transactions on Information Theory, v. IT-31,
     8  // n. 4, 1985, pp. 469-472.
     9  //
    10  // This form of ElGamal embeds PKCS#1 v1.5 padding, which may make it
    11  // unsuitable for other protocols. RSA should be used in preference in any
    12  // case.
    13  package elgamal // import "github.com/ProtonMail/go-crypto/openpgp/elgamal"
    14  
    15  import (
    16  	"crypto/rand"
    17  	"crypto/subtle"
    18  	"errors"
    19  	"io"
    20  	"math/big"
    21  )
    22  
    23  // PublicKey represents an ElGamal public key.
    24  type PublicKey struct {
    25  	G, P, Y *big.Int
    26  }
    27  
    28  // PrivateKey represents an ElGamal private key.
    29  type PrivateKey struct {
    30  	PublicKey
    31  	X *big.Int
    32  }
    33  
    34  // Encrypt encrypts the given message to the given public key. The result is a
    35  // pair of integers. Errors can result from reading random, or because msg is
    36  // too large to be encrypted to the public key.
    37  func Encrypt(random io.Reader, pub *PublicKey, msg []byte) (c1, c2 *big.Int, err error) {
    38  	pLen := (pub.P.BitLen() + 7) / 8
    39  	if len(msg) > pLen-11 {
    40  		err = errors.New("elgamal: message too long")
    41  		return
    42  	}
    43  
    44  	// EM = 0x02 || PS || 0x00 || M
    45  	em := make([]byte, pLen-1)
    46  	em[0] = 2
    47  	ps, mm := em[1:len(em)-len(msg)-1], em[len(em)-len(msg):]
    48  	err = nonZeroRandomBytes(ps, random)
    49  	if err != nil {
    50  		return
    51  	}
    52  	em[len(em)-len(msg)-1] = 0
    53  	copy(mm, msg)
    54  
    55  	m := new(big.Int).SetBytes(em)
    56  
    57  	k, err := rand.Int(random, pub.P)
    58  	if err != nil {
    59  		return
    60  	}
    61  
    62  	c1 = new(big.Int).Exp(pub.G, k, pub.P)
    63  	s := new(big.Int).Exp(pub.Y, k, pub.P)
    64  	c2 = s.Mul(s, m)
    65  	c2.Mod(c2, pub.P)
    66  
    67  	return
    68  }
    69  
    70  // Decrypt takes two integers, resulting from an ElGamal encryption, and
    71  // returns the plaintext of the message. An error can result only if the
    72  // ciphertext is invalid. Users should keep in mind that this is a padding
    73  // oracle and thus, if exposed to an adaptive chosen ciphertext attack, can
    74  // be used to break the cryptosystem.  See “Chosen Ciphertext Attacks
    75  // Against Protocols Based on the RSA Encryption Standard PKCS #1”, Daniel
    76  // Bleichenbacher, Advances in Cryptology (Crypto '98),
    77  func Decrypt(priv *PrivateKey, c1, c2 *big.Int) (msg []byte, err error) {
    78  	s := new(big.Int).Exp(c1, priv.X, priv.P)
    79  	if s.ModInverse(s, priv.P) == nil {
    80  		return nil, errors.New("elgamal: invalid private key")
    81  	}
    82  	s.Mul(s, c2)
    83  	s.Mod(s, priv.P)
    84  	em := s.Bytes()
    85  
    86  	firstByteIsTwo := subtle.ConstantTimeByteEq(em[0], 2)
    87  
    88  	// The remainder of the plaintext must be a string of non-zero random
    89  	// octets, followed by a 0, followed by the message.
    90  	//   lookingForIndex: 1 iff we are still looking for the zero.
    91  	//   index: the offset of the first zero byte.
    92  	var lookingForIndex, index int
    93  	lookingForIndex = 1
    94  
    95  	for i := 1; i < len(em); i++ {
    96  		equals0 := subtle.ConstantTimeByteEq(em[i], 0)
    97  		index = subtle.ConstantTimeSelect(lookingForIndex&equals0, i, index)
    98  		lookingForIndex = subtle.ConstantTimeSelect(equals0, 0, lookingForIndex)
    99  	}
   100  
   101  	if firstByteIsTwo != 1 || lookingForIndex != 0 || index < 9 {
   102  		return nil, errors.New("elgamal: decryption error")
   103  	}
   104  	return em[index+1:], nil
   105  }
   106  
   107  // nonZeroRandomBytes fills the given slice with non-zero random octets.
   108  func nonZeroRandomBytes(s []byte, rand io.Reader) (err error) {
   109  	_, err = io.ReadFull(rand, s)
   110  	if err != nil {
   111  		return
   112  	}
   113  
   114  	for i := 0; i < len(s); i++ {
   115  		for s[i] == 0 {
   116  			_, err = io.ReadFull(rand, s[i:i+1])
   117  			if err != nil {
   118  				return
   119  			}
   120  		}
   121  	}
   122  
   123  	return
   124  }
   125  

View as plain text