gtank / ristretto255

Implements ristretto255, a fast prime-order group.
https://ristretto.group
BSD 3-Clause "New" or "Revised" License
98 stars 22 forks source link

Key exchange examples? #32

Open riobard opened 4 years ago

riobard commented 4 years ago

In https://github.com/golang/go/issues/20504#issuecomment-590654494 @FiloSottile pointed out that I should use ristretto255 for key exchange with uniform-looking public keys. However it is not super clear what I am supposed to do, and the only example I could find is from libsodium but it has a very different looking API

// -------- First party -------- Send blinded p(x)
unsigned char x[crypto_core_ristretto255_HASHBYTES];
randombytes_buf(x, sizeof x);

// Compute px = p(x), a group element derived from x
unsigned char px[crypto_core_ristretto255_BYTES];
crypto_core_ristretto255_from_hash(px, x);

// Compute a = p(x) * g^r
unsigned char r[crypto_core_ristretto255_SCALARBYTES];
unsigned char gr[crypto_core_ristretto255_BYTES];
unsigned char a[crypto_core_ristretto255_BYTES];
crypto_core_ristretto255_scalar_random(r);
crypto_scalarmult_ristretto255_base(gr, r);
crypto_core_ristretto255_add(a, px, gr);

// -------- Second party -------- Send g^k and a^k
unsigned char k[crypto_core_ristretto255_SCALARBYTES];
randombytes_buf(k, sizeof k);

// Compute v = g^k
unsigned char v[crypto_core_ristretto255_BYTES];
crypto_scalarmult_ristretto255_base(v, k);

// Compute b = a^k
unsigned char b[crypto_core_ristretto255_BYTES];
if (crypto_scalarmult_ristretto255(b, k, a) != 0) {
    return -1;
}

// -------- First party -------- Unblind f(x)
// Compute vir = v^(-r)
unsigned char ir[crypto_core_ristretto255_SCALARBYTES];
unsigned char vir[crypto_core_ristretto255_BYTES];
crypto_core_ristretto255_scalar_negate(ir, r);
crypto_scalarmult_ristretto255(vir, ir, v);

// Compute f(x) = b * v^(-r) = (p(x) * g^r)^k * (g^k)^(-r)
//              = (p(x) * g)^k * g^(-k) = p(x)^k
unsigned char fx[crypto_core_ristretto255_BYTES];
crypto_core_ristretto255_add(fx, b, vir);

Anyway I figure since this is Go, maybe the API should be similar to x/crypto/x25519? So I wrote the following test code which seems to be working fine

import (
    "crypto/rand"
    "testing"

    r255 "github.com/gtank/ristretto255"
)

func TestKx(t *testing.T) {
    seed := make([]byte, 64)

    // party 1
    rand.Read(seed)
    sk1 := r255.NewScalar().FromUniformBytes(seed)
    pk1 := r255.NewElement().ScalarBaseMult(sk1) // send pk1.Encode(nil) to party 2

    // party 2
    rand.Read(seed)
    sk2 := r255.NewScalar().FromUniformBytes(seed)
    pk2 := r255.NewElement().ScalarBaseMult(sk2) // send pk2.Encode(nil) to party 1

    shared1 := r255.NewElement().ScalarMult(sk1, pk2)
    shared2 := r255.NewElement().ScalarMult(sk2, pk1)

    if shared1.Equal(shared2) != 1 {
        t.Fatal("kx failed")
    }

    t.Logf("pk1: %x", pk1.Encode(nil))
    t.Logf("sk1: %x", sk1.Encode(nil))
    t.Logf("pk2: %x", pk2.Encode(nil))
    t.Logf("sk2: %x", sk2.Encode(nil))
    t.Logf("shared: %x", shared1.Encode(nil))
}
  1. Did I do something stupid and dangerous?
  2. Does the 32-byte encoded pk1 look random and uniform?
  3. x25519 public keys have only 255 bits with the most significant bit of the byte 31 cleared, and x25519 secret keys have only 251 bits with the lower 3 bits of byte 0 and highest bit of byte 31 cleared and bit 6 of byte 31 set. Is there any similarity here for ristretto255 encoded public and secret keys?
  4. How to relate and translate between the above Go code (assuming it's correct) and the libsodium API for interoperability?
  5. The [ristretto255 draft RFC]() says

It SHOULD be possible for a ristretto255 implementation to change its underlying curve without causing any breaking change. A ristretto255 implementation MUST be interoperable with any other implementation, even if that implementation uses a different curve internally. Any operation on ristretto255 elements that only works correctly or leads to different results based on the underlying curve is explicitly disallowed.

Does it mean that if the above key exchange procedure is correct, either party1 or party2 could use a different underlying curve and the key exchange still works?

justmumu commented 2 years ago

Hi @riobard, Did you find answers to your questions?

riobard commented 2 years ago

@justmumu Unfortunately no.