x.crypto: initial addition of curve25519 module (#24748)

This commit is contained in:
blackshirt 2025-06-19 11:25:26 +07:00 committed by GitHub
parent b9e5757236
commit d32969ed3e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 945 additions and 0 deletions

View file

@ -0,0 +1,53 @@
# curve25519
------------
This module implement Diffie-Hellman key exchange mechanism (ECDHA) based on elliptic curve
known as Curve25519 in pure V.
## About curve25519
From wikipedia, Curve25519 is an elliptic curve used in elliptic-curve cryptography (ECC)
offering 128 bits of security (256-bit key size) and designed for use with the elliptic
curve DiffieHellman (ECDH) key agreement scheme.
It is one of the fastest curves in ECC, and is not covered by any known patents.
The reference implementation is public domain software. The original Curve25519 paper defined it as
a DiffieHellman (DH) function.
Daniel J. Bernstein has since proposed that the name "Curve25519" be used for the underlying curve,
and the name "X25519" for the DH function Curve25519 is an elliptic curve
that offers 128 security bits
and is designed for use in the Elliptic Curve Diffie-Hellman (ECDH) key agreement key design scheme.
Examples
--------
```v
import x.crypto.curve25519
fn main() {
// For example, two peers, Alice and Bob, want to create a shared secret together
//
// Alice generates a private key
mut alice_pvkey := curve25519.PrivateKey.new()!
// Alice's PublicKey to be shared with Bob
alice_pbkey := alice_pvkey.public_key()!
// The other peer, Bob, has a different private key
mut bob_pvkey := curve25519.PrivateKey.new()!
// Bob's public key to be shared with Alice
bob_pbkey := bob_pvkey.public_key()!
// Let the two peers exchange their respective public keys
//
// Alice derives the shared secret, using her own private key, and the public key that Bob shared
alice_shared_sec := curve25519.derive_shared_secret(mut alice_pvkey, bob_pbkey)!
// Bob derives the shared secret, using his own private key, and the public key that Alice shared
bob_shared_sec := curve25519.derive_shared_secret(mut bob_pvkey, alice_pbkey)!
// the two shared secrets (derived by Alice, and derived by Bob), should be the same
//
println(alice_shared_sec.hex()) // 49fd4a4d0637d2413cd501b20111fc50a592dc21460e45f451c03d1fd3cef900
println(bob_shared_sec.hex()) // 49fd4a4d0637d2413cd501b20111fc50a592dc21460e45f451c03d1fd3cef900
dump(alice_shared_sec == bob_shared_sec) // alice_shared_sec == bob_shared_sec: true
assert alice_shared_sec == bob_shared_sec
}
```

View file

@ -0,0 +1,392 @@
// Copyright © 2025 blackshirt.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
//
// This module implements building block for elliptic-curve diffie-helman
// key exchange (ECDH) mechanism through curve25519 curve.
module curve25519
import crypto.rand
import crypto.internal.subtle
import crypto.ed25519.internal.edwards25519
// scalar_size is the size of the Curve25519 key
const scalar_size = 32
// point_size is the size of the Curve25519 point
const point_size = 32
// zero_point is point with 32 bytes length of zeros bytes
const zero_point = []u8{len: 32, init: u8(0x00)}
// base_point is the canonical Curve25519 generator, encoded as a byte with value 9,
// followed by 31 zero bytes
const base_point = [u8(9), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0]
// PrivateKey represents Curve25519 private key
@[noinit]
pub struct PrivateKey {
mut:
// boolean flag that tells this key should not be
// used again when its true. its set after .free call
done bool
// clamped key, 32 bytes length
key []u8
}
// new creates a new Curve25519 key using randomly generated bytes from `crypto.rand`.
@[direct_array_access]
pub fn PrivateKey.new() !&PrivateKey {
mut bytes := rand.read(scalar_size)!
// do bytes clamping
clamp(mut bytes)!
if is_zero_point(bytes) || is_base_point(bytes) {
return error('PrivateKey.new: bytes zeros or base point')
}
return &PrivateKey{
key: bytes
}
}
// new_from_seed creates a new Curve25519 key from provided seed bytes.
@[direct_array_access]
pub fn PrivateKey.new_from_seed(seed []u8) !&PrivateKey {
if seed.len != scalar_size {
return error('invalid scalar size')
}
mut bytes := seed.clone()
clamp(mut bytes)!
if is_zero_point(bytes) || is_base_point(bytes) {
return error('PrivateKey.new_from_seed: bytes was zeros or base point')
}
return &PrivateKey{
key: bytes
}
}
// public_key returns the associated public key part of the PrivateKey.
@[direct_array_access]
pub fn (mut pv PrivateKey) public_key() !&PublicKey {
if pv.done {
return error('your key has been marked as freed')
}
out := x25519(mut pv.key, base_point)!
return &PublicKey{
key: out
}
}
// equal returns whether two private keys are equal.
@[direct_array_access]
pub fn (pv PrivateKey) equal(oth PrivateKey) bool {
if pv.done || oth.done {
panic('the key has been marked as freed')
}
if pv.key.len != scalar_size || oth.key.len != scalar_size {
return false
}
return subtle.constant_time_compare(pv.key, oth.key) == 1
}
// x25519 performs scalar multiplication between key and point and return another bytes (point).
@[direct_array_access]
pub fn (mut pv PrivateKey) x25519(point []u8) ![]u8 {
if pv.done {
return error('PrivateKey has been marked as freed')
}
if point.len != point_size {
return error('bad point size, should be 32')
}
// even technically its possible, but we limit to unallow it
if subtle.constant_time_compare(pv.key, point) == 1 {
return error('pv.key identical with point')
}
out := x25519(mut pv.key, point)!
return out
}
// bytes return a clone of the bytes of the underlying PrivateKey
pub fn (pv PrivateKey) bytes() ![]u8 {
if pv.done {
return error('PrivateKey has been marked as freed')
}
return pv.key.clone()
}
// free releases underlying key. Dont use the key after calling .free
@[unsafe]
pub fn (mut pv PrivateKey) free() {
unsafe { pv.key.free() }
// sets flag to finish
pv.done = true
}
// PublicKey represent Curve25519 key.
@[noinit]
pub struct PublicKey {
mut:
// 32 bytes length of scalar * point
key []u8
}
// new_from_bytes creates a new Curve25519 public key from provided bytes.
pub fn PublicKey.new_from_bytes(bytes []u8) !&PublicKey {
if bytes.len != point_size {
return error('PublicKey.new: bad bytes length')
}
// Refers to the D.J. Bernstein, the designer of the curve25519, public key validation
// in curve25519 is generally not needed for Diffie-Hellman key exchange.
// See https://cr.yp.to/ecdh.html#validate
// But there are availables suggestion to do validation on them spreads on the internet, likes
// bytes return the clone of the bytes of the underlying PublicKey
// - check the shared value and to raise exception if it is zero.
// - You can also bind the exchanged public keys to the shared keys, i.e.,
// instead of using H(abG) as the shared keys, you should use H(aG || bG || abG)
//
// We only, check for zeros public key
if is_zero(bytes) {
return error('PublicKey.new: get zeros bytes')
}
// otherwise, we can return it
return &PublicKey{
key: bytes
}
}
// equal tells whether two public keys are equal
pub fn (pb PublicKey) equal(other PublicKey) bool {
// different length, should not happen
if pb.key.len != point_size || other.key.len != point_size {
return false
}
return subtle.constant_time_compare(pb.key, other.key) == 1
}
// bytes return the clone of the bytes of the underlying PublicKey
pub fn (pb PublicKey) bytes() ![]u8 {
if pb.key.len != point_size {
return error('bad public key size')
}
return pb.key.clone()
}
// SharedOpts is the configuration options to `derive_shared_secret` routine
@[params]
pub struct SharedOpts {
pub mut:
should_derive bool
derivator Derivator = RawDerivator{}
drv_opts DeriveOpts
}
// DeriveOpts is config to drive the Derivator's derive operation.
@[params]
pub struct DeriveOpts {}
// Derivator represent key derivation function
pub interface Derivator {
// derive transforms bytes in sec into another form of bytes.
derive(sec []u8, opt DeriveOpts) ![]u8
}
// RawDerivator was a simple derivator with no derivation behaviour.
struct RawDerivator {}
fn (rd RawDerivator) derive(sec []u8, opt DeriveOpts) ![]u8 {
return sec
}
// derive_shared_secret derives a shared secret between two peer's
// between first private key's peer and the second PublicKey's peer.
// Its accepts SharedOpts options to advance supports for other key derivation mechanism.
//
// 6. Diffie-Hellman with Curve25519
// See https://datatracker.ietf.org/doc/html/rfc7748#section-6
pub fn derive_shared_secret(mut local PrivateKey, remote PublicKey, opt SharedOpts) ![]u8 {
// check for safety
local_pubkey := local.public_key()!
if local_pubkey.equal(remote) {
return error('unallowed equal public key between peer')
}
// The local peer generates local private key, local_privkey, generates local public key, local_pubkey.
// and remote peer generates remote private key, ie, remote_privkey,
// with generated remote public key, remote_pubkey.
// Both now share shared = X25519(local_privkey, remote_pubkey) = X25519(remote_privkey, local_pubkey)
// as a shared secret, which is then used as a key or input to a key derivation function.
sec := local.x25519(remote.key)!
if is_zero(sec) {
return error('zeroes shared secret')
}
// you can choose this sec as an input into other key derivator, and pass this sec
// into provided derivator
if opt.should_derive {
// While the shared secret can be used directly, it's often recommended to apply
// a key derivation function (KDF), like HKDF to derive a more robust key for cryptographic operations.
new_sec := opt.derivator.derive(sec, opt.drv_opts)!
if is_zero(new_sec) {
return error('zeroes shared secret after derivation')
}
return new_sec
}
// otherwise, just return the sec as is.
return sec
}
// x25519 returns the result of the scalar multiplication (`scalar` * `point`),
// according to RFC 7748, Section 5. scalar, point and the return value are slices of 32 bytes.
// The functions take a scalar and a `u-coordinate` as inputs and produce a `u-coordinate` as output.
// Although the functions work internally with integers, the inputs and
// outputs are 32-bytes length (for X25519).
// scalar can be generated at random, for example with `crypto.rand` and point should
// be either `base_point` or the output of another `x25519` call.
@[direct_array_access]
pub fn x25519(mut scalar []u8, point []u8) ![]u8 {
mut dst := []u8{len: 32}
// we do bytes clamping here, to make sure scalar was ready to use
clamp(mut scalar)!
return x25519_generic(mut dst, mut scalar, point)
}
@[direct_array_access; inline]
fn x25519_generic(mut dst []u8, mut scalar []u8, point []u8) ![]u8 {
// we dont check arrays length here, its has been checked
// on the underlying scalar_mult routine
if is_base_point(point) {
scalar_base_mult(mut dst, mut scalar)!
// check for base_point result
if is_base_point(dst) {
return error('dst: get base_point')
}
} else {
// base := point.clone()
scalar_mult(mut dst, mut scalar, point)!
if is_zero_point(dst) {
return error('bad input point: low order point')
}
}
return dst
}
// scalar_base_mult performs scalar * base_point
@[direct_array_access]
fn scalar_base_mult(mut dst []u8, mut scalar []u8) ! {
scalar_mult(mut dst, mut scalar, base_point)!
}
// scalar_mult performs scalar multiplicatipn (scalar * point) on the curve25519 curve.
// for performance reason, scalar marked as mutable.
// scalar_mult is the main routine to perform scalar multiplication
// between scalar key and point on the curve25519 curve.
@[direct_array_access; inline]
fn scalar_mult(mut dst []u8, mut scalar []u8, point []u8) ! {
if dst.len != point_size {
return error('bad dst length')
}
if scalar.len != scalar_size {
return error('scalar.lenght != 32')
}
if point.len != point_size {
return error('point.lenght != 32')
}
// Note: we dont clamping scalar here, and responsible to the caller
// to do the clamping. Its assumed scalar has been clamped.
mut x1 := edwards25519.Element{}
mut x2 := edwards25519.Element{}
mut z2 := edwards25519.Element{}
mut x3 := edwards25519.Element{}
mut z3 := edwards25519.Element{}
mut tmp0 := edwards25519.Element{}
mut tmp1 := edwards25519.Element{}
x1.set_bytes(point[..])!
x2.one()
x3.set(x1)
z3.one()
mut swap := 0
for pos := 254; pos >= 0; pos-- {
mut b := scalar[pos / 8] >> u32(pos & 7)
b &= 1
swap = swap ^ int(b)
x2.swap(mut x3, swap)
z2.swap(mut z3, swap)
swap = int(b)
tmp0.subtract(x3, z3)
tmp1.subtract(x2, z2)
x2.add(x2, z2)
z2.add(x3, z3)
z3.multiply(tmp0, x2)
z2.multiply(z2, tmp1)
tmp0.square(tmp1)
tmp1.square(x2)
x3.add(z3, z2)
z2.subtract(z3, z2)
x2.multiply(tmp1, tmp0)
tmp1.subtract(tmp1, tmp0)
z2.square(z2)
z3.mult_32(tmp1, 121666)
x3.square(x3)
tmp0.add(tmp0, z3)
z3.multiply(x1, z2)
z2.multiply(tmp1, tmp0)
}
x2.swap(mut x3, swap)
z2.swap(mut z3, swap)
z2.invert(z2)
x2.multiply(x2, z2)
copy(mut dst, x2.bytes())
}
// Utility helpers
//
@[direct_array_access; inline]
fn is_zero_point(point []u8) bool {
if point.len != point_size {
return false
}
return subtle.constant_time_compare(point, zero_point) == 1
}
@[direct_array_access; inline]
fn is_base_point(point []u8) bool {
if point.len != point_size {
return false
}
return subtle.constant_time_compare(point, base_point) == 1
}
// clamp clears out some bits of seed bytes
@[direct_array_access; inline]
fn clamp(mut seed []u8) ! {
if seed.len != scalar_size {
return error('bad seed sizes for clamp')
}
// According to RFC 7748, for x25519, in order to decode 32 random bytes
// as an integer scalar, set the three least significant bits of the first byte
// and the most significant bit of the last to zero,
// set the second most significant bit of the last byte to 1
//
seed[0] &= 248
seed[31] &= 127
seed[31] |= 64
}
// is_zero returns whether seed is all zeroes in constant time.
fn is_zero(seed []u8) bool {
mut acc := u8(0)
for b in seed {
acc |= b
}
return acc == 0
}

View file

@ -0,0 +1,408 @@
// Copyright © 2025 blackshirt.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
//
// This test mostly taken and adapted from the golang version of the same library.
module curve25519
import rand
import encoding.hex
const bytes_error = 'byte-error-error'.repeat(2).bytes() // []u8{len:32}
fn test_curve25519_shared_secret() ! {
// From RFC 7748 Test vector:
// Alice's private key, a:
a := hex.decode('77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a')!
// Alice's public key, X25519(a, 9):
a_pbk := hex.decode('8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a')!
// Bob's private key, b:
b := hex.decode('5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb')!
// Bob's public key, X25519(b, 9):
b_pbk := hex.decode('de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f')!
// Their shared secret, K:
k := hex.decode('4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742')!
// check for alice generated public key was equal
mut a_pvk := PrivateKey.new_from_seed(a)!
assert a_pvk.public_key()!.bytes()! == a_pbk
// check for Bob generated public key was equal
mut b_pvk := PrivateKey.new_from_seed(b)!
b_pubkey := b_pvk.public_key()!
assert b_pubkey.bytes()! == b_pbk
// shared secret
sec := derive_shared_secret(mut a_pvk, b_pubkey)!
assert sec == k
}
fn test_private_key() ! {
mut pv0 := PrivateKey.new()!
mut pv1 := pv0
// private key equality
assert pv0.equal(pv1)
assert pv0.bytes()! == pv1.bytes()!
// public key equality
pb0 := pv0.public_key()!
pb1 := pv1.public_key()!
assert pb0.equal(pb1)
// scalar multiplication
x0 := pv0.x25519(pb0.key)!
x1 := pv1.x25519(pb1.key)!
assert x0 == x1
// free the resources
unsafe {
pv0.free()
pv1.free()
}
}
// requirement: when receiving such an array, implementations of x25519 (but not x448) MUST
// mask the most significant bit in the final byte.
fn test_highbit_ignored() ! {
mut s := rand.bytes(32)!
mut u := rand.bytes(32)!
mut hi0 := []u8{len: 32}
mut hi1 := []u8{len: 32}
u[31] &= 0x7f
scalar_mult(mut hi0, mut s, u)!
u[31] |= 0x80
scalar_mult(mut hi1, mut s, u)!
assert hi0 == hi1
}
fn test_x25519_basepoint() {
mut x := []u8{len: 32}
x[0] = 1
for i := 0; i < 200; i++ {
x = x25519(mut x, base_point) or { bytes_error }
assert x != bytes_error
}
expected_hex := '89161fde887b2b53de549af483940106ecc114d6982daa98256de23bdf77661a'
result := hex.encode(x)
assert result == expected_hex
}
struct LowOrderPoint {
point []u8
err IError
}
const loworder_points = [
LowOrderPoint{[u8(0x00), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00], error('bad input point: low order point')},
LowOrderPoint{[u8(0x01), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00], error('bad input point: low order point')},
LowOrderPoint{[u8(0xe0), 0xeb, 0x7a, 0x7c, 0x3b, 0x41, 0xb8, 0xae, 0x16, 0x56, 0xe3, 0xfa,
0xf1, 0x9f, 0xc4, 0x6a, 0xda, 0x09, 0x8d, 0xeb, 0x9c, 0x32, 0xb1, 0xfd, 0x86, 0x62, 0x05,
0x16, 0x5f, 0x49, 0xb8, 0x00], error('bad input point: low order point')},
LowOrderPoint{[u8(0x5f), 0x9c, 0x95, 0xbc, 0xa3, 0x50, 0x8c, 0x24, 0xb1, 0xd0, 0xb1, 0x55,
0x9c, 0x83, 0xef, 0x5b, 0x04, 0x44, 0x5c, 0xc4, 0x58, 0x1c, 0x8e, 0x86, 0xd8, 0x22, 0x4e,
0xdd, 0xd0, 0x9f, 0x11, 0x57], error('bad input point: low order point')},
LowOrderPoint{[u8(0xec), 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x7f], error('bad input point: low order point')},
LowOrderPoint{[u8(0xed), 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x7f], error('bad input point: low order point')},
LowOrderPoint{[u8(0xee), 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x7f], error('bad input point: low order point')},
]
fn test_low_order_points() ! {
nil_bytes := []u8{len: 32}
mut s := rand.bytes(scalar_size)!
for p in loworder_points {
out := x25519(mut s, p.point) or {
assert err == p.err
continue
}
assert out != nil_bytes
}
}
fn test_legacy_scalar_mult() ! {
for mut item in tests_vectors {
mut got := []u8{len: 32}
// clamp the input, because scalar_mult assumed its has been clamped
clamp(mut item.input)!
scalar_mult(mut got, mut item.input, item.base)!
assert got == item.expect
}
}
fn test_x25519_scalar_mult() ! {
for i, mut item in tests_vectors {
got := x25519(mut item.input, item.base)!
assert got == item.expect
// using object based instances
mut pv := PrivateKey.new_from_seed(item.input)!
newpoint := pv.x25519(item.base)!
assert newpoint == item.expect
}
}
struct Vectors {
mut:
input []u8
base []u8
expect []u8
}
const tests_vectors = [
// from https://datatracker.ietf.org/doc/html/rfc7748#section-5.2 test vectors
Vectors{
input: hex.decode('a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4')!
base: hex.decode('e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c')!
expect: hex.decode('c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552')!
},
Vectors{
input: hex.decode('4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d')!
base: hex.decode('e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493')!
expect: hex.decode('95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957')!
},
Vectors{
input: [u8(0x66), 0x8f, 0xb9, 0xf7, 0x6a, 0xd9, 0x71, 0xc8, 0x1a, 0xc9, 0x0, 0x7, 0x1a,
0x15, 0x60, 0xbc, 0xe2, 0xca, 0x0, 0xca, 0xc7, 0xe6, 0x7a, 0xf9, 0x93, 0x48, 0x91,
0x37, 0x61, 0x43, 0x40, 0x14]
base: [u8(0xdb), 0x5f, 0x32, 0xb7, 0xf8, 0x41, 0xe7, 0xa1, 0xa0, 0x9, 0x68, 0xef, 0xfd,
0xed, 0x12, 0x73, 0x5f, 0xc4, 0x7a, 0x3e, 0xb1, 0x3b, 0x57, 0x9a, 0xac, 0xad, 0xea,
0xe8, 0x9, 0x39, 0xa7, 0xdd]
expect: [u8(0x9), 0xd, 0x85, 0xe5, 0x99, 0xea, 0x8e, 0x2b, 0xee, 0xb6, 0x13, 0x4, 0xd3,
0x7b, 0xe1, 0xe, 0xc5, 0xc9, 0x5, 0xf9, 0x92, 0x7d, 0x32, 0xf4, 0x2a, 0x9a, 0xa, 0xfb,
0x3e, 0xb, 0x40, 0x74]
},
Vectors{
input: [u8(0x63), 0x66, 0x95, 0xe3, 0x4f, 0x75, 0xb9, 0xa2, 0x79, 0xc8, 0x70, 0x6f, 0xad,
0x12, 0x89, 0xf2, 0xc0, 0xb1, 0xe2, 0x2e, 0x16, 0xf8, 0xb8, 0x86, 0x17, 0x29, 0xc1,
0xa, 0x58, 0x29, 0x58, 0xaf]
base: [u8(0x9), 0xd, 0x7, 0x1, 0xf8, 0xfd, 0xe2, 0x8f, 0x70, 0x4, 0x3b, 0x83, 0xf2, 0x34,
0x62, 0x25, 0x41, 0x9b, 0x18, 0xa7, 0xf2, 0x7e, 0x9e, 0x3d, 0x2b, 0xfd, 0x4, 0xe1,
0xf, 0x3d, 0x21, 0x3e]
expect: [u8(0xbf), 0x26, 0xec, 0x7e, 0xc4, 0x13, 0x6, 0x17, 0x33, 0xd4, 0x40, 0x70, 0xea,
0x67, 0xca, 0xb0, 0x2a, 0x85, 0xdc, 0x1b, 0xe8, 0xcf, 0xe1, 0xff, 0x73, 0xd5, 0x41,
0xcc, 0x8, 0x32, 0x55, 0x6]
},
Vectors{
input: [u8(0x73), 0x41, 0x81, 0xcd, 0x1a, 0x94, 0x6, 0x52, 0x2a, 0x56, 0xfe, 0x25, 0xe4,
0x3e, 0xcb, 0xf0, 0x29, 0x5d, 0xb5, 0xdd, 0xd0, 0x60, 0x9b, 0x3c, 0x2b, 0x4e, 0x79,
0xc0, 0x6f, 0x8b, 0xd4, 0x6d]
base: [u8(0xf8), 0xa8, 0x42, 0x1c, 0x7d, 0x21, 0xa9, 0x2d, 0xb3, 0xed, 0xe9, 0x79, 0xe1,
0xfa, 0x6a, 0xcb, 0x6, 0x2b, 0x56, 0xb1, 0x88, 0x5c, 0x71, 0xc5, 0x11, 0x53, 0xcc,
0xb8, 0x80, 0xac, 0x73, 0x15]
expect: [u8(0x11), 0x76, 0xd0, 0x16, 0x81, 0xf2, 0xcf, 0x92, 0x9d, 0xa2, 0xc7, 0xa3, 0xdf,
0x66, 0xb5, 0xd7, 0x72, 0x9f, 0xd4, 0x22, 0x22, 0x6f, 0xd6, 0x37, 0x42, 0x16, 0xbf,
0x7e, 0x2, 0xfd, 0xf, 0x62]
},
Vectors{
input: [u8(0x1f), 0x70, 0x39, 0x1f, 0x6b, 0xa8, 0x58, 0x12, 0x94, 0x13, 0xbd, 0x80, 0x1b,
0x12, 0xac, 0xbf, 0x66, 0x23, 0x62, 0x82, 0x5c, 0xa2, 0x50, 0x9c, 0x81, 0x87, 0x59,
0xa, 0x2b, 0xe, 0x61, 0x72]
base: [u8(0xd3), 0xea, 0xd0, 0x7a, 0x0, 0x8, 0xf4, 0x45, 0x2, 0xd5, 0x80, 0x8b, 0xff,
0xc8, 0x97, 0x9f, 0x25, 0xa8, 0x59, 0xd5, 0xad, 0xf4, 0x31, 0x2e, 0xa4, 0x87, 0x48,
0x9c, 0x30, 0xe0, 0x1b, 0x3b]
expect: [u8(0xf8), 0x48, 0x2f, 0x2e, 0x9e, 0x58, 0xbb, 0x6, 0x7e, 0x86, 0xb2, 0x87, 0x24,
0xb3, 0xc0, 0xa3, 0xbb, 0xb5, 0x7, 0x3e, 0x4c, 0x6a, 0xcd, 0x93, 0xdf, 0x54, 0x5e,
0xff, 0xdb, 0xba, 0x50, 0x5f]
},
Vectors{
input: [u8(0x3a), 0x7a, 0xe6, 0xcf, 0x8b, 0x88, 0x9d, 0x2b, 0x7a, 0x60, 0xa4, 0x70, 0xad,
0x6a, 0xd9, 0x99, 0x20, 0x6b, 0xf5, 0x7d, 0x90, 0x30, 0xdd, 0xf7, 0xf8, 0x68, 0xc,
0x8b, 0x1a, 0x64, 0x5d, 0xaa]
base: [u8(0x4d), 0x25, 0x4c, 0x80, 0x83, 0xd8, 0x7f, 0x1a, 0x9b, 0x3e, 0xa7, 0x31, 0xef,
0xcf, 0xf8, 0xa6, 0xf2, 0x31, 0x2d, 0x6f, 0xed, 0x68, 0xe, 0xf8, 0x29, 0x18, 0x51,
0x61, 0xc8, 0xfc, 0x50, 0x60]
expect: [u8(0x47), 0xb3, 0x56, 0xd5, 0x81, 0x8d, 0xe8, 0xef, 0xac, 0x77, 0x4b, 0x71, 0x4c,
0x42, 0xc4, 0x4b, 0xe6, 0x85, 0x23, 0xdd, 0x57, 0xdb, 0xd7, 0x39, 0x62, 0xd5, 0xa5,
0x26, 0x31, 0x87, 0x62, 0x37]
},
Vectors{
input: [u8(0x20), 0x31, 0x61, 0xc3, 0x15, 0x9a, 0x87, 0x6a, 0x2b, 0xea, 0xec, 0x29, 0xd2,
0x42, 0x7f, 0xb0, 0xc7, 0xc3, 0xd, 0x38, 0x2c, 0xd0, 0x13, 0xd2, 0x7c, 0xc3, 0xd3,
0x93, 0xdb, 0xd, 0xaf, 0x6f]
base: [u8(0x6a), 0xb9, 0x5d, 0x1a, 0xbe, 0x68, 0xc0, 0x9b, 0x0, 0x5c, 0x3d, 0xb9, 0x4,
0x2c, 0xc9, 0x1a, 0xc8, 0x49, 0xf7, 0xe9, 0x4a, 0x2a, 0x4a, 0x9b, 0x89, 0x36, 0x78,
0x97, 0xb, 0x7b, 0x95, 0xbf]
expect: [u8(0x11), 0xed, 0xae, 0xdc, 0x95, 0xff, 0x78, 0xf5, 0x63, 0xa1, 0xc8, 0xf1, 0x55,
0x91, 0xc0, 0x71, 0xde, 0xa0, 0x92, 0xb4, 0xd7, 0xec, 0xaa, 0xc8, 0xe0, 0x38, 0x7b,
0x5a, 0x16, 0xc, 0x4e, 0x5d]
},
Vectors{
input: [u8(0x13), 0xd6, 0x54, 0x91, 0xfe, 0x75, 0xf2, 0x3, 0xa0, 0x8, 0xb4, 0x41, 0x5a,
0xbc, 0x60, 0xd5, 0x32, 0xe6, 0x95, 0xdb, 0xd2, 0xf1, 0xe8, 0x3, 0xac, 0xcb, 0x34,
0xb2, 0xb7, 0x2c, 0x3d, 0x70]
base: [u8(0x2e), 0x78, 0x4e, 0x4, 0xca, 0x0, 0x73, 0x33, 0x62, 0x56, 0xa8, 0x39, 0x25,
0x5e, 0xd2, 0xf7, 0xd4, 0x79, 0x6a, 0x64, 0xcd, 0xc3, 0x7f, 0x1e, 0xb0, 0xe5, 0xc4,
0xc8, 0xd1, 0xd1, 0xe0, 0xf5]
expect: [u8(0x56), 0x3e, 0x8c, 0x9a, 0xda, 0xa7, 0xd7, 0x31, 0x1, 0xb0, 0xf2, 0xea, 0xd3,
0xca, 0xe1, 0xea, 0x5d, 0x8f, 0xcd, 0x5c, 0xd3, 0x60, 0x80, 0xbb, 0x8e, 0x6e, 0xc0,
0x3d, 0x61, 0x45, 0x9, 0x17]
},
Vectors{
input: [u8(0x68), 0x6f, 0x7d, 0xa9, 0x3b, 0xf2, 0x68, 0xe5, 0x88, 0x6, 0x98, 0x31, 0xf0,
0x47, 0x16, 0x3f, 0x33, 0x58, 0x99, 0x89, 0xd0, 0x82, 0x6e, 0x98, 0x8, 0xfb, 0x67,
0x8e, 0xd5, 0x7e, 0x67, 0x49]
base: [u8(0x8b), 0x54, 0x9b, 0x2d, 0xf6, 0x42, 0xd3, 0xb2, 0x5f, 0xe8, 0x38, 0xf, 0x8c,
0xc4, 0x37, 0x5f, 0x99, 0xb7, 0xbb, 0x4d, 0x27, 0x5f, 0x77, 0x9f, 0x3b, 0x7c, 0x81,
0xb8, 0xa2, 0xbb, 0xc1, 0x29]
expect: [u8(0x1), 0x47, 0x69, 0x65, 0x42, 0x6b, 0x61, 0x71, 0x74, 0x9a, 0x8a, 0xdd, 0x92,
0x35, 0x2, 0x5c, 0xe5, 0xf5, 0x57, 0xfe, 0x40, 0x9, 0xf7, 0x39, 0x30, 0x44, 0xeb, 0xbb,
0x8a, 0xe9, 0x52, 0x79]
},
Vectors{
input: [u8(0x82), 0xd6, 0x1c, 0xce, 0xdc, 0x80, 0x6a, 0x60, 0x60, 0xa3, 0x34, 0x9a, 0x5e,
0x87, 0xcb, 0xc7, 0xac, 0x11, 0x5e, 0x4f, 0x87, 0x77, 0x62, 0x50, 0xae, 0x25, 0x60,
0x98, 0xa7, 0xc4, 0x49, 0x59]
base: [u8(0x8b), 0x6b, 0x9d, 0x8, 0xf6, 0x1f, 0xc9, 0x1f, 0xe8, 0xb3, 0x29, 0x53, 0xc4,
0x23, 0x40, 0xf0, 0x7, 0xb5, 0x71, 0xdc, 0xb0, 0xa5, 0x6d, 0x10, 0x72, 0x4e, 0xce,
0xf9, 0x95, 0xc, 0xfb, 0x25]
expect: [u8(0x9c), 0x49, 0x94, 0x1f, 0x9c, 0x4f, 0x18, 0x71, 0xfa, 0x40, 0x91, 0xfe, 0xd7,
0x16, 0xd3, 0x49, 0x99, 0xc9, 0x52, 0x34, 0xed, 0xf2, 0xfd, 0xfb, 0xa6, 0xd1, 0x4a,
0x5a, 0xfe, 0x9e, 0x5, 0x58]
},
Vectors{
input: [u8(0x7d), 0xc7, 0x64, 0x4, 0x83, 0x13, 0x97, 0xd5, 0x88, 0x4f, 0xdf, 0x6f, 0x97,
0xe1, 0x74, 0x4c, 0x9e, 0xb1, 0x18, 0xa3, 0x1a, 0x7b, 0x23, 0xf8, 0xd7, 0x9f, 0x48,
0xce, 0x9c, 0xad, 0x15, 0x4b]
base: [u8(0x1a), 0xcd, 0x29, 0x27, 0x84, 0xf4, 0x79, 0x19, 0xd4, 0x55, 0xf8, 0x87, 0x44,
0x83, 0x58, 0x61, 0xb, 0xb9, 0x45, 0x96, 0x70, 0xeb, 0x99, 0xde, 0xe4, 0x60, 0x5, 0xf6,
0x89, 0xca, 0x5f, 0xb6]
expect: [u8(0x0), 0xf4, 0x3c, 0x2, 0x2e, 0x94, 0xea, 0x38, 0x19, 0xb0, 0x36, 0xae, 0x2b,
0x36, 0xb2, 0xa7, 0x61, 0x36, 0xaf, 0x62, 0x8a, 0x75, 0x1f, 0xe5, 0xd0, 0x1e, 0x3,
0xd, 0x44, 0x25, 0x88, 0x59]
},
Vectors{
input: [u8(0xfb), 0xc4, 0x51, 0x1d, 0x23, 0xa6, 0x82, 0xae, 0x4e, 0xfd, 0x8, 0xc8, 0x17,
0x9c, 0x1c, 0x6, 0x7f, 0x9c, 0x8b, 0xe7, 0x9b, 0xbc, 0x4e, 0xff, 0x5c, 0xe2, 0x96,
0xc6, 0xbc, 0x1f, 0xf4, 0x45]
base: [u8(0x55), 0xca, 0xff, 0x21, 0x81, 0xf2, 0x13, 0x6b, 0xe, 0xd0, 0xe1, 0xe2, 0x99,
0x44, 0x48, 0xe1, 0x6c, 0xc9, 0x70, 0x64, 0x6a, 0x98, 0x3d, 0x14, 0xd, 0xc4, 0xea,
0xb3, 0xd9, 0x4c, 0x28, 0x4e]
expect: [u8(0xae), 0x39, 0xd8, 0x16, 0x53, 0x23, 0x45, 0x79, 0x4d, 0x26, 0x91, 0xe0, 0x80,
0x1c, 0xaa, 0x52, 0x5f, 0xc3, 0x63, 0x4d, 0x40, 0x2c, 0xe9, 0x58, 0xb, 0x33, 0x38,
0xb4, 0x6f, 0x8b, 0xb9, 0x72]
},
Vectors{
input: [u8(0x4e), 0x6, 0xc, 0xe1, 0xc, 0xeb, 0xf0, 0x95, 0x9, 0x87, 0x16, 0xc8, 0x66,
0x19, 0xeb, 0x9f, 0x7d, 0xf6, 0x65, 0x24, 0x69, 0x8b, 0xa7, 0x98, 0x8c, 0x3b, 0x90,
0x95, 0xd9, 0xf5, 0x1, 0x34]
base: [u8(0x57), 0x73, 0x3f, 0x2d, 0x86, 0x96, 0x90, 0xd0, 0xd2, 0xed, 0xae, 0xc9, 0x52,
0x3d, 0xaa, 0x2d, 0xa9, 0x54, 0x45, 0xf4, 0x4f, 0x57, 0x83, 0xc1, 0xfa, 0xec, 0x6c,
0x3a, 0x98, 0x28, 0x18, 0xf3]
expect: [u8(0xa6), 0x1e, 0x74, 0x55, 0x2c, 0xce, 0x75, 0xf5, 0xe9, 0x72, 0xe4, 0x24, 0xf2,
0xcc, 0xb0, 0x9c, 0x83, 0xbc, 0x1b, 0x67, 0x1, 0x47, 0x48, 0xf0, 0x2c, 0x37, 0x1a,
0x20, 0x9e, 0xf2, 0xfb, 0x2c]
},
Vectors{
input: [u8(0x5c), 0x49, 0x2c, 0xba, 0x2c, 0xc8, 0x92, 0x48, 0x8a, 0x9c, 0xeb, 0x91, 0x86,
0xc2, 0xaa, 0xc2, 0x2f, 0x1, 0x5b, 0xf3, 0xef, 0x8d, 0x3e, 0xcc, 0x9c, 0x41, 0x76,
0x97, 0x62, 0x61, 0xaa, 0xb1]
base: [u8(0x67), 0x97, 0xc2, 0xe7, 0xdc, 0x92, 0xcc, 0xbe, 0x7c, 0x5, 0x6b, 0xec, 0x35,
0xa, 0xb6, 0xd3, 0xbd, 0x2a, 0x2c, 0x6b, 0xc5, 0xa8, 0x7, 0xbb, 0xca, 0xe1, 0xf6, 0xc2,
0xaf, 0x80, 0x36, 0x44]
expect: [u8(0xfc), 0xf3, 0x7, 0xdf, 0xbc, 0x19, 0x2, 0xb, 0x28, 0xa6, 0x61, 0x8c, 0x6c,
0x62, 0x2f, 0x31, 0x7e, 0x45, 0x96, 0x7d, 0xac, 0xf4, 0xae, 0x4a, 0xa, 0x69, 0x9a,
0x10, 0x76, 0x9f, 0xde, 0x14]
},
Vectors{
input: [u8(0xea), 0x33, 0x34, 0x92, 0x96, 0x5, 0x5a, 0x4e, 0x8b, 0x19, 0x2e, 0x3c, 0x23,
0xc5, 0xf4, 0xc8, 0x44, 0x28, 0x2a, 0x3b, 0xfc, 0x19, 0xec, 0xc9, 0xdc, 0x64, 0x6a,
0x42, 0xc3, 0x8d, 0xc2, 0x48]
base: [u8(0x2c), 0x75, 0xd8, 0x51, 0x42, 0xec, 0xad, 0x3e, 0x69, 0x44, 0x70, 0x4, 0x54,
0xc, 0x1c, 0x23, 0x54, 0x8f, 0xc8, 0xf4, 0x86, 0x25, 0x1b, 0x8a, 0x19, 0x46, 0x3f,
0x3d, 0xf6, 0xf8, 0xac, 0x61]
expect: [u8(0x5d), 0xca, 0xb6, 0x89, 0x73, 0xf9, 0x5b, 0xd3, 0xae, 0x4b, 0x34, 0xfa, 0xb9,
0x49, 0xfb, 0x7f, 0xb1, 0x5a, 0xf1, 0xd8, 0xca, 0xe2, 0x8c, 0xd6, 0x99, 0xf9, 0xc1,
0xaa, 0x33, 0x37, 0x34, 0x2f]
},
Vectors{
input: [u8(0x4f), 0x29, 0x79, 0xb1, 0xec, 0x86, 0x19, 0xe4, 0x5c, 0xa, 0xb, 0x2b, 0x52,
0x9, 0x34, 0x54, 0x1a, 0xb9, 0x44, 0x7, 0xb6, 0x4d, 0x19, 0xa, 0x76, 0xf3, 0x23, 0x14,
0xef, 0xe1, 0x84, 0xe7]
base: [u8(0xf7), 0xca, 0xe1, 0x8d, 0x8d, 0x36, 0xa7, 0xf5, 0x61, 0x17, 0xb8, 0xb7, 0xe,
0x25, 0x52, 0x27, 0x7f, 0xfc, 0x99, 0xdf, 0x87, 0x56, 0xb5, 0xe1, 0x38, 0xbf, 0x63,
0x68, 0xbc, 0x87, 0xf7, 0x4c]
expect: [u8(0xe4), 0xe6, 0x34, 0xeb, 0xb4, 0xfb, 0x66, 0x4f, 0xe8, 0xb2, 0xcf, 0xa1, 0x61,
0x5f, 0x0, 0xe6, 0x46, 0x6f, 0xff, 0x73, 0x2c, 0xe1, 0xf8, 0xa0, 0xc8, 0xd2, 0x72,
0x74, 0x31, 0xd1, 0x6f, 0x14]
},
Vectors{
input: [u8(0xf5), 0xd8, 0xa9, 0x27, 0x90, 0x1d, 0x4f, 0xa4, 0x24, 0x90, 0x86, 0xb7, 0xff,
0xec, 0x24, 0xf5, 0x29, 0x7d, 0x80, 0x11, 0x8e, 0x4a, 0xc9, 0xd3, 0xfc, 0x9a, 0x82,
0x37, 0x95, 0x1e, 0x3b, 0x7f]
base: [u8(0x3c), 0x23, 0x5e, 0xdc, 0x2, 0xf9, 0x11, 0x56, 0x41, 0xdb, 0xf5, 0x16, 0xd5,
0xde, 0x8a, 0x73, 0x5d, 0x6e, 0x53, 0xe2, 0x2a, 0xa2, 0xac, 0x14, 0x36, 0x56, 0x4,
0x5f, 0xf2, 0xe9, 0x52, 0x49]
expect: [u8(0xab), 0x95, 0x15, 0xab, 0x14, 0xaf, 0x9d, 0x27, 0xe, 0x1d, 0xae, 0xc, 0x56,
0x80, 0xcb, 0xc8, 0x88, 0xb, 0xd8, 0xa8, 0xe7, 0xeb, 0x67, 0xb4, 0xda, 0x42, 0xa6,
0x61, 0x96, 0x1e, 0xfc, 0xb]
},
]
// Please be aware, this is long running times test, its loop until 1.000.000 times.
// so, please be patient. See the detail of the type 2 test from rfc
// see at https://datatracker.ietf.org/doc/html/rfc7748#section-5.2
//
// Currently, this test was disabled due to its takes long time to complete,
// please uncomment it if you need run the test.
// On my test machine, its pass successfully in 2225590.282 ms
// ```v
// ... (previous line)
// ...
// start i: 999995
// start i: 999996
// start i: 999997
// start i: 999998
// start i: 999999
//
// OK 2225590.282 ms 3 asserts | curve25519.test_x25519_after_iteration_from_rfc_vector_type2()
// Summary for running V tests in "curve25519_test.v": 3 passed, 3 total. Elapsed time: 2225590 ms.
// ```
/*
fn test_x25519_after_iteration_from_rfc_vector_type2() ! {
iteration1 := hex.decode('422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854b783c60e80311ae3079')!
iteration1000 := hex.decode('684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d99532c51') !
iteration1000000 := hex.decode('7c3911e0ab2586fd864497297e575e6f3bc601c0883c30df5f4dd2d24f665424') !
// Initially, set k and u to be the following values
key := '0900000000000000000000000000000000000000000000000000000000000000'
mut k := hex.decode(key)!
mut u := k.clone()
mut r := []u8{len: 32}
// For each iteration, set k to be the result of calling the function and u
// to be the old value of k. The final result is the value left in k.
//
for i in 0..1000000 {
println("start i: $i")
tmp_k := k.clone()
r = x25519(mut k, u) !
unsafe {u = tmp_k}
unsafe {k = r}
if i == 0 {
assert k == iteration1
} else if i == 999 {
assert k == iteration1000
} else if i == 999999 {
assert k == iteration1000000
}
}
}
*/

View file

@ -0,0 +1,39 @@
// Copyright © 2025 blackshirt.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
//
// This code snippet give us an example on how to use this `curve25519` module
// on creating shared secret with custom derivation function
import crypto.sha512
import encoding.hex
import x.crypto.curve25519
// Custom SHA384 based derivator
struct Sha384Derivator {
}
// derives a new shared secret by hashing the sec bytes with sha384 hash
fn (sd Sha384Derivator) derive(sec []u8, opt curve25519.DeriveOpts) ![]u8 {
return sha512.sum384(sec)
}
fn main() {
// Sample of randomly generated 32 bytes length of the key as a Alice key
a_pvkey := hex.decode('844f744729d93369147d48d82d18b979f0b4be5f27b4b21c68fd42804575fe29')!
mut alice_pvkey := curve25519.PrivateKey.new_from_seed(a_pvkey)!
// For example, Alice receives Bob's public key
b_pubkey := hex.decode('7f869b403ddb1f5708bc2f8246ae78fa53ea304af585d5bb14fea3aafe5315c7')!
bob_pubkey := curve25519.PublicKey.new_from_bytes(b_pubkey)!
opt := curve25519.SharedOpts{
should_derive: true
derivator: Sha384Derivator{}
}
shared_sec := curve25519.derive_shared_secret(mut alice_pvkey, bob_pubkey, opt)!
// shared_sec now contains sha384 digest form, derived from sec bytes supplied
// shared_sec.hex(): 88d648a1c5437e2807ed48aaea7ae3190ffe4b2b74008cd2c1ace9bddf28afe50b9a0c9e06dd50bbf73e0203bc1775dc
dump(shared_sec.hex())
dump(shared_sec.len) // shared_sec.len: 48
}

View file

@ -0,0 +1,23 @@
// Copyright © 2025 blackshirt.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
//
// This code snippet give us an example on how to use this `curve25519` module
// on creating shared secret through `x25519` function thats accepts raw bytes
import encoding.hex
import x.crypto.curve25519
fn main() {
// Sample of randomly generated 32 bytes length of the key as a Alice key
a_privkey := '844f744729d93369147d48d82d18b979f0b4be5f27b4b21c68fd42804575fe29'
mut alice_privkey := hex.decode(a_privkey)!
// For example, Alice receives Bob's public key
b_pubkey := '7f869b403ddb1f5708bc2f8246ae78fa53ea304af585d5bb14fea3aafe5315c7'
bob_pubkey := hex.decode(b_pubkey)!
// Then, Alice can generated shared secret to be shared with Bob
shared_sec := curve25519.x25519(mut alice_privkey, bob_pubkey)!
dump(shared_sec.hex()) // shared_sec.hex(): dfa1c80d488e3de14389a852ae8f4b2f6831f8e5cea80694c7ea104ffe694858
dump(shared_sec.len) // shared_sec.len: 32
}

View file

@ -0,0 +1,30 @@
// Copyright © 2025 blackshirt.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
//
// Usage example of the `curve25519` module
import x.crypto.curve25519
fn test_curve25519_shared_secret_key_exchange() ! {
// Alice generates a private key
mut alice_pvkey := curve25519.PrivateKey.new()!
// Alice's PublicKey to be shared with Bob
alice_pbkey := alice_pvkey.public_key()!
// The other peer, Bob, has a different private key
mut bob_pvkey := curve25519.PrivateKey.new()!
// Bob's public key to be shared
bob_pbkey := bob_pvkey.public_key()!
// Let the two peers exchange their respective public keys
//
// Alice derives the shared secret, using her own private key, and the public key that Bob shared
alice_shared_sec := curve25519.derive_shared_secret(mut alice_pvkey, bob_pbkey)!
// Bob derives the shared secret, using his own private key, and the public key that Alice shared
bob_shared_sec := curve25519.derive_shared_secret(mut bob_pvkey, alice_pbkey)!
// the two shared secrets (derived by Alice, and derived by Bob), should be the same
//
assert alice_shared_sec == bob_shared_sec
}