// csidh provides an API to use the csidh key encryption mechanism.
// Csidh is a quantum-resistant key encapsulation cryptosystem.
// Cloudflare's csidh implements commutative supersingular isogeny-based Diffie-Hellman key exchange algorithm (CSIDH).

package csidh

// Csidh works differently from Frodo, Nacl, and Mlkem
// Each pair of public keys only creates 1 shared key
// Thus, you don't need to send an encrypted key which the recipient decrypts with their private key
// You instead just calculate the shared key from the recipient/sender public key pair

// Public key is 64 bytes long
// Private key is 37 bytes long
// Shared key is 64 bytes long

// Public keys are encoded in Base64
// Private keys are encoded in Hex

import circlCsidh "github.com/cloudflare/circl/dh/csidh"

import "seekia/internal/encoding"

import "crypto/rand"
import "errors"


func VerifyCsidhPublicKeyString(inputKey string)bool{

	_, err := ReadCsidhPublicKeyString(inputKey)
	if (err != nil) {
		return false
	}

	return true
}

func ReadCsidhPublicKeyString(inputKey string)([64]byte, error){

	decodedBytes, err := encoding.DecodeBase64StringToBytes(inputKey)
	if (err != nil) {
		return [64]byte{}, errors.New("ReadCsidhPublicKeyString called with invalid key: " + inputKey)
	}

	if (len(decodedBytes) != 64){
		return [64]byte{}, errors.New("ReadCsidhPublicKeyString called with invalid key: " + inputKey)
	}

	publicKey := [64]byte(decodedBytes)

	isValid := VerifyCsidhPublicKey(publicKey)
	if (isValid == false){
		return [64]byte{}, errors.New("ReadCsidhPublicKeyString called with invalid key: " + inputKey)
	}

	return publicKey, nil
}

func VerifyCsidhPublicKey(inputKey [64]byte)bool{

	// Now we verify that the public key is valid.

	var newPublicKeyObject circlCsidh.PublicKey

	success := newPublicKeyObject.Import(inputKey[:])
	if (success == false){
		return false
	}

	success = circlCsidh.Validate(&newPublicKeyObject, rand.Reader)
	if (success == false){
		return false
	}

	return true
}


// Outputs:
//	-[64]byte: Public Key bytes
//	-[37]byte: Private key bytes
//	-error
func GetNewRandomPublicPrivateCsidhKeys()([64]byte, [37]byte, error){

	var newPublicKeyObject circlCsidh.PublicKey
	var newPrivateKeyObject circlCsidh.PrivateKey

	err := circlCsidh.GeneratePrivateKey(&newPrivateKeyObject, rand.Reader)
	if (err != nil){
		return [64]byte{}, [37]byte{}, errors.New("GeneratePrivateKey failed: " + err.Error())
	}

	circlCsidh.GeneratePublicKey(&newPublicKeyObject, &newPrivateKeyObject, rand.Reader)

	var publicKeyArray [64]byte
	var privateKeyArray [37]byte

	success := newPublicKeyObject.Export(publicKeyArray[:])
	if (success == false){
		return [64]byte{}, [37]byte{}, errors.New("GetNewRandomPublicPrivateCsidhKeys failed: Failed to export public key.")
	}

	success = newPrivateKeyObject.Export(privateKeyArray[:])
	if (success == false){
		return [64]byte{}, [37]byte{}, errors.New("GetNewRandomPublicPrivateCsidhKeys failed: Failed to export private key.")
	}

	return publicKeyArray, privateKeyArray, nil
}

//Outputs:
//	-[64]byte: Csidh Shared Key
//	-error
func CalculateCsidhSharedKey(csidhPublicKey [64]byte, csidhPrivateKey [37]byte)([64]byte, error){

	var newPublicKeyObject circlCsidh.PublicKey
	var newPrivateKeyObject circlCsidh.PrivateKey

	success := newPublicKeyObject.Import(csidhPublicKey[:])
	if (success == false){
		return [64]byte{}, errors.New("CalculateCsidhSharedKey failed: Invalid public key.")
	}

	success = newPrivateKeyObject.Import(csidhPrivateKey[:])
	if (success == false){
		return [64]byte{}, errors.New("CalculateCsidhSharedKey failed: Invalid private key.")
	}

	var sharedSecret [64]byte

	success = circlCsidh.DeriveSecret(&sharedSecret, &newPublicKeyObject, &newPrivateKeyObject, rand.Reader)
	if (success == false){
		return [64]byte{}, errors.New("Failed to derive csidh secret.")
	}

	return sharedSecret, nil
}

