ssh/algorithm/hash/hash.rs
1use super::hash_ctx::HashCtx;
2use crate::algorithm::hash;
3use crate::algorithm::hash::HashType;
4use crate::constant;
5
6/// <https://www.rfc-editor.org/rfc/rfc4253#section-7.2>
7///
8/// The key exchange produces two values: a shared secret K, and an
9/// exchange hash H. Encryption and authentication keys are derived from
10/// these. The exchange hash H from the first key exchange is
11/// additionally used as the session identifier, which is a unique
12/// identifier for this connection. It is used by authentication methods
13/// as a part of the data that is signed as a proof of possession of a
14/// private key. Once computed, the session identifier is not changed,
15/// even if keys are later re-exchanged.
16
17/// Each key exchange method specifies a hash function that is used in
18/// the key exchange. The same hash algorithm MUST be used in key
19/// derivation. Here, we'll call it HASH.
20
21/// Encryption keys MUST be computed as HASH, of a known value and K, as
22/// follows:
23
24/// o Initial IV client to server: HASH(K || H || "A" || session_id)
25/// (Here K is encoded as mpint and "A" as byte and session_id as raw
26/// data. "A" means the single character A, ASCII 65).
27
28/// o Initial IV server to client: HASH(K || H || "B" || session_id)
29
30/// o Encryption key client to server: HASH(K || H || "C" || session_id)
31
32/// o Encryption key server to client: HASH(K || H || "D" || session_id)
33
34/// o Integrity key client to server: HASH(K || H || "E" || session_id)
35
36/// o Integrity key server to client: HASH(K || H || "F" || session_id)
37
38/// Key data MUST be taken from the beginning of the hash output. As
39/// many bytes as needed are taken from the beginning of the hash value.
40/// If the key length needed is longer than the output of the HASH, the
41/// key is extended by computing HASH of the concatenation of K and H and
42/// the entire key so far, and appending the resulting bytes (as many as
43/// HASH generates) to the key. This process is repeated until enough
44/// key material is available; the key is taken from the beginning of
45/// this value. In other words:
46
47/// K1 = HASH(K || H || X || session_id) (X is e.g., "A")
48/// K2 = HASH(K || H || K1)
49/// K3 = HASH(K || H || K1 || K2)
50/// ...
51/// key = K1 || K2 || K3 || ...
52
53/// This process will lose entropy if the amount of entropy in K is
54/// larger than the internal state size of HASH.
55pub struct Hash {
56 /// reandom number used once
57 pub iv_c_s: Vec<u8>,
58 pub iv_s_c: Vec<u8>,
59
60 /// key used for data exchange
61 pub ek_c_s: Vec<u8>,
62 pub ek_s_c: Vec<u8>,
63
64 /// key used for hmac
65 pub ik_c_s: Vec<u8>,
66 pub ik_s_c: Vec<u8>,
67
68 hash_type: HashType,
69 hash_ctx: HashCtx,
70}
71
72impl Hash {
73 pub fn new(hash_ctx: HashCtx, session_id: &[u8], hash_type: HashType) -> Self {
74 let k = hash_ctx.k.as_slice();
75 let h = hash::digest(&hash_ctx.as_bytes(), hash_type);
76 let mut keys = vec![];
77 for v in constant::ALPHABET {
78 keys.push(Hash::mix(k, &h, v, session_id, hash_type));
79 }
80 Hash {
81 iv_c_s: keys[0].clone(),
82 iv_s_c: keys[1].clone(),
83
84 ek_c_s: keys[2].clone(),
85 ek_s_c: keys[3].clone(),
86
87 ik_c_s: keys[4].clone(),
88 ik_s_c: keys[5].clone(),
89
90 hash_type,
91 hash_ctx,
92 }
93 }
94
95 fn mix(k: &[u8], h: &[u8], key_char: u8, session_id: &[u8], hash_type: HashType) -> Vec<u8> {
96 let mut key: Vec<u8> = Vec::new();
97 key.extend(k);
98 key.extend(h);
99 key.push(key_char);
100 key.extend(session_id);
101 hash::digest(key.as_slice(), hash_type)
102 }
103
104 pub fn mix_ek(&self, key_size: usize) -> (Vec<u8>, Vec<u8>) {
105 let mut ck = self.ek_c_s.to_vec();
106 let mut sk = self.ek_s_c.to_vec();
107 while key_size > ck.len() {
108 ck.extend(self.extend(ck.as_slice()));
109 sk.extend(self.extend(sk.as_slice()));
110 }
111 (ck, sk)
112 }
113
114 pub fn mix_ik(&self, key_size: usize) -> (Vec<u8>, Vec<u8>) {
115 let mut ck = self.ik_c_s.to_vec();
116 let mut sk = self.ik_s_c.to_vec();
117 while key_size > ck.len() {
118 ck.extend(self.extend(ck.as_slice()));
119 sk.extend(self.extend(sk.as_slice()));
120 }
121 (ck, sk)
122 }
123
124 fn extend(&self, key: &[u8]) -> Vec<u8> {
125 let k = self.hash_ctx.k.clone();
126 let h = hash::digest(self.hash_ctx.as_bytes().as_slice(), self.hash_type);
127 let mut hash: Vec<u8> = Vec::new();
128 hash.extend(k);
129 hash.extend(h);
130 hash.extend(key);
131 hash::digest(hash.as_slice(), self.hash_type)
132 }
133}