1use std::{fmt, io, str};
2
3use aes_gcm::aead::rand_core::CryptoRng;
4use bstr::{BStr, BString};
5use chrono::{SubsecRound, Utc};
6use rand::Rng;
7
8use crate::errors::Result;
9use crate::packet::{
10 PacketTrait, Signature, SignatureConfig, SignatureType, Subpacket, SubpacketData,
11};
12use crate::ser::Serialize;
13use crate::types::{KeyVersion, PublicKeyTrait, SecretKeyTrait, SignedUser, Tag, Version};
14
15#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct UserId {
19 packet_version: Version,
20 id: BString,
21}
22
23impl UserId {
24 pub fn from_slice(packet_version: Version, input: &[u8]) -> Result<Self> {
26 Ok(UserId {
27 packet_version,
28 id: BString::from(input),
29 })
30 }
31
32 pub fn from_str(packet_version: Version, input: &str) -> Self {
33 UserId {
34 packet_version,
35 id: BString::from(input),
36 }
37 }
38
39 pub fn id(&self) -> &BStr {
40 self.id.as_ref()
41 }
42
43 pub fn sign<R, F>(&self, rng: R, key: &impl SecretKeyTrait, key_pw: F) -> Result<SignedUser>
45 where
46 R: CryptoRng + Rng,
47 F: FnOnce() -> String,
48 {
49 self.sign_third_party(rng, key, key_pw, key)
50 }
51
52 pub fn sign_third_party<R, F>(
54 &self,
55 mut rng: R,
56 signer: &impl SecretKeyTrait,
57 signer_pw: F,
58 signee: &impl PublicKeyTrait,
59 ) -> Result<SignedUser>
60 where
61 R: CryptoRng + Rng,
62 F: FnOnce() -> String,
63 {
64 let hashed_subpackets = vec![Subpacket::regular(SubpacketData::SignatureCreationTime(
65 Utc::now().trunc_subsecs(0),
66 ))];
67 let unhashed_subpackets = vec![Subpacket::regular(SubpacketData::Issuer(signer.key_id()))];
68
69 let mut config = match signer.version() {
70 KeyVersion::V4 => SignatureConfig::v4(
71 SignatureType::CertGeneric,
72 signer.algorithm(),
73 signer.hash_alg(),
74 ),
75 KeyVersion::V6 => SignatureConfig::v6(
76 &mut rng,
77 SignatureType::CertGeneric,
78 signer.algorithm(),
79 signer.hash_alg(),
80 )?,
81 v => unsupported_err!("unsupported key version: {:?}", v),
82 };
83
84 config.hashed_subpackets = hashed_subpackets;
85 config.unhashed_subpackets = unhashed_subpackets;
86
87 let sig =
88 config.sign_certification_third_party(signer, signer_pw, signee, self.tag(), &self)?;
89
90 Ok(SignedUser::new(self.clone(), vec![sig]))
91 }
92
93 pub fn into_signed(self, sig: Signature) -> SignedUser {
94 SignedUser::new(self, vec![sig])
95 }
96}
97
98impl Serialize for UserId {
99 fn to_writer<W: io::Write>(&self, writer: &mut W) -> Result<()> {
100 writer.write_all(&self.id)?;
101
102 Ok(())
103 }
104}
105
106impl fmt::Display for UserId {
107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108 write!(f, "User ID: \"{}\"", self.id)
109 }
110}
111
112impl PacketTrait for UserId {
113 fn packet_version(&self) -> Version {
114 self.packet_version
115 }
116
117 fn tag(&self) -> Tag {
118 Tag::UserId
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 #![allow(clippy::unwrap_used)]
125
126 use rand::SeedableRng;
127 use rand_chacha::ChaCha8Rng;
128
129 use super::*;
130 use crate::types::KeyVersion;
131 use crate::{packet, KeyType};
132
133 #[test]
134 fn test_user_id_certification() {
135 let key_type = KeyType::EdDSALegacy;
136 let mut rng = ChaCha8Rng::seed_from_u64(0);
137
138 let (public_params, secret_params) = key_type.generate(&mut rng).unwrap();
139
140 let alice_sec = packet::SecretKey::new(
141 packet::PublicKey::new(
142 Version::New,
143 KeyVersion::V4,
144 key_type.to_alg(),
145 Utc::now().trunc_subsecs(0),
146 None,
147 public_params,
148 )
149 .unwrap(),
150 secret_params,
151 );
152
153 let alice_pub = alice_sec.public_key();
154
155 let alice_uid = UserId::from_str(Version::New, "<alice@example.org>");
156
157 let self_signed = alice_uid
159 .sign(&mut rng, &alice_sec, String::default)
160 .unwrap();
161 self_signed
162 .verify(&alice_pub)
163 .expect("self signature verification failed");
164
165 let (public_params, secret_params) = key_type.generate(&mut rng).unwrap();
167
168 let signer_sec = packet::SecretKey::new(
169 packet::PublicKey::new(
170 Version::New,
171 KeyVersion::V4,
172 key_type.to_alg(),
173 Utc::now().trunc_subsecs(0),
174 None,
175 public_params,
176 )
177 .unwrap(),
178 secret_params,
179 );
180
181 let signer_pub = signer_sec.public_key();
182
183 let third_signed = alice_uid
184 .sign_third_party(&mut rng, &signer_sec, String::default, &alice_pub)
185 .unwrap();
186 third_signed
187 .verify_third_party(&alice_pub, &signer_pub)
188 .expect("self signature verification failed");
189 }
190}