Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Key exchange examples? #32

Open
riobard opened this issue Feb 25, 2020 · 2 comments
Open

Key exchange examples? #32

riobard opened this issue Feb 25, 2020 · 2 comments

Comments

@riobard
Copy link

riobard commented Feb 25, 2020

In golang/go#20504 (comment) @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?

FiloSottile added a commit to FiloSottile/edwards25519 that referenced this issue Sep 28, 2020
For the license changes, see gtank/ristretto255-private#28 and
gtank/ristretto255#32, that contribute all code in those repositories to
the Go project under the Google CLA.
FiloSottile added a commit to FiloSottile/edwards25519 that referenced this issue Sep 28, 2020
For the license changes, see gtank/ristretto255-private#28 and
gtank/ristretto255#32, that contribute all code in those repositories to
the Go project under the Google CLA.
@justmumu
Copy link

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

@riobard
Copy link
Author

riobard commented Sep 9, 2022

@justmumu Unfortunately no.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants