package databag

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"errors"
)

//GenerateRsaKeyPair creates a public/private key for a new account
func GenerateRsaKeyPair() (*rsa.PrivateKey, *rsa.PublicKey, string, error) {
  keyType := getStrConfigValue(CNFKeyType, "RSA2048");
	if keyType == "RSA2048" {
		privkey, _ := rsa.GenerateKey(rand.Reader, 2048)
		return privkey, &privkey.PublicKey, "RSA2048", nil
	} else if keyType == "RSA4096" {
		privkey, _ := rsa.GenerateKey(rand.Reader, 4096)
		return privkey, &privkey.PublicKey, "RSA2048", nil
	} else {
		return nil, nil, "", errors.New("invalid key setting")
	}
}

//ExportRsaPrivateKeyAsPemStr exports account private key
func ExportRsaPrivateKeyAsPemStr(privkey *rsa.PrivateKey) string {
	privkeyBytes := x509.MarshalPKCS1PrivateKey(privkey)
	privkeyPEM := pem.EncodeToMemory(
		&pem.Block{
			Type:  "RSA PRIVATE KEY",
			Bytes: privkeyBytes,
		},
	)
	return string(privkeyPEM)
}

//ParseRsaPrivateKeyFromPemStr loads account private key
func ParseRsaPrivateKeyFromPemStr(privPEM string) (*rsa.PrivateKey, error) {
	block, _ := pem.Decode([]byte(privPEM))
	if block == nil {
		return nil, errors.New("failed to parse PEM block containing the key")
	}

	priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return nil, err
	}

	return priv, nil
}

//ExportRsaPublicKeyAsPemStr exports account public key
func ExportRsaPublicKeyAsPemStr(pubkey *rsa.PublicKey) (string, error) {
	pubkeyBytes, err := x509.MarshalPKIXPublicKey(pubkey)
	if err != nil {
		return "", err
	}
	pubkeyPEM := pem.EncodeToMemory(
		&pem.Block{
			Type:  "RSA PUBLIC KEY",
			Bytes: pubkeyBytes,
		},
	)

	return string(pubkeyPEM), nil
}

//ParseRsaPublicKeyFromPemStr loads account public key
func ParseRsaPublicKeyFromPemStr(pubPEM string) (*rsa.PublicKey, error) {
	block, _ := pem.Decode([]byte(pubPEM))
	if block == nil {
		return nil, errors.New("failed to parse PEM block containing the key")
	}

	pub, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return nil, err
	}

	switch pub := pub.(type) {
	case *rsa.PublicKey:
		return pub, nil
	default:
		break // fall through
	}
	return nil, errors.New("Key type is not RSA")
}
