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) }