package utils

import (
	"crypto/sha512"
	"filippo.io/edwards25519"
	"golang.org/x/crypto/curve25519"
	"golang.org/x/crypto/ed25519"
)

// EDH implements diffie hellman using curve25519 keys derived from ed25519 keys
func EDH(privateKey ed25519.PrivateKey, remotePublicKey ed25519.PublicKey) ([]byte, error) {
	var privKeyBytes [64]byte
	var remotePubKeyBytes [32]byte
	copy(privKeyBytes[:], privateKey[:])
	copy(remotePubKeyBytes[:], remotePublicKey[:])
	var curve25519priv [32]byte

	PrivateKeyToCurve25519(&curve25519priv, &privKeyBytes)
	remoteCurve25519pub, err := ed25519PublicKeyToCurve25519New(remotePublicKey)
	if err != nil {
		return []byte{}, err
	}
	secret, err := curve25519.X25519(curve25519priv[:], remoteCurve25519pub[:])
	return secret, err
}

// reproduced from https://github.com/FiloSottile/age/blob/main/agessh/agessh.go#L190
func ed25519PublicKeyToCurve25519New(pk ed25519.PublicKey) ([]byte, error) {
	// See https://blog.filippo.io/using-ed25519-keys-for-encryption and
	// https://pkg.go.dev/filippo.io/edwards25519#Point.BytesMontgomery.
	p, err := new(edwards25519.Point).SetBytes(pk)
	if err != nil {
		return nil, err
	}
	return p.BytesMontgomery(), nil
}

// PrivateKeyToCurve25519 converts an ed25519 private key into a corresponding
// curve25519 private key
func PrivateKeyToCurve25519(curve25519Private *[32]byte, privateKey *[64]byte) {
	h := sha512.New()
	h.Write(privateKey[:32])
	digest := h.Sum(nil)

	digest[0] &= 248
	digest[31] &= 127
	digest[31] |= 64

	copy(curve25519Private[:], digest)
}
