Skip to content

Instantly share code, notes, and snippets.

@bifurcation
Last active Oct 22, 2019
Embed
What would you like to do?
An attempt at updateable X25519
package main
import (
"bytes"
"crypto/rand"
"fmt"
"math/big"
"golang.org/x/crypto/curve25519"
)
var (
prng = rand.Reader
n25519x = "1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed"
n25519 *big.Int
)
func privToPub(a [32]byte) (aGx [32]byte) {
curve25519.ScalarBaseMult(&aGx, &a)
return
}
func newKeyPair() (a, aGx [32]byte) {
prng.Read(a[:])
aGx = privToPub(a)
return
}
func dh(a, bGx [32]byte) (abGx [32]byte) {
curve25519.ScalarMult(&abGx, &a, &bGx)
return
}
func reverse(in [32]byte) (out [32]byte) {
for i, x := range in {
out[len(in)-i-1] = x
}
return
}
func le2bn(le [32]byte) *big.Int {
be := reverse(le)
return big.NewInt(0).SetBytes(be[:])
}
func bn2le(bn *big.Int) [32]byte {
sl := bn.Bytes()
pad := bytes.Repeat([]byte{0}, 32-len(sl))
sl = append(pad, sl...)
be := [32]byte{}
copy(be[:], sl)
return reverse(be)
}
func newDelta() (d [32]byte) {
prng.Read(d[:])
return
}
/*
RFC 7748:
def decodeScalar25519(k):
k_list = [ord(b) for b in k]
k_list[0] &= 248
k_list[31] &= 127
k_list[31] |= 64
return decodeLittleEndian(k_list, 255)
*/
func clamp(a [32]byte) *big.Int {
out := a
out[0] &= 248
out[31] &= 127
out[31] |= 64
return le2bn(out)
}
/*
Mult(a,b) {
c = (Clamp(a) - Clamp(b)) mod order
if msb(c) = 0
c = (order - c) mod order
return c
}
*/
func deltaPriv(a, b [32]byte) [32]byte {
aC := le2bn(a)
bC := le2bn(b)
c := big.NewInt(0)
c.Sub(aC, bC)
c.Mod(c, n25519)
if c.Bit(255) == 0 {
c.Sub(n25519, c)
}
return bn2le(c)
}
func deltaPub(d, aGx [32]byte) [32]byte {
return dh(d, aGx)
}
func main() {
n25519, _ = big.NewInt(0).SetString(n25519x, 16)
a, aG := newKeyPair()
b, bG := newKeyPair()
// Check that normal DH works
abG := dh(a, bG)
baG := dh(b, aG)
fmt.Printf("abG=[%x]\n", abG)
fmt.Printf("baG=[%x]\n", baG)
// Generate and apply a delta
d := newDelta()
fmt.Printf("d =[%x]\n", d)
da := deltaPriv(d, a)
d_aG := deltaPub(d, aG)
// Verify that the homomorphism applies
da_G := privToPub(da)
fmt.Printf("da_G =[%x]\n", da_G)
fmt.Printf("d_aG =[%x]\n", d_aG)
// Verify that the private and public keys produce the same results
e, eG := newKeyPair()
da_eG := dh(da, eG)
ed_aG := dh(e, d_aG)
fmt.Printf("da_eG =[%x]\n", da_eG)
fmt.Printf("ed_aG =[%x]\n", ed_aG)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment