aboutsummaryrefslogtreecommitdiff
path: root/lib/std
diff options
context:
space:
mode:
authorFrank Denis <124872+jedisct1@users.noreply.github.com>2025-08-27 11:18:40 +0200
committerGitHub <noreply@github.com>2025-08-27 11:18:40 +0200
commit12a58087a423e33dd5fc8daa5c9fb556fe93f7a9 (patch)
treee941d6830db99ccba93546c626594f2a0b7e3010 /lib/std
parentae2622bf828cd0b2abd7cc3e6b1294de0b167dc6 (diff)
downloadzig-12a58087a423e33dd5fc8daa5c9fb556fe93f7a9.tar.gz
zig-12a58087a423e33dd5fc8daa5c9fb556fe93f7a9.zip
Fix TLS 1.2 client key exchange to use negotiated named group (#25007)
The TLS 1.2 implementation was incorrectly hardcoded to always send the secp256r1 public key in the client key exchange message, regardless of which elliptic curve the server actually negotiated. This caused TLS handshake failures with servers that preferred other curves like X25519. This fix: - Tracks the negotiated named group from the server key exchange message - Dynamically selects the correct public key (X25519, secp256r1, or secp384r1) based on what the server negotiated - Properly constructs the client key exchange message with the appropriate key size for each curve type Fixes TLS 1.2 connections to servers like ziglang.freetls.fastly.net that prefer X25519 over secp256r1.
Diffstat (limited to 'lib/std')
-rw-r--r--lib/std/crypto/tls/Client.zig25
1 files changed, 19 insertions, 6 deletions
diff --git a/lib/std/crypto/tls/Client.zig b/lib/std/crypto/tls/Client.zig
index f9c897e3f0..8cfa942399 100644
--- a/lib/std/crypto/tls/Client.zig
+++ b/lib/std/crypto/tls/Client.zig
@@ -320,6 +320,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client
var handshake_state: HandshakeState = .hello;
var handshake_cipher: tls.HandshakeCipher = undefined;
var main_cert_pub_key: CertificatePublicKey = undefined;
+ var tls12_negotiated_group: ?tls.NamedGroup = null;
const now_sec = std.time.timestamp();
var cleartext_fragment_start: usize = 0;
@@ -679,6 +680,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client
const curve_type = hsd.decode(u8);
if (curve_type != 0x03) return error.TlsIllegalParameter; // named_curve
const named_group = hsd.decode(tls.NamedGroup);
+ tls12_negotiated_group = named_group;
const key_size = hsd.decode(u8);
try hsd.ensure(key_size);
const server_pub_key = hsd.slice(key_size);
@@ -691,10 +693,19 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client
if (cipher_state != .cleartext) return error.TlsUnexpectedMessage;
if (handshake_state != .server_hello_done) return error.TlsUnexpectedMessage;
- const client_key_exchange_msg = .{@intFromEnum(tls.ContentType.handshake)} ++
+ const public_key_bytes: []const u8 = switch (tls12_negotiated_group orelse .secp256r1) {
+ .secp256r1 => &key_share.secp256r1_kp.public_key.toUncompressedSec1(),
+ .secp384r1 => &key_share.secp384r1_kp.public_key.toUncompressedSec1(),
+ .x25519 => &key_share.x25519_kp.public_key,
+ else => return error.TlsIllegalParameter,
+ };
+
+ const client_key_exchange_prefix = .{@intFromEnum(tls.ContentType.handshake)} ++
int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
- array(u16, u8, .{@intFromEnum(tls.HandshakeType.client_key_exchange)} ++
- array(u24, u8, array(u8, u8, key_share.secp256r1_kp.public_key.toUncompressedSec1())));
+ int(u16, @intCast(public_key_bytes.len + 5)) ++ // record length
+ .{@intFromEnum(tls.HandshakeType.client_key_exchange)} ++
+ int(u24, @intCast(public_key_bytes.len + 1)) ++ // handshake message length
+ .{@as(u8, @intCast(public_key_bytes.len))}; // public key length
const client_change_cipher_spec_msg = .{@intFromEnum(tls.ContentType.change_cipher_spec)} ++
int(u16, @intFromEnum(tls.ProtocolVersion.tls_1_2)) ++
array(u16, tls.ChangeCipherSpecType, .{.change_cipher_spec});
@@ -703,7 +714,8 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client
inline else => |*p| {
const P = @TypeOf(p.*).A;
p.transcript_hash.update(wrapped_handshake);
- p.transcript_hash.update(client_key_exchange_msg[tls.record_header_len..]);
+ p.transcript_hash.update(client_key_exchange_prefix[tls.record_header_len..]);
+ p.transcript_hash.update(public_key_bytes);
const master_secret = hmacExpandLabel(P.Hmac, pre_master_secret, &.{
"master secret",
&client_hello_rand,
@@ -757,8 +769,9 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client
nonce,
pv.app_cipher.client_write_key,
);
- var all_msgs_vec: [3][]const u8 = .{
- &client_key_exchange_msg,
+ var all_msgs_vec: [4][]const u8 = .{
+ &client_key_exchange_prefix,
+ public_key_bytes,
&client_change_cipher_spec_msg,
&client_verify_msg,
};