mirror of
https://github.com/rocky-linux/peridot.git
synced 2024-12-04 02:26:27 +00:00
838 lines
22 KiB
Go
838 lines
22 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 packet
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto"
|
|
"crypto/cipher"
|
|
"crypto/dsa"
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/sha1"
|
|
"io"
|
|
"io/ioutil"
|
|
"math/big"
|
|
"strconv"
|
|
"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/elgamal"
|
|
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
|
"github.com/ProtonMail/go-crypto/openpgp/internal/encoding"
|
|
"github.com/ProtonMail/go-crypto/openpgp/s2k"
|
|
)
|
|
|
|
// PrivateKey represents a possibly encrypted private key. See RFC 4880,
|
|
// section 5.5.3.
|
|
type PrivateKey struct {
|
|
PublicKey
|
|
Encrypted bool // if true then the private key is unavailable until Decrypt has been called.
|
|
encryptedData []byte
|
|
cipher CipherFunction
|
|
s2k func(out, in []byte)
|
|
// An *{rsa|dsa|elgamal|ecdh|ecdsa|ed25519}.PrivateKey or
|
|
// crypto.Signer/crypto.Decrypter (Decryptor RSA only).
|
|
PrivateKey interface{}
|
|
sha1Checksum bool
|
|
iv []byte
|
|
|
|
// Type of encryption of the S2K packet
|
|
// Allowed values are 0 (Not encrypted), 254 (SHA1), or
|
|
// 255 (2-byte checksum)
|
|
s2kType S2KType
|
|
// Full parameters of the S2K packet
|
|
s2kParams *s2k.Params
|
|
}
|
|
|
|
// S2KType s2k packet type
|
|
type S2KType uint8
|
|
|
|
const (
|
|
// S2KNON unencrypt
|
|
S2KNON S2KType = 0
|
|
// S2KSHA1 sha1 sum check
|
|
S2KSHA1 S2KType = 254
|
|
// S2KCHECKSUM sum check
|
|
S2KCHECKSUM S2KType = 255
|
|
)
|
|
|
|
func NewRSAPrivateKey(creationTime time.Time, priv *rsa.PrivateKey) *PrivateKey {
|
|
pk := new(PrivateKey)
|
|
pk.PublicKey = *NewRSAPublicKey(creationTime, &priv.PublicKey)
|
|
pk.PrivateKey = priv
|
|
return pk
|
|
}
|
|
|
|
func NewDSAPrivateKey(creationTime time.Time, priv *dsa.PrivateKey) *PrivateKey {
|
|
pk := new(PrivateKey)
|
|
pk.PublicKey = *NewDSAPublicKey(creationTime, &priv.PublicKey)
|
|
pk.PrivateKey = priv
|
|
return pk
|
|
}
|
|
|
|
func NewElGamalPrivateKey(creationTime time.Time, priv *elgamal.PrivateKey) *PrivateKey {
|
|
pk := new(PrivateKey)
|
|
pk.PublicKey = *NewElGamalPublicKey(creationTime, &priv.PublicKey)
|
|
pk.PrivateKey = priv
|
|
return pk
|
|
}
|
|
|
|
func NewECDSAPrivateKey(creationTime time.Time, priv *ecdsa.PrivateKey) *PrivateKey {
|
|
pk := new(PrivateKey)
|
|
pk.PublicKey = *NewECDSAPublicKey(creationTime, &priv.PublicKey)
|
|
pk.PrivateKey = priv
|
|
return pk
|
|
}
|
|
|
|
func NewEdDSAPrivateKey(creationTime time.Time, priv *eddsa.PrivateKey) *PrivateKey {
|
|
pk := new(PrivateKey)
|
|
pk.PublicKey = *NewEdDSAPublicKey(creationTime, &priv.PublicKey)
|
|
pk.PrivateKey = priv
|
|
return pk
|
|
}
|
|
|
|
func NewECDHPrivateKey(creationTime time.Time, priv *ecdh.PrivateKey) *PrivateKey {
|
|
pk := new(PrivateKey)
|
|
pk.PublicKey = *NewECDHPublicKey(creationTime, &priv.PublicKey)
|
|
pk.PrivateKey = priv
|
|
return pk
|
|
}
|
|
|
|
// NewSignerPrivateKey creates a PrivateKey from a crypto.Signer that
|
|
// implements RSA, ECDSA or EdDSA.
|
|
func NewSignerPrivateKey(creationTime time.Time, signer interface{}) *PrivateKey {
|
|
pk := new(PrivateKey)
|
|
// In general, the public Keys should be used as pointers. We still
|
|
// type-switch on the values, for backwards-compatibility.
|
|
switch pubkey := signer.(type) {
|
|
case *rsa.PrivateKey:
|
|
pk.PublicKey = *NewRSAPublicKey(creationTime, &pubkey.PublicKey)
|
|
case rsa.PrivateKey:
|
|
pk.PublicKey = *NewRSAPublicKey(creationTime, &pubkey.PublicKey)
|
|
case *ecdsa.PrivateKey:
|
|
pk.PublicKey = *NewECDSAPublicKey(creationTime, &pubkey.PublicKey)
|
|
case ecdsa.PrivateKey:
|
|
pk.PublicKey = *NewECDSAPublicKey(creationTime, &pubkey.PublicKey)
|
|
case *eddsa.PrivateKey:
|
|
pk.PublicKey = *NewEdDSAPublicKey(creationTime, &pubkey.PublicKey)
|
|
case eddsa.PrivateKey:
|
|
pk.PublicKey = *NewEdDSAPublicKey(creationTime, &pubkey.PublicKey)
|
|
default:
|
|
panic("openpgp: unknown signer type in NewSignerPrivateKey")
|
|
}
|
|
pk.PrivateKey = signer
|
|
return pk
|
|
}
|
|
|
|
// NewDecrypterPrivateKey creates a PrivateKey from a *{rsa|elgamal|ecdh}.PrivateKey.
|
|
func NewDecrypterPrivateKey(creationTime time.Time, decrypter interface{}) *PrivateKey {
|
|
pk := new(PrivateKey)
|
|
switch priv := decrypter.(type) {
|
|
case *rsa.PrivateKey:
|
|
pk.PublicKey = *NewRSAPublicKey(creationTime, &priv.PublicKey)
|
|
case *elgamal.PrivateKey:
|
|
pk.PublicKey = *NewElGamalPublicKey(creationTime, &priv.PublicKey)
|
|
case *ecdh.PrivateKey:
|
|
pk.PublicKey = *NewECDHPublicKey(creationTime, &priv.PublicKey)
|
|
default:
|
|
panic("openpgp: unknown decrypter type in NewDecrypterPrivateKey")
|
|
}
|
|
pk.PrivateKey = decrypter
|
|
return pk
|
|
}
|
|
|
|
func (pk *PrivateKey) parse(r io.Reader) (err error) {
|
|
err = (&pk.PublicKey).parse(r)
|
|
if err != nil {
|
|
return
|
|
}
|
|
v5 := pk.PublicKey.Version == 5
|
|
|
|
var buf [1]byte
|
|
_, err = readFull(r, buf[:])
|
|
if err != nil {
|
|
return
|
|
}
|
|
pk.s2kType = S2KType(buf[0])
|
|
var optCount [1]byte
|
|
if v5 {
|
|
if _, err = readFull(r, optCount[:]); err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
switch pk.s2kType {
|
|
case S2KNON:
|
|
pk.s2k = nil
|
|
pk.Encrypted = false
|
|
case S2KSHA1, S2KCHECKSUM:
|
|
if v5 && pk.s2kType == S2KCHECKSUM {
|
|
return errors.StructuralError("wrong s2k identifier for version 5")
|
|
}
|
|
_, err = readFull(r, buf[:])
|
|
if err != nil {
|
|
return
|
|
}
|
|
pk.cipher = CipherFunction(buf[0])
|
|
if pk.cipher != 0 && !pk.cipher.IsSupported() {
|
|
return errors.UnsupportedError("unsupported cipher function in private key")
|
|
}
|
|
pk.s2kParams, err = s2k.ParseIntoParams(r)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if pk.s2kParams.Dummy() {
|
|
return
|
|
}
|
|
pk.s2k, err = pk.s2kParams.Function()
|
|
if err != nil {
|
|
return
|
|
}
|
|
pk.Encrypted = true
|
|
if pk.s2kType == S2KSHA1 {
|
|
pk.sha1Checksum = true
|
|
}
|
|
default:
|
|
return errors.UnsupportedError("deprecated s2k function in private key")
|
|
}
|
|
|
|
if pk.Encrypted {
|
|
blockSize := pk.cipher.blockSize()
|
|
if blockSize == 0 {
|
|
return errors.UnsupportedError("unsupported cipher in private key: " + strconv.Itoa(int(pk.cipher)))
|
|
}
|
|
pk.iv = make([]byte, blockSize)
|
|
_, err = readFull(r, pk.iv)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
var privateKeyData []byte
|
|
if v5 {
|
|
var n [4]byte /* secret material four octet count */
|
|
_, err = readFull(r, n[:])
|
|
if err != nil {
|
|
return
|
|
}
|
|
count := uint32(uint32(n[0])<<24 | uint32(n[1])<<16 | uint32(n[2])<<8 | uint32(n[3]))
|
|
if !pk.Encrypted {
|
|
count = count + 2 /* two octet checksum */
|
|
}
|
|
privateKeyData = make([]byte, count)
|
|
_, err = readFull(r, privateKeyData)
|
|
if err != nil {
|
|
return
|
|
}
|
|
} else {
|
|
privateKeyData, err = ioutil.ReadAll(r)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
if !pk.Encrypted {
|
|
if len(privateKeyData) < 2 {
|
|
return errors.StructuralError("truncated private key data")
|
|
}
|
|
var sum uint16
|
|
for i := 0; i < len(privateKeyData)-2; i++ {
|
|
sum += uint16(privateKeyData[i])
|
|
}
|
|
if privateKeyData[len(privateKeyData)-2] != uint8(sum>>8) ||
|
|
privateKeyData[len(privateKeyData)-1] != uint8(sum) {
|
|
return errors.StructuralError("private key checksum failure")
|
|
}
|
|
privateKeyData = privateKeyData[:len(privateKeyData)-2]
|
|
return pk.parsePrivateKey(privateKeyData)
|
|
}
|
|
|
|
pk.encryptedData = privateKeyData
|
|
return
|
|
}
|
|
|
|
// Dummy returns true if the private key is a dummy key. This is a GNU extension.
|
|
func (pk *PrivateKey) Dummy() bool {
|
|
return pk.s2kParams.Dummy()
|
|
}
|
|
|
|
func mod64kHash(d []byte) uint16 {
|
|
var h uint16
|
|
for _, b := range d {
|
|
h += uint16(b)
|
|
}
|
|
return h
|
|
}
|
|
|
|
func (pk *PrivateKey) Serialize(w io.Writer) (err error) {
|
|
contents := bytes.NewBuffer(nil)
|
|
err = pk.PublicKey.serializeWithoutHeaders(contents)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if _, err = contents.Write([]byte{uint8(pk.s2kType)}); err != nil {
|
|
return
|
|
}
|
|
|
|
optional := bytes.NewBuffer(nil)
|
|
if pk.Encrypted || pk.Dummy() {
|
|
optional.Write([]byte{uint8(pk.cipher)})
|
|
if err := pk.s2kParams.Serialize(optional); err != nil {
|
|
return err
|
|
}
|
|
if pk.Encrypted {
|
|
optional.Write(pk.iv)
|
|
}
|
|
}
|
|
if pk.Version == 5 {
|
|
contents.Write([]byte{uint8(optional.Len())})
|
|
}
|
|
io.Copy(contents, optional)
|
|
|
|
if !pk.Dummy() {
|
|
l := 0
|
|
var priv []byte
|
|
if !pk.Encrypted {
|
|
buf := bytes.NewBuffer(nil)
|
|
err = pk.serializePrivateKey(buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
l = buf.Len()
|
|
checksum := mod64kHash(buf.Bytes())
|
|
buf.Write([]byte{byte(checksum >> 8), byte(checksum)})
|
|
priv = buf.Bytes()
|
|
} else {
|
|
priv, l = pk.encryptedData, len(pk.encryptedData)
|
|
}
|
|
|
|
if pk.Version == 5 {
|
|
contents.Write([]byte{byte(l >> 24), byte(l >> 16), byte(l >> 8), byte(l)})
|
|
}
|
|
contents.Write(priv)
|
|
}
|
|
|
|
ptype := packetTypePrivateKey
|
|
if pk.IsSubkey {
|
|
ptype = packetTypePrivateSubkey
|
|
}
|
|
err = serializeHeader(w, ptype, contents.Len())
|
|
if err != nil {
|
|
return
|
|
}
|
|
_, err = io.Copy(w, contents)
|
|
if err != nil {
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
func serializeRSAPrivateKey(w io.Writer, priv *rsa.PrivateKey) error {
|
|
if _, err := w.Write(new(encoding.MPI).SetBig(priv.D).EncodedBytes()); err != nil {
|
|
return err
|
|
}
|
|
if _, err := w.Write(new(encoding.MPI).SetBig(priv.Primes[1]).EncodedBytes()); err != nil {
|
|
return err
|
|
}
|
|
if _, err := w.Write(new(encoding.MPI).SetBig(priv.Primes[0]).EncodedBytes()); err != nil {
|
|
return err
|
|
}
|
|
_, err := w.Write(new(encoding.MPI).SetBig(priv.Precomputed.Qinv).EncodedBytes())
|
|
return err
|
|
}
|
|
|
|
func serializeDSAPrivateKey(w io.Writer, priv *dsa.PrivateKey) error {
|
|
_, err := w.Write(new(encoding.MPI).SetBig(priv.X).EncodedBytes())
|
|
return err
|
|
}
|
|
|
|
func serializeElGamalPrivateKey(w io.Writer, priv *elgamal.PrivateKey) error {
|
|
_, err := w.Write(new(encoding.MPI).SetBig(priv.X).EncodedBytes())
|
|
return err
|
|
}
|
|
|
|
func serializeECDSAPrivateKey(w io.Writer, priv *ecdsa.PrivateKey) error {
|
|
_, err := w.Write(encoding.NewMPI(priv.MarshalIntegerSecret()).EncodedBytes())
|
|
return err
|
|
}
|
|
|
|
func serializeEdDSAPrivateKey(w io.Writer, priv *eddsa.PrivateKey) error {
|
|
_, err := w.Write(encoding.NewMPI(priv.MarshalByteSecret()).EncodedBytes())
|
|
return err
|
|
}
|
|
|
|
func serializeECDHPrivateKey(w io.Writer, priv *ecdh.PrivateKey) error {
|
|
_, err := w.Write(encoding.NewMPI(priv.MarshalByteSecret()).EncodedBytes())
|
|
return err
|
|
}
|
|
|
|
// decrypt decrypts an encrypted private key using a decryption key.
|
|
func (pk *PrivateKey) decrypt(decryptionKey []byte) error {
|
|
if pk.Dummy() {
|
|
return errors.ErrDummyPrivateKey("dummy key found")
|
|
}
|
|
if !pk.Encrypted {
|
|
return nil
|
|
}
|
|
|
|
block := pk.cipher.new(decryptionKey)
|
|
cfb := cipher.NewCFBDecrypter(block, pk.iv)
|
|
|
|
data := make([]byte, len(pk.encryptedData))
|
|
cfb.XORKeyStream(data, pk.encryptedData)
|
|
|
|
if pk.sha1Checksum {
|
|
if len(data) < sha1.Size {
|
|
return errors.StructuralError("truncated private key data")
|
|
}
|
|
h := sha1.New()
|
|
h.Write(data[:len(data)-sha1.Size])
|
|
sum := h.Sum(nil)
|
|
if !bytes.Equal(sum, data[len(data)-sha1.Size:]) {
|
|
return errors.StructuralError("private key checksum failure")
|
|
}
|
|
data = data[:len(data)-sha1.Size]
|
|
} else {
|
|
if len(data) < 2 {
|
|
return errors.StructuralError("truncated private key data")
|
|
}
|
|
var sum uint16
|
|
for i := 0; i < len(data)-2; i++ {
|
|
sum += uint16(data[i])
|
|
}
|
|
if data[len(data)-2] != uint8(sum>>8) ||
|
|
data[len(data)-1] != uint8(sum) {
|
|
return errors.StructuralError("private key checksum failure")
|
|
}
|
|
data = data[:len(data)-2]
|
|
}
|
|
|
|
err := pk.parsePrivateKey(data)
|
|
if _, ok := err.(errors.KeyInvalidError); ok {
|
|
return errors.KeyInvalidError("invalid key parameters")
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Mark key as unencrypted
|
|
pk.s2kType = S2KNON
|
|
pk.s2k = nil
|
|
pk.Encrypted = false
|
|
pk.encryptedData = nil
|
|
|
|
return nil
|
|
}
|
|
|
|
func (pk *PrivateKey) decryptWithCache(passphrase []byte, keyCache *s2k.Cache) error {
|
|
if pk.Dummy() {
|
|
return errors.ErrDummyPrivateKey("dummy key found")
|
|
}
|
|
if !pk.Encrypted {
|
|
return nil
|
|
}
|
|
|
|
key, err := keyCache.GetOrComputeDerivedKey(passphrase, pk.s2kParams, pk.cipher.KeySize())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return pk.decrypt(key)
|
|
}
|
|
|
|
// Decrypt decrypts an encrypted private key using a passphrase.
|
|
func (pk *PrivateKey) Decrypt(passphrase []byte) error {
|
|
if pk.Dummy() {
|
|
return errors.ErrDummyPrivateKey("dummy key found")
|
|
}
|
|
if !pk.Encrypted {
|
|
return nil
|
|
}
|
|
|
|
key := make([]byte, pk.cipher.KeySize())
|
|
pk.s2k(key, passphrase)
|
|
return pk.decrypt(key)
|
|
}
|
|
|
|
// DecryptPrivateKeys decrypts all encrypted keys with the given config and passphrase.
|
|
// Avoids recomputation of similar s2k key derivations.
|
|
func DecryptPrivateKeys(keys []*PrivateKey, passphrase []byte) error {
|
|
// Create a cache to avoid recomputation of key derviations for the same passphrase.
|
|
s2kCache := &s2k.Cache{}
|
|
for _, key := range keys {
|
|
if key != nil && !key.Dummy() && key.Encrypted {
|
|
err := key.decryptWithCache(passphrase, s2kCache)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// encrypt encrypts an unencrypted private key.
|
|
func (pk *PrivateKey) encrypt(key []byte, params *s2k.Params, cipherFunction CipherFunction) error {
|
|
if pk.Dummy() {
|
|
return errors.ErrDummyPrivateKey("dummy key found")
|
|
}
|
|
if pk.Encrypted {
|
|
return nil
|
|
}
|
|
// check if encryptionKey has the correct size
|
|
if len(key) != cipherFunction.KeySize() {
|
|
return errors.InvalidArgumentError("supplied encryption key has the wrong size")
|
|
}
|
|
|
|
priv := bytes.NewBuffer(nil)
|
|
err := pk.serializePrivateKey(priv)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
pk.cipher = cipherFunction
|
|
pk.s2kParams = params
|
|
pk.s2k, err = pk.s2kParams.Function()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
privateKeyBytes := priv.Bytes()
|
|
pk.sha1Checksum = true
|
|
block := pk.cipher.new(key)
|
|
pk.iv = make([]byte, pk.cipher.blockSize())
|
|
_, err = rand.Read(pk.iv)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cfb := cipher.NewCFBEncrypter(block, pk.iv)
|
|
|
|
if pk.sha1Checksum {
|
|
pk.s2kType = S2KSHA1
|
|
h := sha1.New()
|
|
h.Write(privateKeyBytes)
|
|
sum := h.Sum(nil)
|
|
privateKeyBytes = append(privateKeyBytes, sum...)
|
|
} else {
|
|
pk.s2kType = S2KCHECKSUM
|
|
var sum uint16
|
|
for _, b := range privateKeyBytes {
|
|
sum += uint16(b)
|
|
}
|
|
priv.Write([]byte{uint8(sum >> 8), uint8(sum)})
|
|
}
|
|
|
|
pk.encryptedData = make([]byte, len(privateKeyBytes))
|
|
cfb.XORKeyStream(pk.encryptedData, privateKeyBytes)
|
|
pk.Encrypted = true
|
|
pk.PrivateKey = nil
|
|
return err
|
|
}
|
|
|
|
// EncryptWithConfig encrypts an unencrypted private key using the passphrase and the config.
|
|
func (pk *PrivateKey) EncryptWithConfig(passphrase []byte, config *Config) error {
|
|
params, err := s2k.Generate(config.Random(), config.S2K())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Derive an encryption key with the configured s2k function.
|
|
key := make([]byte, config.Cipher().KeySize())
|
|
s2k, err := params.Function()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
s2k(key, passphrase)
|
|
// Encrypt the private key with the derived encryption key.
|
|
return pk.encrypt(key, params, config.Cipher())
|
|
}
|
|
|
|
// EncryptPrivateKeys encrypts all unencrypted keys with the given config and passphrase.
|
|
// Only derives one key from the passphrase, which is then used to encrypt each key.
|
|
func EncryptPrivateKeys(keys []*PrivateKey, passphrase []byte, config *Config) error {
|
|
params, err := s2k.Generate(config.Random(), config.S2K())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Derive an encryption key with the configured s2k function.
|
|
encryptionKey := make([]byte, config.Cipher().KeySize())
|
|
s2k, err := params.Function()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
s2k(encryptionKey, passphrase)
|
|
for _, key := range keys {
|
|
if key != nil && !key.Dummy() && !key.Encrypted {
|
|
err = key.encrypt(encryptionKey, params, config.Cipher())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Encrypt encrypts an unencrypted private key using a passphrase.
|
|
func (pk *PrivateKey) Encrypt(passphrase []byte) error {
|
|
// Default config of private key encryption
|
|
config := &Config{
|
|
S2KConfig: &s2k.Config{
|
|
S2KMode: s2k.IteratedSaltedS2K,
|
|
S2KCount: 65536,
|
|
Hash: crypto.SHA256,
|
|
} ,
|
|
DefaultCipher: CipherAES256,
|
|
}
|
|
return pk.EncryptWithConfig(passphrase, config)
|
|
}
|
|
|
|
func (pk *PrivateKey) serializePrivateKey(w io.Writer) (err error) {
|
|
switch priv := pk.PrivateKey.(type) {
|
|
case *rsa.PrivateKey:
|
|
err = serializeRSAPrivateKey(w, priv)
|
|
case *dsa.PrivateKey:
|
|
err = serializeDSAPrivateKey(w, priv)
|
|
case *elgamal.PrivateKey:
|
|
err = serializeElGamalPrivateKey(w, priv)
|
|
case *ecdsa.PrivateKey:
|
|
err = serializeECDSAPrivateKey(w, priv)
|
|
case *eddsa.PrivateKey:
|
|
err = serializeEdDSAPrivateKey(w, priv)
|
|
case *ecdh.PrivateKey:
|
|
err = serializeECDHPrivateKey(w, priv)
|
|
default:
|
|
err = errors.InvalidArgumentError("unknown private key type")
|
|
}
|
|
return
|
|
}
|
|
|
|
func (pk *PrivateKey) parsePrivateKey(data []byte) (err error) {
|
|
switch pk.PublicKey.PubKeyAlgo {
|
|
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoRSAEncryptOnly:
|
|
return pk.parseRSAPrivateKey(data)
|
|
case PubKeyAlgoDSA:
|
|
return pk.parseDSAPrivateKey(data)
|
|
case PubKeyAlgoElGamal:
|
|
return pk.parseElGamalPrivateKey(data)
|
|
case PubKeyAlgoECDSA:
|
|
return pk.parseECDSAPrivateKey(data)
|
|
case PubKeyAlgoECDH:
|
|
return pk.parseECDHPrivateKey(data)
|
|
case PubKeyAlgoEdDSA:
|
|
return pk.parseEdDSAPrivateKey(data)
|
|
}
|
|
panic("impossible")
|
|
}
|
|
|
|
func (pk *PrivateKey) parseRSAPrivateKey(data []byte) (err error) {
|
|
rsaPub := pk.PublicKey.PublicKey.(*rsa.PublicKey)
|
|
rsaPriv := new(rsa.PrivateKey)
|
|
rsaPriv.PublicKey = *rsaPub
|
|
|
|
buf := bytes.NewBuffer(data)
|
|
d := new(encoding.MPI)
|
|
if _, err := d.ReadFrom(buf); err != nil {
|
|
return err
|
|
}
|
|
|
|
p := new(encoding.MPI)
|
|
if _, err := p.ReadFrom(buf); err != nil {
|
|
return err
|
|
}
|
|
|
|
q := new(encoding.MPI)
|
|
if _, err := q.ReadFrom(buf); err != nil {
|
|
return err
|
|
}
|
|
|
|
rsaPriv.D = new(big.Int).SetBytes(d.Bytes())
|
|
rsaPriv.Primes = make([]*big.Int, 2)
|
|
rsaPriv.Primes[0] = new(big.Int).SetBytes(p.Bytes())
|
|
rsaPriv.Primes[1] = new(big.Int).SetBytes(q.Bytes())
|
|
if err := rsaPriv.Validate(); err != nil {
|
|
return errors.KeyInvalidError(err.Error())
|
|
}
|
|
rsaPriv.Precompute()
|
|
pk.PrivateKey = rsaPriv
|
|
|
|
return nil
|
|
}
|
|
|
|
func (pk *PrivateKey) parseDSAPrivateKey(data []byte) (err error) {
|
|
dsaPub := pk.PublicKey.PublicKey.(*dsa.PublicKey)
|
|
dsaPriv := new(dsa.PrivateKey)
|
|
dsaPriv.PublicKey = *dsaPub
|
|
|
|
buf := bytes.NewBuffer(data)
|
|
x := new(encoding.MPI)
|
|
if _, err := x.ReadFrom(buf); err != nil {
|
|
return err
|
|
}
|
|
|
|
dsaPriv.X = new(big.Int).SetBytes(x.Bytes())
|
|
if err := validateDSAParameters(dsaPriv); err != nil {
|
|
return err
|
|
}
|
|
pk.PrivateKey = dsaPriv
|
|
|
|
return nil
|
|
}
|
|
|
|
func (pk *PrivateKey) parseElGamalPrivateKey(data []byte) (err error) {
|
|
pub := pk.PublicKey.PublicKey.(*elgamal.PublicKey)
|
|
priv := new(elgamal.PrivateKey)
|
|
priv.PublicKey = *pub
|
|
|
|
buf := bytes.NewBuffer(data)
|
|
x := new(encoding.MPI)
|
|
if _, err := x.ReadFrom(buf); err != nil {
|
|
return err
|
|
}
|
|
|
|
priv.X = new(big.Int).SetBytes(x.Bytes())
|
|
if err := validateElGamalParameters(priv); err != nil {
|
|
return err
|
|
}
|
|
pk.PrivateKey = priv
|
|
|
|
return nil
|
|
}
|
|
|
|
func (pk *PrivateKey) parseECDSAPrivateKey(data []byte) (err error) {
|
|
ecdsaPub := pk.PublicKey.PublicKey.(*ecdsa.PublicKey)
|
|
ecdsaPriv := ecdsa.NewPrivateKey(*ecdsaPub)
|
|
|
|
buf := bytes.NewBuffer(data)
|
|
d := new(encoding.MPI)
|
|
if _, err := d.ReadFrom(buf); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := ecdsaPriv.UnmarshalIntegerSecret(d.Bytes()); err != nil {
|
|
return err
|
|
}
|
|
if err := ecdsa.Validate(ecdsaPriv); err != nil {
|
|
return err
|
|
}
|
|
pk.PrivateKey = ecdsaPriv
|
|
|
|
return nil
|
|
}
|
|
|
|
func (pk *PrivateKey) parseECDHPrivateKey(data []byte) (err error) {
|
|
ecdhPub := pk.PublicKey.PublicKey.(*ecdh.PublicKey)
|
|
ecdhPriv := ecdh.NewPrivateKey(*ecdhPub)
|
|
|
|
buf := bytes.NewBuffer(data)
|
|
d := new(encoding.MPI)
|
|
if _, err := d.ReadFrom(buf); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := ecdhPriv.UnmarshalByteSecret(d.Bytes()); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := ecdh.Validate(ecdhPriv); err != nil {
|
|
return err
|
|
}
|
|
|
|
pk.PrivateKey = ecdhPriv
|
|
|
|
return nil
|
|
}
|
|
|
|
func (pk *PrivateKey) parseEdDSAPrivateKey(data []byte) (err error) {
|
|
eddsaPub := pk.PublicKey.PublicKey.(*eddsa.PublicKey)
|
|
eddsaPriv := eddsa.NewPrivateKey(*eddsaPub)
|
|
eddsaPriv.PublicKey = *eddsaPub
|
|
|
|
buf := bytes.NewBuffer(data)
|
|
d := new(encoding.MPI)
|
|
if _, err := d.ReadFrom(buf); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = eddsaPriv.UnmarshalByteSecret(d.Bytes()); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := eddsa.Validate(eddsaPriv); err != nil {
|
|
return err
|
|
}
|
|
|
|
pk.PrivateKey = eddsaPriv
|
|
|
|
return nil
|
|
}
|
|
|
|
func validateDSAParameters(priv *dsa.PrivateKey) error {
|
|
p := priv.P // group prime
|
|
q := priv.Q // subgroup order
|
|
g := priv.G // g has order q mod p
|
|
x := priv.X // secret
|
|
y := priv.Y // y == g**x mod p
|
|
one := big.NewInt(1)
|
|
// expect g, y >= 2 and g < p
|
|
if g.Cmp(one) <= 0 || y.Cmp(one) <= 0 || g.Cmp(p) > 0 {
|
|
return errors.KeyInvalidError("dsa: invalid group")
|
|
}
|
|
// expect p > q
|
|
if p.Cmp(q) <= 0 {
|
|
return errors.KeyInvalidError("dsa: invalid group prime")
|
|
}
|
|
// q should be large enough and divide p-1
|
|
pSub1 := new(big.Int).Sub(p, one)
|
|
if q.BitLen() < 150 || new(big.Int).Mod(pSub1, q).Cmp(big.NewInt(0)) != 0 {
|
|
return errors.KeyInvalidError("dsa: invalid order")
|
|
}
|
|
// confirm that g has order q mod p
|
|
if !q.ProbablyPrime(32) || new(big.Int).Exp(g, q, p).Cmp(one) != 0 {
|
|
return errors.KeyInvalidError("dsa: invalid order")
|
|
}
|
|
// check y
|
|
if new(big.Int).Exp(g, x, p).Cmp(y) != 0 {
|
|
return errors.KeyInvalidError("dsa: mismatching values")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func validateElGamalParameters(priv *elgamal.PrivateKey) error {
|
|
p := priv.P // group prime
|
|
g := priv.G // g has order p-1 mod p
|
|
x := priv.X // secret
|
|
y := priv.Y // y == g**x mod p
|
|
one := big.NewInt(1)
|
|
// Expect g, y >= 2 and g < p
|
|
if g.Cmp(one) <= 0 || y.Cmp(one) <= 0 || g.Cmp(p) > 0 {
|
|
return errors.KeyInvalidError("elgamal: invalid group")
|
|
}
|
|
if p.BitLen() < 1024 {
|
|
return errors.KeyInvalidError("elgamal: group order too small")
|
|
}
|
|
pSub1 := new(big.Int).Sub(p, one)
|
|
if new(big.Int).Exp(g, pSub1, p).Cmp(one) != 0 {
|
|
return errors.KeyInvalidError("elgamal: invalid group")
|
|
}
|
|
// Since p-1 is not prime, g might have a smaller order that divides p-1.
|
|
// We cannot confirm the exact order of g, but we make sure it is not too small.
|
|
gExpI := new(big.Int).Set(g)
|
|
i := 1
|
|
threshold := 2 << 17 // we want order > threshold
|
|
for i < threshold {
|
|
i++ // we check every order to make sure key validation is not easily bypassed by guessing y'
|
|
gExpI.Mod(new(big.Int).Mul(gExpI, g), p)
|
|
if gExpI.Cmp(one) == 0 {
|
|
return errors.KeyInvalidError("elgamal: order too small")
|
|
}
|
|
}
|
|
// Check y
|
|
if new(big.Int).Exp(g, x, p).Cmp(y) != 0 {
|
|
return errors.KeyInvalidError("elgamal: mismatching values")
|
|
}
|
|
|
|
return nil
|
|
}
|