ssh_agent_lib/proto/message/add_remove/
credential.rs

1//! A container for a public / private key pair, or a certificate / private key.
2
3use core::str::FromStr;
4
5use ssh_encoding::{self, CheckedSum, Decode, Encode, Reader, Writer};
6use ssh_key::{certificate::Certificate, private::KeypairData, Algorithm};
7
8use crate::proto::{Error, PrivateKeyData, Result};
9
10/// A container for a public / private key pair, or a certificate / private key.
11///
12/// When adding an identity to an agent, a user can provide either:
13/// 1. A public / private key pair
14/// 2. An OpenSSH [certificate](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.certkeys)
15///
16/// This structure covers both types of identities a user may
17/// send to an agent as part of a [`Request::AddIdentity`](crate::proto::Request::AddIdentity) message.
18#[derive(Clone, PartialEq, Debug)]
19pub enum Credential {
20    /// A public/private key pair
21    Key {
22        /// Public/private key pair data
23        privkey: KeypairData,
24
25        /// Key comment, if any.
26        comment: String,
27    },
28
29    /// An OpenSSH [certificate](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.certkeys)
30    Cert {
31        /// Certificate algorithm.
32        algorithm: Algorithm,
33
34        /// Certificate data.
35        certificate: Certificate,
36
37        /// Private key data.
38        privkey: PrivateKeyData,
39
40        /// Comment, if any.
41        comment: String,
42    },
43}
44
45impl Decode for Credential {
46    type Error = Error;
47
48    fn decode(reader: &mut impl Reader) -> Result<Self> {
49        let alg = String::decode(reader)?;
50        let cert_alg = Algorithm::new_certificate(&alg);
51
52        if let Ok(algorithm) = cert_alg {
53            let certificate = reader.read_prefixed(|reader| {
54                let cert = Certificate::decode(reader)?;
55                Ok::<_, Error>(cert)
56            })?;
57            let privkey = PrivateKeyData::decode_as(reader, algorithm.clone())?;
58            let comment = String::decode(reader)?;
59
60            Ok(Credential::Cert {
61                algorithm,
62                certificate,
63                privkey,
64                comment,
65            })
66        } else {
67            let algorithm = Algorithm::from_str(&alg).map_err(ssh_encoding::Error::from)?;
68            let privkey = KeypairData::decode_as(reader, algorithm)?;
69            let comment = String::decode(reader)?;
70            Ok(Credential::Key { privkey, comment })
71        }
72    }
73}
74
75impl Encode for Credential {
76    fn encoded_len(&self) -> ssh_encoding::Result<usize> {
77        match self {
78            Self::Key { privkey, comment } => {
79                [privkey.encoded_len()?, comment.encoded_len()?].checked_sum()
80            }
81            Self::Cert {
82                algorithm,
83                certificate,
84                privkey,
85                comment,
86            } => [
87                algorithm.to_certificate_type().encoded_len()?,
88                certificate.encoded_len_prefixed()?,
89                privkey.encoded_len()?,
90                comment.encoded_len()?,
91            ]
92            .checked_sum(),
93        }
94    }
95
96    fn encode(&self, writer: &mut impl Writer) -> ssh_encoding::Result<()> {
97        match self {
98            Self::Key { privkey, comment } => {
99                privkey.encode(writer)?;
100                comment.encode(writer)
101            }
102            Self::Cert {
103                algorithm,
104                certificate,
105                privkey,
106                comment,
107            } => {
108                algorithm.to_certificate_type().encode(writer)?;
109                certificate.encode_prefixed(writer)?;
110                privkey.encode(writer)?;
111                comment.encode(writer)
112            }
113        }
114    }
115}