1use crate::{public, Algorithm, Error, HashAlg, Result, Signature, SigningKey};
4use alloc::{string::String, string::ToString, vec::Vec};
5use core::str::FromStr;
6use encoding::{
7 pem::{LineEnding, PemLabel},
8 CheckedSum, Decode, DecodePem, Encode, EncodePem, Reader, Writer,
9};
10use signature::Verifier;
11
12#[cfg(doc)]
13use crate::{PrivateKey, PublicKey};
14
15type Version = u32;
16
17#[derive(Clone, Debug, Eq, PartialEq)]
35pub struct SshSig {
36 version: Version,
37 public_key: public::KeyData,
38 namespace: String,
39 reserved: Vec<u8>,
40 hash_alg: HashAlg,
41 signature: Signature,
42}
43
44impl SshSig {
45 pub const VERSION: Version = 1;
47
48 const MAGIC_PREAMBLE: &'static [u8] = b"SSHSIG";
53
54 pub fn new(
57 public_key: public::KeyData,
58 namespace: impl Into<String>,
59 hash_alg: HashAlg,
60 signature: Signature,
61 ) -> Result<Self> {
62 let version = Self::VERSION;
63 let namespace = namespace.into();
64 let reserved = Vec::new();
65
66 if namespace.is_empty() {
67 return Err(Error::Namespace);
68 }
69
70 Ok(Self {
71 version,
72 public_key,
73 namespace,
74 reserved,
75 hash_alg,
76 signature,
77 })
78 }
79
80 pub fn from_pem(pem: impl AsRef<[u8]>) -> Result<Self> {
86 Self::decode_pem(pem)
87 }
88
89 pub fn to_pem(&self, line_ending: LineEnding) -> Result<String> {
95 Ok(self.encode_pem_string(line_ending)?)
96 }
97
98 pub fn sign<S: SigningKey>(
102 signing_key: &S,
103 namespace: &str,
104 hash_alg: HashAlg,
105 msg: &[u8],
106 ) -> Result<Self> {
107 if namespace.is_empty() {
108 return Err(Error::Namespace);
109 }
110
111 if signing_key.public_key().is_sk_ed25519() {
112 return Err(Algorithm::SkEd25519.unsupported_error());
113 }
114
115 #[cfg(feature = "ecdsa")]
116 if signing_key.public_key().is_sk_ecdsa_p256() {
117 return Err(Algorithm::SkEcdsaSha2NistP256.unsupported_error());
118 }
119
120 let signed_data = Self::signed_data(namespace, hash_alg, msg)?;
121 let signature = signing_key.try_sign(&signed_data)?;
122 Self::new(signing_key.public_key(), namespace, hash_alg, signature)
123 }
124
125 pub fn signed_data(namespace: &str, hash_alg: HashAlg, msg: &[u8]) -> Result<Vec<u8>> {
135 if namespace.is_empty() {
136 return Err(Error::Namespace);
137 }
138
139 SignedData {
140 namespace,
141 reserved: &[],
142 hash_alg,
143 hash: hash_alg.digest(msg).as_slice(),
144 }
145 .to_bytes()
146 }
147
148 pub(crate) fn verify(&self, msg: &[u8]) -> Result<()> {
154 let signed_data = SignedData {
155 namespace: self.namespace.as_str(),
156 reserved: self.reserved.as_slice(),
157 hash_alg: self.hash_alg,
158 hash: self.hash_alg.digest(msg).as_slice(),
159 }
160 .to_bytes()?;
161
162 Ok(self.public_key.verify(&signed_data, &self.signature)?)
163 }
164
165 pub fn algorithm(&self) -> Algorithm {
167 self.signature.algorithm()
168 }
169
170 pub fn version(&self) -> Version {
175 self.version
176 }
177
178 pub fn public_key(&self) -> &public::KeyData {
181 &self.public_key
182 }
183
184 pub fn namespace(&self) -> &str {
192 &self.namespace
193 }
194
195 pub fn reserved(&self) -> &[u8] {
201 &self.reserved
202 }
203
204 pub fn hash_alg(&self) -> HashAlg {
212 self.hash_alg
213 }
214
215 pub fn signature(&self) -> &Signature {
217 &self.signature
218 }
219
220 pub fn signature_bytes(&self) -> &[u8] {
222 self.signature.as_bytes()
223 }
224}
225
226impl Decode for SshSig {
227 type Error = Error;
228
229 fn decode(reader: &mut impl Reader) -> Result<Self> {
230 let mut magic_preamble = [0u8; Self::MAGIC_PREAMBLE.len()];
231 reader.read(&mut magic_preamble)?;
232
233 if magic_preamble != Self::MAGIC_PREAMBLE {
234 return Err(Error::FormatEncoding);
235 }
236
237 let version = Version::decode(reader)?;
238
239 if version > Self::VERSION {
240 return Err(Error::Version { number: version });
241 }
242
243 let public_key = reader.read_prefixed(public::KeyData::decode)?;
244 let namespace = String::decode(reader)?;
245
246 if namespace.is_empty() {
247 return Err(Error::Namespace);
248 }
249
250 let reserved = Vec::decode(reader)?;
251 let hash_alg = HashAlg::decode(reader)?;
252 let signature = reader.read_prefixed(Signature::decode)?;
253
254 Ok(Self {
255 version,
256 public_key,
257 namespace,
258 reserved,
259 hash_alg,
260 signature,
261 })
262 }
263}
264
265impl Encode for SshSig {
266 fn encoded_len(&self) -> encoding::Result<usize> {
267 [
268 Self::MAGIC_PREAMBLE.len(),
269 self.version.encoded_len()?,
270 self.public_key.encoded_len_prefixed()?,
271 self.namespace.encoded_len()?,
272 self.reserved.encoded_len()?,
273 self.hash_alg.encoded_len()?,
274 self.signature.encoded_len_prefixed()?,
275 ]
276 .checked_sum()
277 }
278
279 fn encode(&self, writer: &mut impl Writer) -> encoding::Result<()> {
280 writer.write(Self::MAGIC_PREAMBLE)?;
281 self.version.encode(writer)?;
282 self.public_key.encode_prefixed(writer)?;
283 self.namespace.encode(writer)?;
284 self.reserved.encode(writer)?;
285 self.hash_alg.encode(writer)?;
286 self.signature.encode_prefixed(writer)?;
287 Ok(())
288 }
289}
290
291impl FromStr for SshSig {
292 type Err = Error;
293
294 fn from_str(s: &str) -> Result<Self> {
295 Self::from_pem(s)
296 }
297}
298
299impl PemLabel for SshSig {
300 const PEM_LABEL: &'static str = "SSH SIGNATURE";
301}
302
303impl ToString for SshSig {
304 fn to_string(&self) -> String {
305 self.to_pem(LineEnding::default())
306 .expect("SSH signature encoding error")
307 }
308}
309
310#[derive(Clone, Copy, Debug, Eq, PartialEq)]
312struct SignedData<'a> {
313 namespace: &'a str,
314 reserved: &'a [u8],
315 hash_alg: HashAlg,
316 hash: &'a [u8],
317}
318
319impl<'a> SignedData<'a> {
320 fn to_bytes(self) -> Result<Vec<u8>> {
321 let mut signed_bytes = Vec::with_capacity(self.encoded_len()?);
322 self.encode(&mut signed_bytes)?;
323 Ok(signed_bytes)
324 }
325}
326
327impl Encode for SignedData<'_> {
328 fn encoded_len(&self) -> encoding::Result<usize> {
329 [
330 SshSig::MAGIC_PREAMBLE.len(),
331 self.namespace.encoded_len()?,
332 self.reserved.encoded_len()?,
333 self.hash_alg.encoded_len()?,
334 self.hash.encoded_len()?,
335 ]
336 .checked_sum()
337 }
338
339 fn encode(&self, writer: &mut impl Writer) -> encoding::Result<()> {
340 writer.write(SshSig::MAGIC_PREAMBLE)?;
341 self.namespace.encode(writer)?;
342 self.reserved.encode(writer)?;
343 self.hash_alg.encode(writer)?;
344 self.hash.encode(writer)?;
345 Ok(())
346 }
347}