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}