auths_core/crypto/ssh/
encoding.rs1use auths_crypto::CurveType;
8
9pub fn encode_ssh_pubkey(pubkey: &[u8], curve: CurveType) -> Vec<u8> {
23 match curve {
24 CurveType::Ed25519 => encode_ssh_pubkey_ed25519(pubkey),
25 CurveType::P256 => encode_ssh_pubkey_ecdsa_p256(pubkey),
26 }
27}
28
29pub fn encode_ssh_signature(signature: &[u8], curve: CurveType) -> Vec<u8> {
43 match curve {
44 CurveType::Ed25519 => encode_ssh_sig_ed25519(signature),
45 CurveType::P256 => encode_ssh_sig_ecdsa_p256(signature),
46 }
47}
48
49fn encode_ssh_pubkey_ed25519(pubkey: &[u8]) -> Vec<u8> {
50 let mut blob = Vec::new();
51 let key_type = b"ssh-ed25519";
52 blob.extend_from_slice(&(key_type.len() as u32).to_be_bytes());
53 blob.extend_from_slice(key_type);
54 blob.extend_from_slice(&(pubkey.len() as u32).to_be_bytes());
55 blob.extend_from_slice(pubkey);
56 blob
57}
58
59fn encode_ssh_pubkey_ecdsa_p256(pubkey: &[u8]) -> Vec<u8> {
60 let uncompressed = if pubkey.len() == 33 {
61 decompress_p256(pubkey)
62 } else {
63 pubkey.to_vec()
64 };
65
66 let mut blob = Vec::new();
67 let key_type = b"ecdsa-sha2-nistp256";
68 let curve_name = b"nistp256";
69 blob.extend_from_slice(&(key_type.len() as u32).to_be_bytes());
70 blob.extend_from_slice(key_type);
71 blob.extend_from_slice(&(curve_name.len() as u32).to_be_bytes());
72 blob.extend_from_slice(curve_name);
73 blob.extend_from_slice(&(uncompressed.len() as u32).to_be_bytes());
74 blob.extend_from_slice(&uncompressed);
75 blob
76}
77
78fn decompress_p256(compressed: &[u8]) -> Vec<u8> {
79 use p256::PublicKey;
80 use p256::elliptic_curve::sec1::ToEncodedPoint;
81
82 match PublicKey::from_sec1_bytes(compressed) {
83 Ok(pk) => {
84 let point = pk.to_encoded_point(false);
85 point.as_bytes().to_vec()
86 }
87 Err(_) => compressed.to_vec(),
88 }
89}
90
91fn encode_ssh_sig_ed25519(signature: &[u8]) -> Vec<u8> {
92 let mut blob = Vec::new();
93 let sig_type = b"ssh-ed25519";
94 blob.extend_from_slice(&(sig_type.len() as u32).to_be_bytes());
95 blob.extend_from_slice(sig_type);
96 blob.extend_from_slice(&(signature.len() as u32).to_be_bytes());
97 blob.extend_from_slice(signature);
98 blob
99}
100
101fn encode_ssh_sig_ecdsa_p256(signature: &[u8]) -> Vec<u8> {
102 let r = &signature[..32];
103 let s = &signature[32..];
104
105 let mut inner = Vec::new();
106 inner.extend_from_slice(&encode_mpint(r));
107 inner.extend_from_slice(&encode_mpint(s));
108
109 let mut blob = Vec::new();
110 let sig_type = b"ecdsa-sha2-nistp256";
111 blob.extend_from_slice(&(sig_type.len() as u32).to_be_bytes());
112 blob.extend_from_slice(sig_type);
113 blob.extend_from_slice(&(inner.len() as u32).to_be_bytes());
114 blob.extend_from_slice(&inner);
115 blob
116}
117
118fn encode_mpint(bytes: &[u8]) -> Vec<u8> {
119 let trimmed = match bytes.iter().position(|&b| b != 0) {
120 Some(pos) => &bytes[pos..],
121 None => &bytes[bytes.len() - 1..],
122 };
123
124 let mut buf = Vec::new();
125 if trimmed[0] & 0x80 != 0 {
126 buf.extend_from_slice(&((trimmed.len() + 1) as u32).to_be_bytes());
127 buf.push(0x00);
128 buf.extend_from_slice(trimmed);
129 } else {
130 buf.extend_from_slice(&(trimmed.len() as u32).to_be_bytes());
131 buf.extend_from_slice(trimmed);
132 }
133 buf
134}
135
136pub fn encode_mpint_for_agent(bytes: &[u8]) -> Vec<u8> {
148 encode_mpint(bytes)
149}