auths_core/crypto/ssh/
signatures.rs1use sha2::{Digest, Sha512};
4use ssh_key::private::{Ed25519Keypair as SshEd25519Keypair, KeypairData};
5use ssh_key::{HashAlg, LineEnding, PrivateKey as SshPrivateKey, SshSig};
6
7use super::CryptoError;
8use super::SecureSeed;
9use super::encoding::{encode_ssh_pubkey, encode_ssh_signature};
10
11pub fn create_sshsig(
27 seed: &SecureSeed,
28 data: &[u8],
29 namespace: &str,
30) -> Result<String, CryptoError> {
31 let ssh_keypair = SshEd25519Keypair::from_seed(seed.as_bytes());
32 let keypair_data = KeypairData::Ed25519(ssh_keypair);
33 let private_key = SshPrivateKey::new(keypair_data, "auths-sign")
34 .map_err(|e| CryptoError::SshKeyConstruction(e.to_string()))?;
35
36 let sshsig = SshSig::sign(&private_key, namespace, HashAlg::Sha512, data)
37 .map_err(|e| CryptoError::SigningFailed(e.to_string()))?;
38
39 let pem = sshsig
40 .to_pem(LineEnding::LF)
41 .map_err(|e| CryptoError::PemEncoding(e.to_string()))?;
42
43 Ok(pem)
44}
45
46pub fn construct_sshsig_signed_data(data: &[u8], namespace: &str) -> Result<Vec<u8>, CryptoError> {
65 let mut blob = Vec::new();
66
67 blob.extend_from_slice(b"SSHSIG");
69
70 blob.extend_from_slice(&(namespace.len() as u32).to_be_bytes());
71 blob.extend_from_slice(namespace.as_bytes());
72
73 blob.extend_from_slice(&0u32.to_be_bytes());
75
76 let hash_algo = b"sha512";
77 blob.extend_from_slice(&(hash_algo.len() as u32).to_be_bytes());
78 blob.extend_from_slice(hash_algo);
79
80 let mut hasher = Sha512::new();
81 hasher.update(data);
82 let hash = hasher.finalize();
83 blob.extend_from_slice(&(hash.len() as u32).to_be_bytes());
84 blob.extend_from_slice(&hash);
85
86 Ok(blob)
87}
88
89pub fn construct_sshsig_pem(
106 pubkey: &[u8],
107 signature: &[u8],
108 namespace: &str,
109) -> Result<String, CryptoError> {
110 let mut blob = Vec::new();
111
112 blob.extend_from_slice(b"SSHSIG");
113
114 blob.extend_from_slice(&1u32.to_be_bytes());
116
117 let pubkey_blob = encode_ssh_pubkey(pubkey);
119 blob.extend_from_slice(&(pubkey_blob.len() as u32).to_be_bytes());
120 blob.extend_from_slice(&pubkey_blob);
121
122 blob.extend_from_slice(&(namespace.len() as u32).to_be_bytes());
124 blob.extend_from_slice(namespace.as_bytes());
125
126 blob.extend_from_slice(&0u32.to_be_bytes());
128
129 let hash_algo = b"sha512";
131 blob.extend_from_slice(&(hash_algo.len() as u32).to_be_bytes());
132 blob.extend_from_slice(hash_algo);
133
134 let sig_blob = encode_ssh_signature(signature);
136 blob.extend_from_slice(&(sig_blob.len() as u32).to_be_bytes());
137 blob.extend_from_slice(&sig_blob);
138
139 let b64 = base64::Engine::encode(&base64::engine::general_purpose::STANDARD, &blob);
140
141 let wrapped: String = b64
142 .chars()
143 .collect::<Vec<_>>()
144 .chunks(70)
145 .map(|c| c.iter().collect::<String>())
146 .collect::<Vec<_>>()
147 .join("\n");
148
149 Ok(format!(
150 "-----BEGIN SSH SIGNATURE-----\n{}\n-----END SSH SIGNATURE-----\n",
151 wrapped
152 ))
153}