feature. See also
. The project being documented here (as the example) is the Zig library itself.
Client.KeyShare
const KeyShare = struct
File
Code
const KeyShare = struct {
ml_kem768_kp: crypto.kem.ml_kem.MLKem768.KeyPair,
secp256r1_kp: crypto.sign.ecdsa.EcdsaP256Sha256.KeyPair,
secp384r1_kp: crypto.sign.ecdsa.EcdsaP384Sha384.KeyPair,
x25519_kp: crypto.dh.X25519.KeyPair,
sk_buf: [sk_max_len]u8,
sk_len: std.math.IntFittingRange(0, sk_max_len),
const sk_max_len = @max(
crypto.dh.X25519.shared_length + crypto.kem.ml_kem.MLKem768.shared_length,
crypto.ecc.P256.scalar.encoded_length,
crypto.ecc.P384.scalar.encoded_length,
crypto.dh.X25519.shared_length,
);
fn init(seed: *const [176]u8) error{IdentityElement}!KeyShare {
return .{
.ml_kem768_kp = try .generateDeterministic(seed[0..64].*),
.secp256r1_kp = try .generateDeterministic(seed[64..96].*),
.secp384r1_kp = try .generateDeterministic(seed[96..144].*),
.x25519_kp = try .generateDeterministic(seed[144..176].*),
.sk_buf = undefined,
.sk_len = 0,
};
}
fn exchange(
ks: *KeyShare,
named_group: tls.NamedGroup,
server_pub_key: []const u8,
) error{ TlsIllegalParameter, TlsDecryptFailure }!void {
switch (named_group) {
.x25519_ml_kem768 => {
const hksl = crypto.kem.ml_kem.MLKem768.ciphertext_length;
const xksl = hksl + crypto.dh.X25519.public_length;
if (server_pub_key.len != xksl) return error.TlsIllegalParameter;
const hsk = ks.ml_kem768_kp.secret_key.decaps(server_pub_key[0..hksl]) catch
return error.TlsDecryptFailure;
const xsk = crypto.dh.X25519.scalarmult(ks.x25519_kp.secret_key, server_pub_key[hksl..xksl].*) catch
return error.TlsDecryptFailure;
@memcpy(ks.sk_buf[0..hsk.len], &hsk);
@memcpy(ks.sk_buf[hsk.len..][0..xsk.len], &xsk);
ks.sk_len = hsk.len + xsk.len;
},
.secp256r1 => {
const PublicKey = crypto.sign.ecdsa.EcdsaP256Sha256.PublicKey;
const pk = PublicKey.fromSec1(server_pub_key) catch return error.TlsDecryptFailure;
const mul = pk.p.mulPublic(ks.secp256r1_kp.secret_key.bytes, .big) catch
return error.TlsDecryptFailure;
const sk = mul.affineCoordinates().x.toBytes(.big);
@memcpy(ks.sk_buf[0..sk.len], &sk);
ks.sk_len = sk.len;
},
.secp384r1 => {
const PublicKey = crypto.sign.ecdsa.EcdsaP384Sha384.PublicKey;
const pk = PublicKey.fromSec1(server_pub_key) catch return error.TlsDecryptFailure;
const mul = pk.p.mulPublic(ks.secp384r1_kp.secret_key.bytes, .big) catch
return error.TlsDecryptFailure;
const sk = mul.affineCoordinates().x.toBytes(.big);
@memcpy(ks.sk_buf[0..sk.len], &sk);
ks.sk_len = sk.len;
},
.x25519 => {
const ksl = crypto.dh.X25519.public_length;
if (server_pub_key.len != ksl) return error.TlsIllegalParameter;
const sk = crypto.dh.X25519.scalarmult(ks.x25519_kp.secret_key, server_pub_key[0..ksl].*) catch
return error.TlsDecryptFailure;
@memcpy(ks.sk_buf[0..sk.len], &sk);
ks.sk_len = sk.len;
},
else => return error.TlsIllegalParameter,
}
}
fn getSharedSecret(ks: *const KeyShare) ?[]const u8 {
return if (ks.sk_len > 0) ks.sk_buf[0..ks.sk_len] else null;
}
}