1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
//! TLCP key schedule — GB/T 38636-2020 §6.5.
//!
//! The TLS-1.2-style PRF (`P_hash`, RFC 5246 §5 structure)
//! instantiated with HMAC-SM3, plus the three derivations the
//! protocol needs. Engine-shaped: explicit inputs, caller-supplied
//! output buffers (the caller owns placement AND wiping — the
//! `pbkdf2_hmac_sm3` discipline), no hidden state.
//!
//! Pinned facts (verification items D-2/D-7/D-10 of the
//! decomposition, resolved against the gotlcp reference
//! implementation):
//! - The pre-master secret is 48 bytes in BOTH TLCP key-exchange
//! variants (ECC key transport: `version(2) ‖ random(46)`; ECDHE:
//! SM2-KX output with klen = 48) — hence the typed `[u8; 48]`.
//! - TLCP handshake signatures (`ServerKeyExchange`,
//! `CertificateVerify`) are plain SM2 message-mode signatures with
//! the default signer ID; they need [`crate::sm2::sign_with_id`],
//! not this module.
//! - An SM2 key-transport decrypt failure may abort the handshake:
//! GM/T 0009 ciphertext is integrity-protected (C3), so the
//! RSA-style random-PMS-substitution countermeasure is not
//! load-bearing here.
use crateHmacSm3;
use Zeroize;
/// TLCP master secret length (GB/T 38636 §6.5.1).
pub const MASTER_SECRET_LEN: usize = 48;
/// TLCP Finished `verify_data` length (GB/T 38636 §6.4.5.10).
pub const FINISHED_VERIFY_DATA_LEN: usize = 12;
/// `P_SM3(secret, label ‖ seed1 ‖ seed2)` per RFC 5246 §5 (the
/// GB/T 38636 §6.5 PRF), writing exactly `out.len()` bytes.
///
/// `A(0) = label ‖ seeds; A(i) = HMAC(secret, A(i-1))`;
/// `out = HMAC(secret, A(1) ‖ label ‖ seeds) ‖ HMAC(secret, A(2) ‖ …) …`
///
/// Private by design (scope Q6.4): every TLCP use is one of the
/// three public derivations. The `A(i)` chain and each output block
/// are secret-derived and wiped; loop bounds depend only on the
/// public `out.len()`.
/// Derive the 48-byte TLCP master secret (GB/T 38636 §6.5.1):
/// `PRF(pre_master, "master secret", client_random ‖ server_random)`.
///
/// `pre_master` is typed `[u8; 48]` — TLCP pins the pre-master secret
/// to 48 bytes in both key-exchange variants. The caller owns wiping
/// `out` (and its `pre_master` copy) when done.
/// Expand the key block (GB/T 38636 §6.5.2):
/// `PRF(master_secret, "key expansion", server_random ‖ client_random)`.
///
/// The seed order FLIPS vs master-secret derivation (server first —
/// a KAT-pinned trap). Writes exactly `out.len()` bytes; the caller
/// carves per suite, in this order: client MAC key, server MAC key,
/// client key, server key, client IV, server IV (CBC suites:
/// 2×(32+16+16) = 128 bytes; GCM suites: 2×(0+16+4) = 40 bytes, the
/// 4-byte halves being the implicit nonce salts). Suite-agnostic by
/// design (engine-shaped, resumption-agnostic). `out.len() == 0` is
/// vacuously fine. The caller owns wiping `out`.
/// Finished-message role: selects the `verify_data` label.
/// Compute Finished `verify_data` (GB/T 38636 §6.4.5.10):
/// `PRF(master_secret, label, SM3(handshake_messages))[0..12]`.
///
/// `transcript_hash` is the SM3 hash of all handshake messages up to
/// (excluding) this Finished — hashing the transcript is the caller's
/// (a [`crate::sm3::Sm3`] over the message bytes); this keeps the
/// function stateless and engine-shaped.