mirror of
https://github.com/cheat/cheat.git
synced 2024-11-25 15:31:36 +01:00
404 lines
12 KiB
Go
404 lines
12 KiB
Go
|
// 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 openpgp
|
|||
|
|
|||
|
import (
|
|||
|
"crypto"
|
|||
|
"crypto/rand"
|
|||
|
"crypto/rsa"
|
|||
|
goerrors "errors"
|
|||
|
"io"
|
|||
|
"math/big"
|
|||
|
"time"
|
|||
|
|
|||
|
"github.com/ProtonMail/go-crypto/openpgp/ecdh"
|
|||
|
"github.com/ProtonMail/go-crypto/openpgp/ecdsa"
|
|||
|
"github.com/ProtonMail/go-crypto/openpgp/eddsa"
|
|||
|
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
|||
|
"github.com/ProtonMail/go-crypto/openpgp/internal/algorithm"
|
|||
|
"github.com/ProtonMail/go-crypto/openpgp/internal/ecc"
|
|||
|
"github.com/ProtonMail/go-crypto/openpgp/packet"
|
|||
|
)
|
|||
|
|
|||
|
// NewEntity returns an Entity that contains a fresh RSA/RSA keypair with a
|
|||
|
// single identity composed of the given full name, comment and email, any of
|
|||
|
// which may be empty but must not contain any of "()<>\x00".
|
|||
|
// If config is nil, sensible defaults will be used.
|
|||
|
func NewEntity(name, comment, email string, config *packet.Config) (*Entity, error) {
|
|||
|
creationTime := config.Now()
|
|||
|
keyLifetimeSecs := config.KeyLifetime()
|
|||
|
|
|||
|
// Generate a primary signing key
|
|||
|
primaryPrivRaw, err := newSigner(config)
|
|||
|
if err != nil {
|
|||
|
return nil, err
|
|||
|
}
|
|||
|
primary := packet.NewSignerPrivateKey(creationTime, primaryPrivRaw)
|
|||
|
if config != nil && config.V5Keys {
|
|||
|
primary.UpgradeToV5()
|
|||
|
}
|
|||
|
|
|||
|
e := &Entity{
|
|||
|
PrimaryKey: &primary.PublicKey,
|
|||
|
PrivateKey: primary,
|
|||
|
Identities: make(map[string]*Identity),
|
|||
|
Subkeys: []Subkey{},
|
|||
|
}
|
|||
|
|
|||
|
err = e.addUserId(name, comment, email, config, creationTime, keyLifetimeSecs)
|
|||
|
if err != nil {
|
|||
|
return nil, err
|
|||
|
}
|
|||
|
|
|||
|
// NOTE: No key expiry here, but we will not return this subkey in EncryptionKey()
|
|||
|
// if the primary/master key has expired.
|
|||
|
err = e.addEncryptionSubkey(config, creationTime, 0)
|
|||
|
if err != nil {
|
|||
|
return nil, err
|
|||
|
}
|
|||
|
|
|||
|
return e, nil
|
|||
|
}
|
|||
|
|
|||
|
func (t *Entity) AddUserId(name, comment, email string, config *packet.Config) error {
|
|||
|
creationTime := config.Now()
|
|||
|
keyLifetimeSecs := config.KeyLifetime()
|
|||
|
return t.addUserId(name, comment, email, config, creationTime, keyLifetimeSecs)
|
|||
|
}
|
|||
|
|
|||
|
func (t *Entity) addUserId(name, comment, email string, config *packet.Config, creationTime time.Time, keyLifetimeSecs uint32) error {
|
|||
|
uid := packet.NewUserId(name, comment, email)
|
|||
|
if uid == nil {
|
|||
|
return errors.InvalidArgumentError("user id field contained invalid characters")
|
|||
|
}
|
|||
|
|
|||
|
if _, ok := t.Identities[uid.Id]; ok {
|
|||
|
return errors.InvalidArgumentError("user id exist")
|
|||
|
}
|
|||
|
|
|||
|
primary := t.PrivateKey
|
|||
|
|
|||
|
isPrimaryId := len(t.Identities) == 0
|
|||
|
|
|||
|
selfSignature := &packet.Signature{
|
|||
|
Version: primary.PublicKey.Version,
|
|||
|
SigType: packet.SigTypePositiveCert,
|
|||
|
PubKeyAlgo: primary.PublicKey.PubKeyAlgo,
|
|||
|
Hash: config.Hash(),
|
|||
|
CreationTime: creationTime,
|
|||
|
KeyLifetimeSecs: &keyLifetimeSecs,
|
|||
|
IssuerKeyId: &primary.PublicKey.KeyId,
|
|||
|
IssuerFingerprint: primary.PublicKey.Fingerprint,
|
|||
|
IsPrimaryId: &isPrimaryId,
|
|||
|
FlagsValid: true,
|
|||
|
FlagSign: true,
|
|||
|
FlagCertify: true,
|
|||
|
MDC: true, // true by default, see 5.8 vs. 5.14
|
|||
|
AEAD: config.AEAD() != nil,
|
|||
|
V5Keys: config != nil && config.V5Keys,
|
|||
|
}
|
|||
|
|
|||
|
// Set the PreferredHash for the SelfSignature from the packet.Config.
|
|||
|
// If it is not the must-implement algorithm from rfc4880bis, append that.
|
|||
|
selfSignature.PreferredHash = []uint8{hashToHashId(config.Hash())}
|
|||
|
if config.Hash() != crypto.SHA256 {
|
|||
|
selfSignature.PreferredHash = append(selfSignature.PreferredHash, hashToHashId(crypto.SHA256))
|
|||
|
}
|
|||
|
|
|||
|
// Likewise for DefaultCipher.
|
|||
|
selfSignature.PreferredSymmetric = []uint8{uint8(config.Cipher())}
|
|||
|
if config.Cipher() != packet.CipherAES128 {
|
|||
|
selfSignature.PreferredSymmetric = append(selfSignature.PreferredSymmetric, uint8(packet.CipherAES128))
|
|||
|
}
|
|||
|
|
|||
|
// We set CompressionNone as the preferred compression algorithm because
|
|||
|
// of compression side channel attacks, then append the configured
|
|||
|
// DefaultCompressionAlgo if any is set (to signal support for cases
|
|||
|
// where the application knows that using compression is safe).
|
|||
|
selfSignature.PreferredCompression = []uint8{uint8(packet.CompressionNone)}
|
|||
|
if config.Compression() != packet.CompressionNone {
|
|||
|
selfSignature.PreferredCompression = append(selfSignature.PreferredCompression, uint8(config.Compression()))
|
|||
|
}
|
|||
|
|
|||
|
// And for DefaultMode.
|
|||
|
selfSignature.PreferredAEAD = []uint8{uint8(config.AEAD().Mode())}
|
|||
|
if config.AEAD().Mode() != packet.AEADModeEAX {
|
|||
|
selfSignature.PreferredAEAD = append(selfSignature.PreferredAEAD, uint8(packet.AEADModeEAX))
|
|||
|
}
|
|||
|
|
|||
|
// User ID binding signature
|
|||
|
err := selfSignature.SignUserId(uid.Id, &primary.PublicKey, primary, config)
|
|||
|
if err != nil {
|
|||
|
return err
|
|||
|
}
|
|||
|
t.Identities[uid.Id] = &Identity{
|
|||
|
Name: uid.Id,
|
|||
|
UserId: uid,
|
|||
|
SelfSignature: selfSignature,
|
|||
|
Signatures: []*packet.Signature{selfSignature},
|
|||
|
}
|
|||
|
return nil
|
|||
|
}
|
|||
|
|
|||
|
// AddSigningSubkey adds a signing keypair as a subkey to the Entity.
|
|||
|
// If config is nil, sensible defaults will be used.
|
|||
|
func (e *Entity) AddSigningSubkey(config *packet.Config) error {
|
|||
|
creationTime := config.Now()
|
|||
|
keyLifetimeSecs := config.KeyLifetime()
|
|||
|
|
|||
|
subPrivRaw, err := newSigner(config)
|
|||
|
if err != nil {
|
|||
|
return err
|
|||
|
}
|
|||
|
sub := packet.NewSignerPrivateKey(creationTime, subPrivRaw)
|
|||
|
|
|||
|
subkey := Subkey{
|
|||
|
PublicKey: &sub.PublicKey,
|
|||
|
PrivateKey: sub,
|
|||
|
Sig: &packet.Signature{
|
|||
|
Version: e.PrimaryKey.Version,
|
|||
|
CreationTime: creationTime,
|
|||
|
KeyLifetimeSecs: &keyLifetimeSecs,
|
|||
|
SigType: packet.SigTypeSubkeyBinding,
|
|||
|
PubKeyAlgo: e.PrimaryKey.PubKeyAlgo,
|
|||
|
Hash: config.Hash(),
|
|||
|
FlagsValid: true,
|
|||
|
FlagSign: true,
|
|||
|
IssuerKeyId: &e.PrimaryKey.KeyId,
|
|||
|
EmbeddedSignature: &packet.Signature{
|
|||
|
Version: e.PrimaryKey.Version,
|
|||
|
CreationTime: creationTime,
|
|||
|
SigType: packet.SigTypePrimaryKeyBinding,
|
|||
|
PubKeyAlgo: sub.PublicKey.PubKeyAlgo,
|
|||
|
Hash: config.Hash(),
|
|||
|
IssuerKeyId: &e.PrimaryKey.KeyId,
|
|||
|
},
|
|||
|
},
|
|||
|
}
|
|||
|
if config != nil && config.V5Keys {
|
|||
|
subkey.PublicKey.UpgradeToV5()
|
|||
|
}
|
|||
|
|
|||
|
err = subkey.Sig.EmbeddedSignature.CrossSignKey(subkey.PublicKey, e.PrimaryKey, subkey.PrivateKey, config)
|
|||
|
if err != nil {
|
|||
|
return err
|
|||
|
}
|
|||
|
|
|||
|
subkey.PublicKey.IsSubkey = true
|
|||
|
subkey.PrivateKey.IsSubkey = true
|
|||
|
if err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config); err != nil {
|
|||
|
return err
|
|||
|
}
|
|||
|
|
|||
|
e.Subkeys = append(e.Subkeys, subkey)
|
|||
|
return nil
|
|||
|
}
|
|||
|
|
|||
|
// AddEncryptionSubkey adds an encryption keypair as a subkey to the Entity.
|
|||
|
// If config is nil, sensible defaults will be used.
|
|||
|
func (e *Entity) AddEncryptionSubkey(config *packet.Config) error {
|
|||
|
creationTime := config.Now()
|
|||
|
keyLifetimeSecs := config.KeyLifetime()
|
|||
|
return e.addEncryptionSubkey(config, creationTime, keyLifetimeSecs)
|
|||
|
}
|
|||
|
|
|||
|
func (e *Entity) addEncryptionSubkey(config *packet.Config, creationTime time.Time, keyLifetimeSecs uint32) error {
|
|||
|
subPrivRaw, err := newDecrypter(config)
|
|||
|
if err != nil {
|
|||
|
return err
|
|||
|
}
|
|||
|
sub := packet.NewDecrypterPrivateKey(creationTime, subPrivRaw)
|
|||
|
|
|||
|
subkey := Subkey{
|
|||
|
PublicKey: &sub.PublicKey,
|
|||
|
PrivateKey: sub,
|
|||
|
Sig: &packet.Signature{
|
|||
|
Version: e.PrimaryKey.Version,
|
|||
|
CreationTime: creationTime,
|
|||
|
KeyLifetimeSecs: &keyLifetimeSecs,
|
|||
|
SigType: packet.SigTypeSubkeyBinding,
|
|||
|
PubKeyAlgo: e.PrimaryKey.PubKeyAlgo,
|
|||
|
Hash: config.Hash(),
|
|||
|
FlagsValid: true,
|
|||
|
FlagEncryptStorage: true,
|
|||
|
FlagEncryptCommunications: true,
|
|||
|
IssuerKeyId: &e.PrimaryKey.KeyId,
|
|||
|
},
|
|||
|
}
|
|||
|
if config != nil && config.V5Keys {
|
|||
|
subkey.PublicKey.UpgradeToV5()
|
|||
|
}
|
|||
|
|
|||
|
subkey.PublicKey.IsSubkey = true
|
|||
|
subkey.PrivateKey.IsSubkey = true
|
|||
|
if err = subkey.Sig.SignKey(subkey.PublicKey, e.PrivateKey, config); err != nil {
|
|||
|
return err
|
|||
|
}
|
|||
|
|
|||
|
e.Subkeys = append(e.Subkeys, subkey)
|
|||
|
return nil
|
|||
|
}
|
|||
|
|
|||
|
// Generates a signing key
|
|||
|
func newSigner(config *packet.Config) (signer interface{}, err error) {
|
|||
|
switch config.PublicKeyAlgorithm() {
|
|||
|
case packet.PubKeyAlgoRSA:
|
|||
|
bits := config.RSAModulusBits()
|
|||
|
if bits < 1024 {
|
|||
|
return nil, errors.InvalidArgumentError("bits must be >= 1024")
|
|||
|
}
|
|||
|
if config != nil && len(config.RSAPrimes) >= 2 {
|
|||
|
primes := config.RSAPrimes[0:2]
|
|||
|
config.RSAPrimes = config.RSAPrimes[2:]
|
|||
|
return generateRSAKeyWithPrimes(config.Random(), 2, bits, primes)
|
|||
|
}
|
|||
|
return rsa.GenerateKey(config.Random(), bits)
|
|||
|
case packet.PubKeyAlgoEdDSA:
|
|||
|
curve := ecc.FindEdDSAByGenName(string(config.CurveName()))
|
|||
|
if curve == nil {
|
|||
|
return nil, errors.InvalidArgumentError("unsupported curve")
|
|||
|
}
|
|||
|
|
|||
|
priv, err := eddsa.GenerateKey(config.Random(), curve)
|
|||
|
if err != nil {
|
|||
|
return nil, err
|
|||
|
}
|
|||
|
return priv, nil
|
|||
|
case packet.PubKeyAlgoECDSA:
|
|||
|
curve := ecc.FindECDSAByGenName(string(config.CurveName()))
|
|||
|
if curve == nil {
|
|||
|
return nil, errors.InvalidArgumentError("unsupported curve")
|
|||
|
}
|
|||
|
|
|||
|
priv, err := ecdsa.GenerateKey(config.Random(), curve)
|
|||
|
if err != nil {
|
|||
|
return nil, err
|
|||
|
}
|
|||
|
return priv, nil
|
|||
|
default:
|
|||
|
return nil, errors.InvalidArgumentError("unsupported public key algorithm")
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Generates an encryption/decryption key
|
|||
|
func newDecrypter(config *packet.Config) (decrypter interface{}, err error) {
|
|||
|
switch config.PublicKeyAlgorithm() {
|
|||
|
case packet.PubKeyAlgoRSA:
|
|||
|
bits := config.RSAModulusBits()
|
|||
|
if bits < 1024 {
|
|||
|
return nil, errors.InvalidArgumentError("bits must be >= 1024")
|
|||
|
}
|
|||
|
if config != nil && len(config.RSAPrimes) >= 2 {
|
|||
|
primes := config.RSAPrimes[0:2]
|
|||
|
config.RSAPrimes = config.RSAPrimes[2:]
|
|||
|
return generateRSAKeyWithPrimes(config.Random(), 2, bits, primes)
|
|||
|
}
|
|||
|
return rsa.GenerateKey(config.Random(), bits)
|
|||
|
case packet.PubKeyAlgoEdDSA, packet.PubKeyAlgoECDSA:
|
|||
|
fallthrough // When passing EdDSA or ECDSA, we generate an ECDH subkey
|
|||
|
case packet.PubKeyAlgoECDH:
|
|||
|
var kdf = ecdh.KDF{
|
|||
|
Hash: algorithm.SHA512,
|
|||
|
Cipher: algorithm.AES256,
|
|||
|
}
|
|||
|
curve := ecc.FindECDHByGenName(string(config.CurveName()))
|
|||
|
if curve == nil {
|
|||
|
return nil, errors.InvalidArgumentError("unsupported curve")
|
|||
|
}
|
|||
|
return ecdh.GenerateKey(config.Random(), curve, kdf)
|
|||
|
default:
|
|||
|
return nil, errors.InvalidArgumentError("unsupported public key algorithm")
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
var bigOne = big.NewInt(1)
|
|||
|
|
|||
|
// generateRSAKeyWithPrimes generates a multi-prime RSA keypair of the
|
|||
|
// given bit size, using the given random source and prepopulated primes.
|
|||
|
func generateRSAKeyWithPrimes(random io.Reader, nprimes int, bits int, prepopulatedPrimes []*big.Int) (*rsa.PrivateKey, error) {
|
|||
|
priv := new(rsa.PrivateKey)
|
|||
|
priv.E = 65537
|
|||
|
|
|||
|
if nprimes < 2 {
|
|||
|
return nil, goerrors.New("generateRSAKeyWithPrimes: nprimes must be >= 2")
|
|||
|
}
|
|||
|
|
|||
|
if bits < 1024 {
|
|||
|
return nil, goerrors.New("generateRSAKeyWithPrimes: bits must be >= 1024")
|
|||
|
}
|
|||
|
|
|||
|
primes := make([]*big.Int, nprimes)
|
|||
|
|
|||
|
NextSetOfPrimes:
|
|||
|
for {
|
|||
|
todo := bits
|
|||
|
// crypto/rand should set the top two bits in each prime.
|
|||
|
// Thus each prime has the form
|
|||
|
// p_i = 2^bitlen(p_i) × 0.11... (in base 2).
|
|||
|
// And the product is:
|
|||
|
// P = 2^todo × α
|
|||
|
// where α is the product of nprimes numbers of the form 0.11...
|
|||
|
//
|
|||
|
// If α < 1/2 (which can happen for nprimes > 2), we need to
|
|||
|
// shift todo to compensate for lost bits: the mean value of 0.11...
|
|||
|
// is 7/8, so todo + shift - nprimes * log2(7/8) ~= bits - 1/2
|
|||
|
// will give good results.
|
|||
|
if nprimes >= 7 {
|
|||
|
todo += (nprimes - 2) / 5
|
|||
|
}
|
|||
|
for i := 0; i < nprimes; i++ {
|
|||
|
var err error
|
|||
|
if len(prepopulatedPrimes) == 0 {
|
|||
|
primes[i], err = rand.Prime(random, todo/(nprimes-i))
|
|||
|
if err != nil {
|
|||
|
return nil, err
|
|||
|
}
|
|||
|
} else {
|
|||
|
primes[i] = prepopulatedPrimes[0]
|
|||
|
prepopulatedPrimes = prepopulatedPrimes[1:]
|
|||
|
}
|
|||
|
|
|||
|
todo -= primes[i].BitLen()
|
|||
|
}
|
|||
|
|
|||
|
// Make sure that primes is pairwise unequal.
|
|||
|
for i, prime := range primes {
|
|||
|
for j := 0; j < i; j++ {
|
|||
|
if prime.Cmp(primes[j]) == 0 {
|
|||
|
continue NextSetOfPrimes
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
n := new(big.Int).Set(bigOne)
|
|||
|
totient := new(big.Int).Set(bigOne)
|
|||
|
pminus1 := new(big.Int)
|
|||
|
for _, prime := range primes {
|
|||
|
n.Mul(n, prime)
|
|||
|
pminus1.Sub(prime, bigOne)
|
|||
|
totient.Mul(totient, pminus1)
|
|||
|
}
|
|||
|
if n.BitLen() != bits {
|
|||
|
// This should never happen for nprimes == 2 because
|
|||
|
// crypto/rand should set the top two bits in each prime.
|
|||
|
// For nprimes > 2 we hope it does not happen often.
|
|||
|
continue NextSetOfPrimes
|
|||
|
}
|
|||
|
|
|||
|
priv.D = new(big.Int)
|
|||
|
e := big.NewInt(int64(priv.E))
|
|||
|
ok := priv.D.ModInverse(e, totient)
|
|||
|
|
|||
|
if ok != nil {
|
|||
|
priv.Primes = primes
|
|||
|
priv.N = n
|
|||
|
break
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
priv.Precompute()
|
|||
|
return priv, nil
|
|||
|
}
|