package utils import ( "crypto/sha512" "golang.org/x/crypto/curve25519" "golang.org/x/crypto/ed25519" "math/big" ) // 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) curve25519pub := ed25519PublicKeyToCurve25519(remotePublicKey) secret, err := curve25519.X25519(curve25519priv[:], curve25519pub[:]) return secret, err } // https://github.com/FiloSottile/age/blob/master/internal/age/ssh.go#L174 // Copyright 2019 Google LLC // //Redistribution and use in source and binary forms, with or without //modification, are permitted provided that the following conditions are //met: // // * Redistributions of source code must retain the above copyright //notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above //copyright notice, this list of conditions and the following disclaimer //in the documentation and/or other materials provided with the //distribution. // * Neither the name of Google LLC nor the names of its //contributors may be used to endorse or promote products derived from //this software without specific prior written permission. // //THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS //"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT //LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR //A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT //OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, //SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT //LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, //DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY //THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE //OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. var curve25519P, _ = new(big.Int).SetString("57896044618658097711785492504343953926634992332820282019728792003956564819949", 10) func ed25519PublicKeyToCurve25519(pk ed25519.PublicKey) []byte { // ed25519.PublicKey is a little endian representation of the y-coordinate, // with the most significant bit set based on the sign of the x-coordinate. bigEndianY := make([]byte, ed25519.PublicKeySize) for i, b := range pk { bigEndianY[ed25519.PublicKeySize-i-1] = b } bigEndianY[0] &= 0b0111_1111 // The Montgomery u-coordinate is derived through the bilinear map // // u = (1 + y) / (1 - y) // // See https://blog.filippo.io/using-ed25519-keys-for-encryption. y := new(big.Int).SetBytes(bigEndianY) denom := big.NewInt(1) denom.ModInverse(denom.Sub(denom, y), curve25519P) // 1 / (1 - y) u := y.Mul(y.Add(y, big.NewInt(1)), denom) u.Mod(u, curve25519P) out := make([]byte, curve25519.PointSize) uBytes := u.Bytes() for i, b := range uBytes { out[len(uBytes)-i-1] = b } return out } // 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) }