1use crate::{Algorithm, AssociatedHashAlg, Error, HashAlg, Result, Signature, SigningKey, public};
4use alloc::{string::String, string::ToString, vec::Vec};
5use core::str::FromStr;
6use encoding::{
7 CheckedSum, Decode, DecodePem, Encode, EncodePem, Reader, Writer,
8 pem::{LineEnding, PemLabel},
9};
10use sha2::Digest;
11use signature::Verifier;
12
13#[cfg(doc)]
14use crate::{PrivateKey, PublicKey};
15
16type Version = u32;
17
18#[cfg(feature = "serde")]
19use serde::{Deserialize, Serialize, de, ser};
20
21#[derive(Clone, Debug, Eq, PartialEq)]
39pub struct SshSig {
40 version: Version,
41 public_key: public::KeyData,
42 namespace: String,
43 reserved: Vec<u8>,
44 hash_alg: HashAlg,
45 signature: Signature,
46}
47
48impl SshSig {
49 pub const VERSION: Version = 1;
51
52 const MAGIC_PREAMBLE: &'static [u8] = b"SSHSIG";
57
58 pub fn new(
63 public_key: public::KeyData,
64 namespace: impl Into<String>,
65 hash_alg: HashAlg,
66 signature: Signature,
67 ) -> Result<Self> {
68 let version = Self::VERSION;
69 let namespace = namespace.into();
70 let reserved = Vec::new();
71
72 if namespace.is_empty() {
73 return Err(Error::Namespace);
74 }
75
76 Ok(Self {
77 version,
78 public_key,
79 namespace,
80 reserved,
81 hash_alg,
82 signature,
83 })
84 }
85
86 pub fn from_pem(pem: impl AsRef<[u8]>) -> Result<Self> {
95 Self::decode_pem(pem)
96 }
97
98 pub fn to_pem(&self, line_ending: LineEnding) -> Result<String> {
107 Ok(self.encode_pem_string(line_ending)?)
108 }
109
110 pub fn sign<S: SigningKey>(
121 signing_key: &S,
122 namespace: &str,
123 hash_alg: HashAlg,
124 msg: &[u8],
125 ) -> Result<Self> {
126 Self::sign_prehash(
127 signing_key,
128 namespace,
129 hash_alg,
130 hash_alg.digest(msg).as_slice(),
131 )
132 }
133
134 pub fn sign_digest<S, D>(signing_key: &S, namespace: &str, digest: D) -> Result<Self>
143 where
144 S: SigningKey,
145 D: AssociatedHashAlg + Digest,
146 {
147 Self::sign_prehash(
148 signing_key,
149 namespace,
150 D::HASH_ALG,
151 digest.finalize().as_slice(),
152 )
153 }
154
155 pub fn sign_prehash<S: SigningKey>(
164 signing_key: &S,
165 namespace: &str,
166 hash_alg: HashAlg,
167 prehash: &[u8],
168 ) -> Result<Self> {
169 if namespace.is_empty() {
170 return Err(Error::Namespace);
171 }
172
173 if signing_key.public_key().is_sk_ed25519() {
174 return Err(Algorithm::SkEd25519.unsupported_error());
175 }
176
177 #[cfg(feature = "ecdsa")]
178 if signing_key.public_key().is_sk_ecdsa_p256() {
179 return Err(Algorithm::SkEcdsaSha2NistP256.unsupported_error());
180 }
181
182 let signed_data = Self::signed_data_for_prehash(namespace, hash_alg, prehash)?;
183 let signature = signing_key.try_sign(&signed_data)?;
184 Self::new(signing_key.public_key(), namespace, hash_alg, signature)
185 }
186
187 pub fn signed_data(namespace: &str, hash_alg: HashAlg, msg: &[u8]) -> Result<Vec<u8>> {
199 Self::signed_data_for_prehash(namespace, hash_alg, hash_alg.digest(msg).as_slice())
200 }
201
202 pub fn signed_data_for_prehash(
209 namespace: &str,
210 hash_alg: HashAlg,
211 prehash: &[u8],
212 ) -> Result<Vec<u8>> {
213 if prehash.len() != hash_alg.digest_size() {
214 return Err(Error::HashSize);
215 }
216
217 if namespace.is_empty() {
218 return Err(Error::Namespace);
219 }
220
221 SignedData {
222 namespace,
223 reserved: &[],
224 hash_alg,
225 hash: prehash,
226 }
227 .to_bytes()
228 }
229
230 pub(crate) fn verify_prehash(&self, prehash: &[u8]) -> Result<()> {
236 if prehash.len() != self.hash_alg.digest_size() {
237 return Err(Error::HashSize);
238 }
239
240 let signed_data = SignedData {
241 namespace: self.namespace.as_str(),
242 reserved: self.reserved.as_slice(),
243 hash_alg: self.hash_alg,
244 hash: prehash,
245 }
246 .to_bytes()?;
247
248 Ok(self.public_key.verify(&signed_data, &self.signature)?)
249 }
250
251 #[must_use]
253 pub fn algorithm(&self) -> Algorithm {
254 self.signature.algorithm()
255 }
256
257 #[must_use]
262 pub fn version(&self) -> Version {
263 self.version
264 }
265
266 #[must_use]
269 pub fn public_key(&self) -> &public::KeyData {
270 &self.public_key
271 }
272
273 #[must_use]
281 pub fn namespace(&self) -> &str {
282 &self.namespace
283 }
284
285 #[must_use]
291 pub fn reserved(&self) -> &[u8] {
292 &self.reserved
293 }
294
295 #[must_use]
303 pub fn hash_alg(&self) -> HashAlg {
304 self.hash_alg
305 }
306
307 #[must_use]
309 pub fn signature(&self) -> &Signature {
310 &self.signature
311 }
312
313 #[must_use]
315 pub fn signature_bytes(&self) -> &[u8] {
316 self.signature.as_bytes()
317 }
318}
319
320impl Decode for SshSig {
321 type Error = Error;
322
323 fn decode(reader: &mut impl Reader) -> Result<Self> {
324 let mut magic_preamble = [0u8; Self::MAGIC_PREAMBLE.len()];
325 reader.read(&mut magic_preamble)?;
326
327 if magic_preamble != Self::MAGIC_PREAMBLE {
328 return Err(Error::FormatEncoding);
329 }
330
331 let version = Version::decode(reader)?;
332
333 if version > Self::VERSION {
334 return Err(Error::Version { number: version });
335 }
336
337 let public_key = reader.read_prefixed(public::KeyData::decode)?;
338 let namespace = String::decode(reader)?;
339
340 if namespace.is_empty() {
341 return Err(Error::Namespace);
342 }
343
344 let reserved = Vec::decode(reader)?;
345 let hash_alg = HashAlg::decode(reader)?;
346 let signature = reader.read_prefixed(Signature::decode)?;
347
348 Ok(Self {
349 version,
350 public_key,
351 namespace,
352 reserved,
353 hash_alg,
354 signature,
355 })
356 }
357}
358
359impl Encode for SshSig {
360 fn encoded_len(&self) -> encoding::Result<usize> {
361 [
362 Self::MAGIC_PREAMBLE.len(),
363 self.version.encoded_len()?,
364 self.public_key.encoded_len_prefixed()?,
365 self.namespace.encoded_len()?,
366 self.reserved.encoded_len()?,
367 self.hash_alg.encoded_len()?,
368 self.signature.encoded_len_prefixed()?,
369 ]
370 .checked_sum()
371 }
372
373 fn encode(&self, writer: &mut impl Writer) -> encoding::Result<()> {
374 writer.write(Self::MAGIC_PREAMBLE)?;
375 self.version.encode(writer)?;
376 self.public_key.encode_prefixed(writer)?;
377 self.namespace.encode(writer)?;
378 self.reserved.encode(writer)?;
379 self.hash_alg.encode(writer)?;
380 self.signature.encode_prefixed(writer)?;
381 Ok(())
382 }
383}
384
385impl FromStr for SshSig {
386 type Err = Error;
387
388 fn from_str(s: &str) -> Result<Self> {
389 Self::from_pem(s)
390 }
391}
392
393impl PemLabel for SshSig {
394 const PEM_LABEL: &'static str = "SSH SIGNATURE";
395}
396
397#[allow(clippy::to_string_trait_impl)]
398impl ToString for SshSig {
399 fn to_string(&self) -> String {
400 self.to_pem(LineEnding::default())
401 .expect("SSH signature encoding error")
402 }
403}
404
405#[derive(Clone, Copy, Debug, Eq, PartialEq)]
407struct SignedData<'a> {
408 namespace: &'a str,
409 reserved: &'a [u8],
410 hash_alg: HashAlg,
411 hash: &'a [u8],
412}
413
414impl SignedData<'_> {
415 fn to_bytes(self) -> Result<Vec<u8>> {
416 let mut signed_bytes = Vec::with_capacity(self.encoded_len()?);
417 self.encode(&mut signed_bytes)?;
418 Ok(signed_bytes)
419 }
420}
421
422impl Encode for SignedData<'_> {
423 fn encoded_len(&self) -> encoding::Result<usize> {
424 [
425 SshSig::MAGIC_PREAMBLE.len(),
426 self.namespace.encoded_len()?,
427 self.reserved.encoded_len()?,
428 self.hash_alg.encoded_len()?,
429 self.hash.encoded_len()?,
430 ]
431 .checked_sum()
432 }
433
434 fn encode(&self, writer: &mut impl Writer) -> encoding::Result<()> {
435 writer.write(SshSig::MAGIC_PREAMBLE)?;
436 self.namespace.encode(writer)?;
437 self.reserved.encode(writer)?;
438 self.hash_alg.encode(writer)?;
439 self.hash.encode(writer)?;
440 Ok(())
441 }
442}
443
444#[cfg(feature = "serde")]
445impl<'de> Deserialize<'de> for SshSig {
446 fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
447 where
448 D: de::Deserializer<'de>,
449 {
450 if deserializer.is_human_readable() {
451 let string = String::deserialize(deserializer)?;
452 string.parse::<SshSig>().map_err(de::Error::custom)
453 } else {
454 let bytes = Vec::<u8>::deserialize(deserializer)?;
455 Self::decode(&mut bytes.as_slice()).map_err(de::Error::custom)
456 }
457 }
458}
459
460#[cfg(feature = "serde")]
461impl Serialize for SshSig {
462 fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
463 where
464 S: ser::Serializer,
465 {
466 if serializer.is_human_readable() {
467 self.to_pem(LineEnding::LF)
468 .map_err(ser::Error::custom)?
469 .serialize(serializer)
470 } else {
471 let bytes = self.encode_vec().map_err(ser::Error::custom)?;
472 bytes.serialize(serializer)
473 }
474 }
475}