1use std::fmt;
2use std::io::Write;
3
4use sequoia_openpgp as openpgp;
5use openpgp::Error;
6use openpgp::Result;
7use openpgp::crypto::mpi::{MPI, PublicKey};
8use openpgp::types::{Curve, HashAlgorithm};
9
10#[derive(Clone, PartialEq, Eq, Hash)]
26pub struct Keygrip([u8; 20]);
27
28impl fmt::Debug for Keygrip {
29 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
30 for b in self.0.iter() {
31 write!(f, "{:02X}", *b)?;
32 }
33 Ok(())
34 }
35}
36
37impl fmt::Display for Keygrip {
38 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
39 for b in self.0.iter() {
40 write!(f, "{:02X}", *b)?;
41 }
42 Ok(())
43 }
44}
45
46impl std::str::FromStr for Keygrip {
47 type Err = anyhow::Error;
48
49 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
50 let bytes = openpgp::fmt::hex::decode_pretty(s)?;
51 if bytes.len() == 20 {
52 let mut digest = [0; 20];
53 digest[..].copy_from_slice(&bytes[..]);
54 Ok(Keygrip(digest))
55 } else {
56 Err(Error::InvalidArgument(
57 format!("Expected 20 bytes, got {}", bytes.len())).into())
58 }
59 }
60}
61
62impl Keygrip {
63 pub fn of(key: &PublicKey) -> Result<Keygrip> {
92 use openpgp::crypto::hash;
93 use self::PublicKey::*;
94 let mut hash =
95 HashAlgorithm::SHA1.context().unwrap().for_digest();
96
97 fn hash_sexp_mpi(hash: &mut hash::Context, kind: char, mpi: &MPI)
98 {
99 let prefix = if mpi.value().get(0).map(|msb| msb & 0x80 > 0)
107 .unwrap_or(false)
108 {
109 &b"\x00"[..]
113 } else {
114 &b""[..]
116 };
117 hash_sexp(hash, kind, prefix, mpi.value());
118 }
119
120 fn hash_sexp(hash: &mut hash::Context, kind: char, prefix: &[u8],
121 buf: &[u8])
122 {
123 write!(hash, "(1:{}{}:",
124 kind, buf.len() + prefix.len()).unwrap();
125 hash.update(prefix);
126 hash.update(buf);
127 write!(hash, ")").unwrap();
128 }
129
130 fn hash_ecc(hash: &mut hash::Context, curve: &Curve, q: &MPI)
131 -> Result<()>
132 {
133 for (i, name) in "pabgnhq".chars().enumerate() {
134 if i == 5 {
135 continue; }
137
138 let param;
139 let mut m = if i == 6 {
140 q.value()
141 } else {
142 param = ecc_param(curve, i)?;
143 param.value()
144 };
145
146 if m[0] == 0x40 {
148 m = &m[1..];
150 }
151
152 hash_sexp(hash, name, &[], m);
153 }
154
155 Ok(())
156 }
157
158 match key {
159 &RSA { ref n, .. } => {
167 hash.update(&[0]);
171 hash.update(n.value());
172 },
173
174 &DSA { ref p, ref q, ref g, ref y } => {
175 hash_sexp_mpi(&mut hash, 'p', p);
176 hash_sexp_mpi(&mut hash, 'q', q);
177 hash_sexp_mpi(&mut hash, 'g', g);
178 hash_sexp_mpi(&mut hash, 'y', y);
179 },
180
181 &ElGamal { ref p, ref g, ref y } => {
182 hash_sexp_mpi(&mut hash, 'p', p);
183 hash_sexp_mpi(&mut hash, 'g', g);
184 hash_sexp_mpi(&mut hash, 'y', y);
185 },
186
187 &EdDSA { ref curve, ref q } => hash_ecc(&mut hash, curve, q)?,
188 &ECDSA { ref curve, ref q } => hash_ecc(&mut hash, curve, q)?,
189 &ECDH { ref curve, ref q, .. } => hash_ecc(&mut hash, curve, q)?,
190
191 &Unknown { .. } | &_ =>
194 return Err(Error::InvalidOperation(
195 "Keygrip not defined for this kind of public key".into())
196 .into()),
197 }
198
199 let mut digest = [0; 20];
200 let _ = hash.digest(&mut digest);
201 Ok(Keygrip(digest))
202 }
203}
204
205fn ecc_param(curve: &Curve, i: usize) -> Result<MPI> {
209 use self::Curve::*;
210 assert!(i < 6);
211 let hex = match (curve, i) {
212 (NistP256, 0) => "0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff",
213 (NistP256, 1) => "0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc",
214 (NistP256, 2) => "0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b",
215 (NistP256, 4) => "0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551",
216 (NistP256, 3) => "0x04\
217 6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296\
218 4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5",
219 (NistP256, 5) => "0x01",
220
221 (NistP384, 0) => "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff",
222 (NistP384, 1) => "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffc",
223 (NistP384, 2) => "0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef",
224 (NistP384, 4) => "0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973",
225 (NistP384, 3) => "0x04\
226 aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7\
227 3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f",
228 (NistP384, 5) => "0x01",
229
230 (NistP521, 0) => "0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
231 (NistP521, 1) => "0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc",
232 (NistP521, 2) => "0x51953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00",
233 (NistP521, 4) => "0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409",
234 (NistP521, 3) => "0x04\
235 00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66\
236 011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650",
237 (NistP521, 5) => "0x01",
238
239 (BrainpoolP256, 0) => "0xa9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e5377",
240 (BrainpoolP256, 1) => "0x7d5a0975fc2c3057eef67530417affe7fb8055c126dc5c6ce94a4b44f330b5d9",
241 (BrainpoolP256, 2) => "0x26dc5c6ce94a4b44f330b5d9bbd77cbf958416295cf7e1ce6bccdc18ff8c07b6",
242 (BrainpoolP256, 4) => "0xa9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974856a7",
243 (BrainpoolP256, 3) => "0x04\
244 8bd2aeb9cb7e57cb2c4b482ffc81b7afb9de27e1e3bd23c23a4453bd9ace3262\
245 547ef835c3dac4fd97f8461a14611dc9c27745132ded8e545c1d54c72f046997",
246 (BrainpoolP256, 5) => "0x01",
247
248 (BrainpoolP512, 0) => "0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca703308717d4d9b009bc66842aecda12ae6a380e62881ff2f2d82c68528aa6056583a48f3",
249 (BrainpoolP512, 1) => "0x7830a3318b603b89e2327145ac234cc594cbdd8d3df91610a83441caea9863bc2ded5d5aa8253aa10a2ef1c98b9ac8b57f1117a72bf2c7b9e7c1ac4d77fc94ca",
250 (BrainpoolP512, 2) => "0x3df91610a83441caea9863bc2ded5d5aa8253aa10a2ef1c98b9ac8b57f1117a72bf2c7b9e7c1ac4d77fc94cadc083e67984050b75ebae5dd2809bd638016f723",
251 (BrainpoolP512, 4) => "0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca90069",
252 (BrainpoolP512, 3) => "0x04\
253 81aee4bdd82ed9645a21322e9c4c6a9385ed9f70b5d916c1b43b62eef4d0098eff3b1f78e2d0d48d50d1687b93b97d5f7c6d5047406a5e688b352209bcb9f822\
254 7dde385d566332ecc0eabfa9cf7822fdf209f70024a57b1aa000c55b881f8111b2dcde494a5f485e5bca4bd88a2763aed1ca2b2fa8f0540678cd1e0f3ad80892",
255 (BrainpoolP512, 5) => "0x01",
256
257 (Ed25519, 0) => "0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED",
258 (Ed25519, 1) => "0x01",
259 (Ed25519, 2) => "0x2DFC9311D490018C7338BF8688861767FF8FF5B2BEBE27548A14B235ECA6874A",
260 (Ed25519, 4) => "0x1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED",
261 (Ed25519, 3) => "0x04\
262 216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A\
263 6666666666666666666666666666666666666666666666666666666666666658",
264 (Ed25519, 5) => "0x08",
265
266 (Cv25519, 0) => "0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED",
267 (Cv25519, 1) => "0x01DB41",
268 (Cv25519, 2) => "0x01",
269 (Cv25519, 4) => "0x1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED",
270 (Cv25519, 3) => "0x04\
271 0000000000000000000000000000000000000000000000000000000000000009\
272 20AE19A1B8A086B4E01EDD2C7748D14C923D4D7E6D7C61B229E9C5A27ECED3D9",
273 (Cv25519, 5) => "0x08",
274
275 (_, _) => {
276 let error = || {
277 Err(Error::InvalidArgument(
278 format!("Invalid or unknown curve parameters: \
279 curve: {}, i: {}",
280 curve, i)).into())
281 };
282
283 const BRAINPOOL_P384_OID: &[u8] =
284 &[0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B];
285
286 if curve.oid() == BRAINPOOL_P384_OID {
287 match i {
288 0 => "0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b412b1da197fb71123acd3a729901d1a71874700133107ec53",
289 1 => "0x7bc382c63d8c150c3c72080ace05afa0c2bea28e4fb22787139165efba91f90f8aa5814a503ad4eb04a8c7dd22ce2826",
290 2 => "0x04a8c7dd22ce28268b39b55416f0447c2fb77de107dcd2a62e880ea53eeb62d57cb4390295dbc9943ab78696fa504c11",
291 4 => "0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202e9046565",
292 3 => "0x04\
293 1d1c64f068cf45ffa2a63a81b7c13f6b8847a3e77ef14fe3db7fcafe0cbd10e8e826e03436d646aaef87b2e247d4af1e\
294 8abe1d7520f9c2a45cb1eb8e95cfd55262b70b29feec5864e19c054ff99129280e4646217791811142820341263c5315",
295 5 => "0x01",
296 _ => return error(),
297 }
298 } else {
299 return error();
300 }
301 }
302 };
303
304 Ok(openpgp::fmt::hex::decode_pretty(hex).unwrap().into())
305}
306
307#[cfg(test)]
308mod tests {
309 use super::*;
310 use openpgp::fmt::hex;
311
312 #[test]
314 fn libgcrypt_basic() {
315 let tests = vec![
316 (PublicKey::RSA {
317 n: hex::decode(
318 "00e0ce96f90b6c9e02f3922beada93fe50a875eac6bcc18bb9a9cf2e84965caa\
319 2d1ff95a7f542465c6c0c19d276e4526ce048868a7a914fd343cc3a87dd74291\
320 ffc565506d5bbb25cbac6a0e2dd1f8bcaab0d4a29c2f37c950f363484bf269f7\
321 891440464baf79827e03a36e70b814938eebdc63e964247be75dc58b014b7ea2\
322 51").unwrap().into(),
323 e: hex::decode("010001").unwrap().into(),
324 }, Keygrip(*b"\x32\x10\x0c\x27\x17\x3e\xf6\xe9\xc4\xe9\
325 \xa2\x5d\x3d\x69\xf8\x6d\x37\xa4\xf9\x39")),
326 (PublicKey::DSA {
327 p: hex::decode(
328 "00AD7C0025BA1A15F775F3F2D673718391D00456978D347B33D7B49E7F32EDAB\
329 96273899DD8B2BB46CD6ECA263FAF04A28903503D59062A8865D2AE8ADFB5191\
330 CF36FFB562D0E2F5809801A1F675DAE59698A9E01EFE8D7DCFCA084F4C6F5A44\
331 44D499A06FFAEA5E8EF5E01F2FD20A7B7EF3F6968AFBA1FB8D91F1559D52D877\
332 7B").unwrap().into(),
333 q: hex::decode(
334 "00EB7B5751D25EBBB7BD59D920315FD840E19AEBF9").unwrap().into(),
335 g: hex::decode(
336 "1574363387FDFD1DDF38F4FBE135BB20C7EE4772FB94C337AF86EA8E49666503\
337 AE04B6BE81A2F8DD095311E0217ACA698A11E6C5D33CCDAE71498ED35D13991E\
338 B02F09AB40BD8F4C5ED8C75DA779D0AE104BC34C960B002377068AB4B5A1F984\
339 3FBA91F537F1B7CAC4D8DD6D89B0D863AF7025D549F9C765D2FC07EE208F8D15\
340 ").unwrap().into(),
341 y: hex::decode(
342 "64B11EF8871BE4AB572AA810D5D3CA11A6CDBC637A8014602C72960DB135BF46\
343 A1816A724C34F87330FC9E187C5D66897A04535CC2AC9164A7150ABFA8179827\
344 6E45831AB811EEE848EBB24D9F5F2883B6E5DDC4C659DEF944DCFD80BF4D0A20\
345 42CAA7DC289F0C5A9D155F02D3D551DB741A81695B74D4C8F477F9C7838EB0FB\
346 ").unwrap().into(),
347 }, Keygrip(*b"\xc6\x39\x83\x1a\x43\xe5\x05\x5d\xc6\xd8\
348 \x4a\xa6\xf9\xeb\x23\xbf\xa9\x12\x2d\x5b")),
349 (PublicKey::ElGamal {
350 p: hex::decode(
351 "00B93B93386375F06C2D38560F3B9C6D6D7B7506B20C1773F73F8DE56E6CD65D\
352 F48DFAAA1E93F57A2789B168362A0F787320499F0B2461D3A4268757A7B27517\
353 B7D203654A0CD484DEC6AF60C85FEB84AAC382EAF2047061FE5DAB81A20A0797\
354 6E87359889BAE3B3600ED718BE61D4FC993CC8098A703DD0DC942E965E8F18D2\
355 A7").unwrap().into(),
356 g: hex::decode("05").unwrap().into(),
357 y: hex::decode(
358 "72DAB3E83C9F7DD9A931FDECDC6522C0D36A6F0A0FEC955C5AC3C09175BBFF2B\
359 E588DB593DC2E420201BEB3AC17536918417C497AC0F8657855380C1FCF11C5B\
360 D20DB4BEE9BDF916648DE6D6E419FA446C513AAB81C30CB7B34D6007637BE675\
361 56CE6473E9F9EE9B9FADD275D001563336F2186F424DEC6199A0F758F6A00FF4\
362 ").unwrap().into(),
363 }, Keygrip(*b"\xa7\x99\x61\xeb\x88\x83\xd2\xf4\x05\xc8\
364 \x4f\xba\x06\xf8\x78\x09\xbc\x1e\x20\xe5")),
365 ];
366
367 for (key, keygrip) in tests {
368 assert_eq!(Keygrip::of(&key).unwrap(), keygrip);
369 }
370 }
371
372 #[test]
374 fn our_keys() {
375 use std::collections::HashMap;
376 use openpgp::Fingerprint as FP;
377 use super::Keygrip as KG;
378 use openpgp::parse::Parse;
379
380 let keygrips: HashMap<FP, KG> = [
381 ("3E8877C877274692975189F5D03F6F865226FE8B".parse::<FP>().unwrap(),
383 "71ADDE3BBC0B7F1BFC2DA414C4F473B197763733".parse::<KG>().unwrap()),
384 ("01F187575BD45644046564C149E2118166C92632".parse::<FP>().unwrap(),
385 "CB6149C50DF90DC88626283A6B6C918A1C29E37D".parse::<KG>().unwrap()),
386 ("8F17777118A33DDA9BA48E62AACB3243630052D9".parse::<FP>().unwrap(),
388 "C45986381F54F967C2F6B104521C8634090F326A".parse::<KG>().unwrap()),
389 ("C03FA6411B03AE12576461187223B56678E02528".parse::<FP>().unwrap(),
390 "BE2FE8C8793141322AC30E3EAFD1E4F9D8DACCC4".parse::<KG>().unwrap()),
391 ("50E6D924308DBF223CFB510AC2B819056C652598".parse::<FP>().unwrap(),
392 "9873FD355DE470DDC151CD9919AC9785C3C2FDDE".parse::<KG>().unwrap()),
393 ("2DC50AB55BE2F3B04C2D2CF8A3506AFB820ABD08".parse::<FP>().unwrap(),
394 "9483454871CC1239D4C2A1416F2742D39A14DB14".parse::<KG>().unwrap()),
395 ("5BFBCD2A23E6866B77198C1147606B18E3D45CE9".parse::<FP>().unwrap(),
397 "D3E87BECEF18FB4C561F3C4E73A92C4D7A43FD90".parse::<KG>().unwrap()),
398 ("39D100AB67D5BD8C04010205FB3751F1587DAEF1".parse::<FP>().unwrap(),
400 "DD143ABA8D1D7D09875D6209E01BCF020788FF77".parse::<KG>().unwrap()),
401 ("F4D1450B041F622FCEFBFDB18BD88E94C0D20333".parse::<FP>().unwrap(),
402 "583225FBC0A88293472FB95F37E9595E1367188C".parse::<KG>().unwrap()),
403 ("8E8C33FA4626337976D97978069C0C348DD82C19".parse::<FP>().unwrap(),
405 "8BFFDC31BCFC3F31304DACD55AC5F15839A64040".parse::<KG>().unwrap()),
406 ("061C3CA44AFF0EC58DC66E9522E3FAFE96B56C32".parse::<FP>().unwrap(),
407 "E80BBB4AC2048A708ADB376C6491E8302150DCC9".parse::<KG>().unwrap()),
408 ("B45FB2CD7B227C057D6BD690DA6846EEA212A3C0".parse::<FP>().unwrap(),
410 "CA791A9F0F2EF0163461BA991BFEB2315EDF13F5".parse::<KG>().unwrap()),
411 ("E837639193664C9BB1C212E70CB719D5AA7D91F1".parse::<FP>().unwrap(),
413 "625CC3D9A795AD7AC6B666E92E46156917773CBC".parse::<KG>().unwrap()),
414 ("B9E41C493B8988A7EDC502D99A404C898D411DC8".parse::<FP>().unwrap(),
416 "8F669049015534649776D0F1F439D37EE3F3D948".parse::<KG>().unwrap()),
417 ("597B1FEA9F1B91F6749E8A24652CC528EBDA1B20".parse::<FP>().unwrap(),
419 "EF0CCDE02FFF9E24EFCCBF6F6FFE52716820E497".parse::<KG>().unwrap()),
420 ("7147EB2C548AEF87E425B9543EF9867F7073B689".parse::<FP>().unwrap(),
421 "642314FF90E6F8DA595EF51B7BA6B25071D3B0F1".parse::<KG>().unwrap()),
422 ].iter().cloned().collect();
423
424 for (name, cert) in [
425 "testy.pgp",
426 "neal.pgp",
427 "dennis-simon-anton.pgp",
428 "testy-new.pgp",
429 "emmelie-dorothea-dina-samantha-awina-ed25519.pgp",
430 "erika-corinna-daniela-simone-antonia-nistp256.pgp",
431 "erika-corinna-daniela-simone-antonia-nistp384.pgp",
432 "erika-corinna-daniela-simone-antonia-nistp521.pgp",
433 "keygrip-issue-439.pgp",
434 ]
435 .iter().map(|n| (n, openpgp::Cert::from_bytes(crate::tests::key(n)).unwrap()))
436 {
437 eprintln!("{}", name);
438 for key in cert.keys().map(|a| a.key()) {
439 let fp = key.fingerprint();
440 eprintln!("(sub)key: {}", fp);
441 assert_eq!(&Keygrip::of(key.mpis()).unwrap(),
442 keygrips.get(&fp).unwrap());
443 }
444 }
445 }
446
447 #[test]
449 fn gpgme_keys() {
450 use std::collections::HashMap;
451 use openpgp::Fingerprint as FP;
452 use super::Keygrip as KG;
453 use openpgp::parse::Parse;
454
455 let keygrips: HashMap<FP, KG> = [
456 ("A0FF4590BB6122EDEF6E3C542D727CC768697734".parse::<FP>().unwrap(),
458 "76F7E2B35832976B50A27A282D9B87E44577EB66".parse::<KG>().unwrap()),
459 ("3B3FBC948FE59301ED629EFB6AE6D7EE46A871F8".parse::<FP>().unwrap(),
460 "A0747D5F9425E6664F4FFBEED20FBCA79FDED2BD".parse::<KG>().unwrap()),
461 ("23FD347A419429BACCD5E72D6BC4778054ACD246".parse::<FP>().unwrap(),
463 "13CBE3758AFE42B5E5E2AE4CED27AFA455E3F87F".parse::<KG>().unwrap()),
464 ("2DCA5A1392DE06ED4FCB8C53EF9DC276A172C881".parse::<FP>().unwrap(),
465 "7A030357C0F253A5BBCD282FFC4E521B37558F5C".parse::<KG>().unwrap()),
466 ].iter().cloned().collect();
467
468 for (name, cert) in [
469 "alpha.pgp",
470 "zulu.pgp",
471 ]
472 .iter().map(|n| (n, openpgp::Cert::from_bytes(crate::tests::key(n)).unwrap()))
473 {
474 eprintln!("{}", name);
475 for key in cert.keys().map(|a| a.key()) {
476 let fp = key.fingerprint();
477 eprintln!("(sub)key: {}", fp);
478 assert_eq!(&Keygrip::of(key.mpis()).unwrap(),
479 keygrips.get(&fp).unwrap());
480 }
481 }
482 }
483}