gpg_inspector_lib/packet/
secret_key.rs1use crate::error::Result;
8use crate::lookup::{lookup_s2k_type, lookup_symmetric_algorithm};
9use crate::packet::Field;
10use crate::packet::public_key::{PublicKeyPacket, parse_public_key};
11use crate::stream::ByteStream;
12
13#[derive(Debug, Clone)]
18pub struct SecretKeyPacket {
19 pub public_key: PublicKeyPacket,
21 pub s2k_usage: u8,
23 pub encryption_info: Option<EncryptionInfo>,
25 pub secret_key_data: Vec<u8>,
27}
28
29#[derive(Debug, Clone)]
34pub struct EncryptionInfo {
35 pub cipher_algo: u8,
37 pub s2k_type: u8,
39 pub s2k_hash: u8,
41 pub s2k_salt: Option<Vec<u8>>,
43 pub s2k_count: Option<u8>,
45 pub iv: Vec<u8>,
47}
48
49pub fn parse_secret_key(
54 stream: &mut ByteStream,
55 fields: &mut Vec<Field>,
56 offset: usize,
57) -> Result<SecretKeyPacket> {
58 let public_key = parse_public_key(stream, fields, offset)?;
59
60 let s2k_start = offset + stream.pos();
61 let s2k_usage = stream.octet()?;
62 let s2k_end = offset + stream.pos();
63
64 let s2k_desc = match s2k_usage {
65 0 => "Unencrypted".to_string(),
66 254 => "SHA-1 checksum, encrypted".to_string(),
67 255 => "Checksum, encrypted".to_string(),
68 n => format!("Symmetric algorithm {}", n),
69 };
70 fields.push(Field::field("S2K Usage", s2k_desc, (s2k_start, s2k_end)));
71
72 let encryption_info = if s2k_usage == 254 || s2k_usage == 255 {
73 let cipher_start = offset + stream.pos();
74 let cipher_algo = stream.octet()?;
75 let cipher_end = offset + stream.pos();
76 let cipher_info = lookup_symmetric_algorithm(cipher_algo);
77 fields.push(Field::field(
78 "Cipher",
79 cipher_info.display(),
80 (cipher_start, cipher_end),
81 ));
82
83 let s2k_type_start = offset + stream.pos();
84 let s2k_type = stream.octet()?;
85 let s2k_type_end = offset + stream.pos();
86 fields.push(Field::field(
87 "S2K Type",
88 lookup_s2k_type(s2k_type).to_string(),
89 (s2k_type_start, s2k_type_end),
90 ));
91
92 let _s2k_hash_start = offset + stream.pos();
93 let s2k_hash = stream.octet()?;
94 let _s2k_hash_end = offset + stream.pos();
95
96 let (s2k_salt, s2k_count) = if s2k_type == 1 || s2k_type == 3 {
97 let salt_start = offset + stream.pos();
98 let salt = stream.bytes(8)?;
99 let salt_end = offset + stream.pos();
100 fields.push(Field::field("S2K Salt", "8 bytes", (salt_start, salt_end)));
101
102 let count = if s2k_type == 3 {
103 let count_start = offset + stream.pos();
104 let c = stream.octet()?;
105 let count_end = offset + stream.pos();
106 let iterations = (16u32 + (c as u32 & 15)) << ((c >> 4) + 6);
107 fields.push(Field::field(
108 "S2K Iterations",
109 iterations.to_string(),
110 (count_start, count_end),
111 ));
112 Some(c)
113 } else {
114 None
115 };
116
117 (Some(salt), count)
118 } else {
119 (None, None)
120 };
121
122 let iv_size = match cipher_algo {
123 7..=9 => 16,
124 _ => 8,
125 };
126 let iv_start = offset + stream.pos();
127 let iv = stream.bytes(iv_size)?;
128 let iv_end = offset + stream.pos();
129 fields.push(Field::field(
130 "IV",
131 format!("{} bytes", iv.len()),
132 (iv_start, iv_end),
133 ));
134
135 Some(EncryptionInfo {
136 cipher_algo,
137 s2k_type,
138 s2k_hash,
139 s2k_salt,
140 s2k_count,
141 iv,
142 })
143 } else if s2k_usage != 0 {
144 let iv_size = match s2k_usage {
145 7..=9 => 16,
146 _ => 8,
147 };
148 let _iv_start = offset + stream.pos();
149 let iv = stream.bytes(iv_size)?;
150 let _iv_end = offset + stream.pos();
151
152 Some(EncryptionInfo {
153 cipher_algo: s2k_usage,
154 s2k_type: 0,
155 s2k_hash: 0,
156 s2k_salt: None,
157 s2k_count: None,
158 iv,
159 })
160 } else {
161 None
162 };
163
164 let data_start = offset + stream.pos();
165 let secret_key_data = stream.rest();
166 if !secret_key_data.is_empty() {
167 let data_end = offset + stream.pos();
168 fields.push(Field::field(
169 "Secret Key Data",
170 format!("{} bytes (encrypted)", secret_key_data.len()),
171 (data_start, data_end),
172 ));
173 }
174
175 Ok(SecretKeyPacket {
176 public_key,
177 s2k_usage,
178 encryption_info,
179 secret_key_data,
180 })
181}