1use std::{fmt, io};
2
3use aes_gcm::aead::rand_core::CryptoRng;
4use byteorder::{LittleEndian, WriteBytesExt};
5use chrono::{SubsecRound, Utc};
6use log::debug;
7use nom::bytes::streaming::take;
8use nom::combinator::{map, map_parser, rest};
9use nom::number::streaming::{be_u8, le_u16};
10use rand::Rng;
11
12use crate::errors::{IResult, Result};
13use crate::packet::{
14 PacketTrait, Signature, SignatureConfig, SignatureType, Subpacket, SubpacketData,
15};
16use crate::ser::Serialize;
17use crate::types::{KeyVersion, PublicKeyTrait, SecretKeyTrait, SignedUserAttribute, Tag, Version};
18use crate::util::{packet_length, write_packet_length};
19
20#[derive(Clone, PartialEq, Eq, derive_more::Debug)]
23pub enum UserAttribute {
24 Image {
25 packet_version: Version,
26 #[debug("{}", hex::encode(header))]
27 header: Vec<u8>,
28 #[debug("{}", hex::encode(data))]
29 data: Vec<u8>,
30 },
31 Unknown {
32 packet_version: Version,
33 typ: u8,
34 #[debug("{}", hex::encode(data))]
35 data: Vec<u8>,
36 },
37}
38
39impl UserAttribute {
40 pub fn from_slice(packet_version: Version, input: &[u8]) -> Result<Self> {
42 let (_, pk) = parse(packet_version)(input)?;
43
44 Ok(pk)
45 }
46
47 pub fn to_u8(&self) -> u8 {
48 match *self {
49 UserAttribute::Image { .. } => 1,
50 UserAttribute::Unknown { typ, .. } => typ,
51 }
52 }
53
54 pub fn packet_len(&self) -> usize {
55 match self {
56 UserAttribute::Image { ref data, .. } => {
57 1 + 16 + data.len()
59 }
60 UserAttribute::Unknown { ref data, .. } => {
61 1 + data.len()
63 }
64 }
65 }
66
67 pub fn sign<R, F>(
69 &self,
70 rng: R,
71 key: &impl SecretKeyTrait,
72 key_pw: F,
73 ) -> Result<SignedUserAttribute>
74 where
75 R: CryptoRng + Rng,
76 F: FnOnce() -> String,
77 {
78 self.sign_third_party(rng, key, key_pw, key)
79 }
80
81 pub fn sign_third_party<R, F>(
83 &self,
84 mut rng: R,
85 signer: &impl SecretKeyTrait,
86 signer_pw: F,
87 signee: &impl PublicKeyTrait,
88 ) -> Result<SignedUserAttribute>
89 where
90 R: CryptoRng + Rng,
91 F: FnOnce() -> String,
92 {
93 let hashed_subpackets = vec![Subpacket::regular(SubpacketData::SignatureCreationTime(
94 Utc::now().trunc_subsecs(0),
95 ))];
96 let unhashed_subpackets = vec![Subpacket::regular(SubpacketData::Issuer(signer.key_id()))];
97
98 let mut config = match signer.version() {
99 KeyVersion::V4 => SignatureConfig::v4(
100 SignatureType::CertGeneric,
101 signer.algorithm(),
102 signer.hash_alg(),
103 ),
104
105 KeyVersion::V6 => SignatureConfig::v6(
106 &mut rng,
107 SignatureType::CertGeneric,
108 signer.algorithm(),
109 signer.hash_alg(),
110 )?,
111 v => unsupported_err!("unsupported key version: {:?}", v),
112 };
113
114 config.hashed_subpackets = hashed_subpackets;
115 config.unhashed_subpackets = unhashed_subpackets;
116
117 let sig =
118 config.sign_certification_third_party(signer, signer_pw, signee, self.tag(), &self)?;
119
120 Ok(SignedUserAttribute::new(self.clone(), vec![sig]))
121 }
122
123 pub fn into_signed(self, sig: Signature) -> SignedUserAttribute {
124 SignedUserAttribute::new(self, vec![sig])
125 }
126}
127
128impl fmt::Display for UserAttribute {
129 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130 match self {
131 UserAttribute::Image { data, .. } => {
132 write!(f, "User Attribute: Image (len: {})", data.len())
133 }
134 UserAttribute::Unknown { typ, data, .. } => {
135 write!(f, "User Attribute: typ: {} (len: {})", typ, data.len())
136 }
137 }
138 }
139}
140
141fn image(packet_version: Version) -> impl Fn(&[u8]) -> IResult<&[u8], UserAttribute> {
142 move |i: &[u8]| {
143 let (i, len) = le_u16(i)?;
145 if len < 2 {
146 return Err(nom::Err::Error(crate::errors::Error::InvalidInput));
147 }
148 let (img, header) = take(len - 2)(i)?;
149
150 Ok((
152 &[][..],
153 UserAttribute::Image {
154 packet_version,
155 header: header.to_vec(),
156 data: img.to_vec(),
157 },
158 ))
159 }
160}
161
162fn parse(packet_version: Version) -> impl Fn(&[u8]) -> IResult<&[u8], UserAttribute> {
163 move |i: &[u8]| {
164 let (i, len) = packet_length(i)?;
165 if len < 1 {
166 return Err(nom::Err::Error(crate::errors::Error::InvalidInput));
167 }
168 let (i, typ) = be_u8(i)?;
169 let (i, attr) = map_parser(take(len - 1), |i| match typ {
170 1 => image(packet_version)(i),
171 _ => map(rest, |data: &[u8]| UserAttribute::Unknown {
172 packet_version,
173 typ,
174 data: data.to_vec(),
175 })(i),
176 })(i)?;
177 Ok((i, {
178 debug!("attr with len {}", len);
179 attr
180 }))
181 }
182}
183
184impl Serialize for UserAttribute {
185 fn to_writer<W: io::Write>(&self, writer: &mut W) -> Result<()> {
186 debug!("write_packet_len {}", self.packet_len());
187 write_packet_length(self.packet_len(), writer)?;
188
189 match self {
190 UserAttribute::Image {
191 ref data,
192 ref header,
193 ..
194 } => {
195 writer.write_u8(0x01)?;
197 writer.write_u16::<LittleEndian>((header.len() + 2).try_into()?)?;
198 writer.write_all(header)?;
199
200 writer.write_all(data)?;
202 }
203 UserAttribute::Unknown { ref data, typ, .. } => {
204 writer.write_u8(*typ)?;
205 writer.write_all(data)?;
206 }
207 }
208 Ok(())
209 }
210}
211
212impl PacketTrait for UserAttribute {
213 fn packet_version(&self) -> Version {
214 match self {
215 UserAttribute::Image { packet_version, .. } => *packet_version,
216 UserAttribute::Unknown { packet_version, .. } => *packet_version,
217 }
218 }
219
220 fn tag(&self) -> Tag {
221 Tag::UserAttribute
222 }
223}