wala-rust

Content-adressed HTTP file server
Info | Log | Files | Refs | README | LICENSE

pgp_sequoia.rs (22498B)


      1 //! This module provides authentication using PGP signatures.
      2 //!
      3 //! The public key and signature may be provided both as literal values from the individual PGP packets (i.e. raw public key and signature),
      4 //! or as the conventional packet bundle.
      5 //!
      6 //! If using bundle, the encoded data must be from the binary content, e.g. the output value of:
      7 //! 
      8 //! ``` ignore,
      9 //! gpg -b <file>
     10 //! ```
     11 //!
     12 //! Does not work for ECC secp256k1 signature.
     13 use std::io::Read;
     14 use crate::auth::{
     15     AuthSpec,
     16     AuthError,
     17     AuthResult,
     18 };
     19 use sequoia_openpgp::Cert;
     20 use sequoia_openpgp::KeyHandle;
     21 use sequoia_openpgp::Result as PGPResult;
     22 //use sequoia_openpgp::cert::prelude::*;
     23 use sequoia_openpgp::packet::key::PrimaryRole;
     24 use sequoia_openpgp::packet::key::PublicParts;
     25 use sequoia_openpgp::packet::prelude::*;
     26 use sequoia_openpgp::parse::Parse;
     27 use sequoia_openpgp::parse::stream::*;
     28 use sequoia_openpgp::crypto::hash::Digest;
     29 use sequoia_openpgp::serialize::MarshalInto;
     30 use sequoia_openpgp::parse::PacketParser;
     31 use sequoia_openpgp::parse::PacketParserResult;
     32 use sequoia_openpgp::policy::StandardPolicy;
     33 use nettle::hash::Sha256;
     34 
     35 use base64;
     36 
     37 use log::debug;
     38 use log::error;
     39 
     40 
     41 fn check_key_single(data: &Vec<u8>) -> Option<Key<PublicParts, PrimaryRole>> {
     42     match Key::from_bytes(&data) {
     43         Ok(v) => {
     44             let pubkey: Key<PublicParts, PrimaryRole> = v.into();
     45             return Some(pubkey);
     46         },
     47         Err(_) => {
     48         },
     49     };
     50     None
     51 }
     52 
     53 fn check_key_bundle(data: &Vec<u8>) -> Option<Cert> {
     54     match Cert::from_bytes(&data) {
     55         Ok(v) => {
     56             //let pubkey = v.primary_key().key();
     57             //return Some(pubkey.clone());
     58             return Some(v);
     59         },
     60         Err(_) => {
     61         },
     62     };
     63     None
     64 }
     65 
     66 fn check_sig_single(public_key: &Key<PublicParts, PrimaryRole>, signature_data: Vec<u8>, mut message: impl Read, message_length: usize) -> bool {
     67     match Signature::from_bytes(&signature_data) {
     68         Ok(mut v) => {
     69             //let mut hasher = Sha256::default();
     70             let mut hasher = Sha256::default();
     71             let mut message_data: Vec<u8> = vec!();
     72             _ = message.read_to_end(&mut message_data);
     73             hasher.update(&message_data);
     74             match v.verify_hash(&public_key, Box::new(hasher)) {
     75                 Ok(_) => {
     76                     return true;
     77                 },
     78                 Err(_) => {
     79                 },
     80             }
     81         },
     82         Err(_) => {
     83             _ = public_key;
     84             _ = message;
     85             _ = message_length;
     86         },
     87     }
     88     false
     89 }
     90 
     91 struct Helper {
     92     cert: Cert,
     93 }
     94 
     95 impl VerificationHelper for Helper {
     96     fn get_certs(&mut self, _ids: &[KeyHandle]) -> PGPResult<Vec<Cert>> {
     97         let mut certs = Vec::new();
     98         certs.push(self.cert.clone());
     99         return Ok(certs);
    100     }
    101 
    102     fn check(&mut self, structure: MessageStructure) -> PGPResult<()> {
    103         //let good = false;
    104 
    105         for (i, layer) in structure.into_iter().enumerate() {
    106             match (i, layer) {
    107                 (0, MessageLayer::SignatureGroup { results }) => {
    108                     match results.into_iter().next() {
    109                         Some(Ok(_)) => {
    110                         },
    111                         None => {
    112                             panic!("none");
    113                         },
    114                         Some(Err(e)) => {
    115                             panic!("err {}", e);
    116                         },
    117                     };
    118                 },
    119                 _ => {
    120                     println!("ouch");
    121                 },
    122             };
    123         }
    124         Ok(())
    125     }
    126 }
    127 
    128 // TODO handle unreadable message
    129 fn check_sig_bundle(public_key: &Cert, signature_data: Vec<u8>, mut message: impl Read, message_length: usize) -> bool {
    130     _ = message_length;
    131     let p = StandardPolicy::new();
    132     let mut sig_bundle_packets = PacketParser::from_bytes(&signature_data).unwrap();
    133     //while let PacketParserResult::Some(mut pp) = sig_bundle_packets {
    134     while let PacketParserResult::Some(pp) = sig_bundle_packets {
    135         let mut pk = pp.packet.clone();
    136         if let Packet::Signature(ref mut sig) = pk {
    137             _ = sig;
    138             let pbytes = pk.to_vec().unwrap();
    139             let helper = Helper{
    140                 cert: public_key.clone(),
    141             };
    142             let mut verifier = DetachedVerifierBuilder::from_bytes(&pbytes).unwrap().with_policy(&p, None, helper).unwrap();
    143 
    144             let mut message_all: Vec<u8> = vec!();
    145             _ = message.read_to_end(&mut message_all);
    146             match verifier.verify_bytes(&message_all) {
    147                 Ok(_) => {
    148                     return true;
    149                 },
    150                 Err(_) => {
    151                 },
    152             };
    153         }
    154         sig_bundle_packets = pp.recurse().unwrap().1;
    155     }
    156     false
    157 }
    158 
    159 //fn check_sig_bundle(public_key: &PublicKey, signature_data: Vec<u8>, mut message: impl Read, message_length: usize) -> bool {
    160 //    match StandaloneSignature::from_bytes(&signature_data[..]) {
    161 //        Ok(v) => {
    162 //            let mut data: Vec<u8> = vec!();
    163 //            let r = message.read_to_end(&mut data);
    164 //            match v.verify(public_key, &data) {
    165 //                Ok(v) => {
    166 //                    return true;
    167 //                },
    168 //                _ => {},
    169 //            };
    170 //        },
    171 //        _ => {},
    172 //    };
    173 //    false
    174 //}
    175 
    176 /// Verifies the given [auth::AuthSpec](crate::auth::AuthSpec) structure against the `pgp` scheme.
    177 ///
    178 /// The `key` and `signature` fields of the [auth::AuthSpec](crate::auth::AuthSpec) **MUST** be
    179 /// base64 encoded.
    180 ///
    181 /// # Arguments
    182 ///
    183 /// * `auth` - Authentication data submitted by client.
    184 /// * `data` - Content body submitted by client, to match signature against.
    185 /// * `data_length` - Length of content body.
    186 pub fn auth_check(auth: &AuthSpec, data: impl Read, data_length: usize) -> Result<AuthResult, AuthError> {
    187     if auth.method != "pgp" {
    188         return Err(AuthError{});
    189     }
    190 
    191     let key_data = match base64::decode(&auth.key) {
    192         Ok(v) => {
    193             v
    194         },
    195         Err(_) => {
    196             return Err(AuthError{});
    197         }
    198     };
    199 
    200     debug!("signature data {:?}", auth.signature);
    201     let sig_data = match base64::decode(&auth.signature) {
    202         Ok(v) => {
    203             v
    204         },
    205         Err(_) => {
    206             return Err(AuthError{});
    207         }
    208     };
    209 
    210     
    211     let key = match check_key_single(&key_data) {
    212         Some(v) => {
    213             debug!("using public key (raw) {:?}", v.keyid());
    214             let fingerprint = &v.fingerprint().to_vec().unwrap();
    215             let fingerprint_hex = hex::encode(&fingerprint);
    216             if !check_sig_single(&v, sig_data, data, data_length) {
    217                 error!("invalid raw signature for {:?}", &fingerprint_hex);
    218                 return Err(AuthError{});
    219             }
    220             debug!("found valid raw key {:?}", &fingerprint_hex);
    221             v
    222         },
    223         None => {
    224             let key = match check_key_bundle(&key_data) {
    225                 Some(v) => {
    226                     let fingerprint = &v.fingerprint().to_vec().unwrap();
    227                     let fingerprint_hex = hex::encode(&fingerprint);
    228                     debug!("using public key (bundle) {:?}", v.keyid());
    229                     if !check_sig_bundle(&v, sig_data, data, data_length) {
    230                         error!("invalid bundle signature for {:?}", &fingerprint_hex);
    231                         return Err(AuthError{});
    232                     }
    233                     debug!("found valid key bundle {:?}", &fingerprint_hex);
    234                     v
    235                 },
    236                 None => {
    237                     return Err(AuthError{});
    238                 },
    239             };
    240             key.primary_key().key().clone()
    241         },
    242     };
    243 
    244 
    245     let res = AuthResult {
    246         identity: key.fingerprint().to_vec().unwrap(),
    247         error: false,
    248     };
    249     Ok(res)
    250 }
    251 
    252 #[cfg(test)]
    253 mod tests { 
    254 
    255     use super::auth_check;
    256     use super::AuthSpec;
    257     use std::str::FromStr;
    258     use super::{
    259         check_key_bundle,
    260         check_key_single,
    261         check_sig_single,
    262         check_sig_bundle,
    263     };
    264     use env_logger;
    265 
    266 
    267     #[test]
    268     fn test_pgp_single() {
    269         let key_single_hex = "0462a9f5a916092b06010401da470f0101074061f06baae76d5115553019e50353890e498652fac873d78003e9e192dd9f3e13";
    270         let sig_foo_single_hex = "0401160a0006050262a9f5a9002109108b21a9d88b4a0c7f1621044ab95b491980f89789ae8fde8b21a9d88b4a0c7f2aba0100b7b06c424cdb67bba97463d2eb3035ead329f62c92fb6100b629df003748131200fd17e8b6dc866aa1662b93a17ff599334002de273b800fc7160634516187b41407";
    271 
    272         let key_single = hex::decode(&key_single_hex).unwrap();
    273         let key_single_base64 = base64::encode(&key_single);
    274 
    275         let sig_foo_single = hex::decode(&sig_foo_single_hex).unwrap();
    276         let sig_foo_single_base64 = base64::encode(&sig_foo_single);
    277 
    278         let auth_spec_str = format!("PUBSIG pgp:{}:{}", key_single_base64, sig_foo_single_base64);
    279         let auth_spec = AuthSpec::from_str(&auth_spec_str).unwrap();
    280 
    281         let data = b"foo";
    282         let r = match check_key_single(&key_single) {
    283             Some(v) => {
    284                 //if !check_sig_single(&v, sig_foo_single, &data[..], 0) {
    285                 //    panic!("invalid");
    286                 //}
    287             },
    288             None => {
    289                 panic!("no public key");
    290             },
    291         };
    292 
    293     }
    294 
    295  
    296     #[test]
    297     fn test_pgp_bundle() {
    298         let key_bundle_hex = "99018d045fa148e8010c00a990f4048c00e39d0b63980b1d3d8a71e4df8e3090588f50c0a0862c0ed57abdb701250b7de0e9b7c65ed1061bfd9b6a0b8333ec891c230841515b2352bb4054a790858dc5df9b44b82b67a0c787ab1674e74920bd4bab6654dad53445ef49c13ab0a027989ec9357d44c49b848963db50345627586823df8047ef0438d78944ba3f8f4369f92e081439f43ecc5d4fe481d06634cf6704823be3a0faf8956f4801bf05b7d4c3629fa63b37a39f5160ec2b88ae5051480bfeb23edb550c35e5d8754a96f0b52e71c6e6c26bc1311062380725e6797751d0a649f8403992c3b4892b10ffa8a948e75283e8b49e2382945366d4ffce85b52c600c4251f897eb9e05327db3a315411232777bb974a47ee4b6875ac4472d3e87d02c103d2d20d421e8ea26c5349e9c3f0c70c3daebf11befc0ea5815f4bf044e5be0a7c47378e09fd9b1ee88a618cfceedc6f905c2c5e0535f936716e4fa6b4205b9e0b153cb35aad8fca1a45492feca1707443d0f978c1751de9ab90b98eaf43d02e2a2093d567b6b0011010001b41e4d6572204d616e203c6d65726d616e4067726579736b756c6c2e636f6d3e8901d404130108003e162104f3faf668e82ef5124d5187baef26f4682343f69205025fa148e8021b03050903c26700050b0908070206150a09080b020416020301021e01021780000a0910ef26f4682343f692f08b0c0084525b03250ad394c929f0f3f35ef9ecde3bd924ed07cb1faf46aec2646bf19b35bad1d154cd1bfe39234eea38b8936bed85552932a013bfab27ea70866df0953438e0b54b8c1c96022ffb35683713d6f67a59beaa47e09bf45f16ddef92e3b1192c99c8814587efedc2fea20013efafac3b319c2b15a4b450fde0d5519ae5316a83a28dc6877d1ff80f2f1bf8e65bad5dfdd5bc1653269af5a719fb68b1e51731322203596cffcdd50178e064ab37f9340df2fadf5b198dab945da6576e2e711ac28a098c09d60deae7cee98ba9937535779f484a815ba2a4ef211b2e7ef9878ebfe857b02c43c3267e4a1bfea9abddfd26a9c58802744b9ebb038d2057ec22c58277748272329789c4d310532ef27bc199fef1ba8da1bfabc43b1a228e55d5fb82e41abb24741f19320f0fcb0131e832e60e89209c08532eb4dcd5285b90eb50df23638f214aec10e9aa86ec25a97a77c4a96e171c5092dea5a4a6b24d02809b138e1025e84c17d046204e8de43f97d272aadb0fc57041a4fc09c138ac2578b9018d045fa148e8010c00c7b694d3d64e31fba14de4e794469280688b283c7821909da56fac969844826be0da47a19700a95b7742fd50c7172ea9da0a7e12a59bdb7fc84fe1f251816751979b9537fae6c956d92d456333c35c55d15122b4ec372bb898b066c9d737880ffdd2c09310e8fd7bb0d2bf12e698b163a70339c572ddeb3d25ebef46fb0a0d980457552dd5fd1af2167b72282dc04c2a95949a10046e394e3da0308ba7a5a7532575a6762a28a1e196dec2de52fe4c33df26481e128ca46526a18f363c7994ee8c9896d979767f2ff68ee84b175100f687cd96a61965e2ba466163e21d3098a99622a8d62be84df1529cdcbd6569dee09854958fe83b35d7ba7b068c8a573d34cb10bd95e745ddea6992d656a85568c02322850137ebbe8bdc30d366c9c97f5ffcc4a521e54d8c8f7941be396f08408a8b4d2aa3c6f59a7fce9254232794f85b34eb1fa5feff3a5ecfc6649ad70e6b21dd50a0ffeb8ae867d7576d71bf869e40f945a3f2849560a83640d03002767e6a2676295444fd8eb50c280583a09cc7f700110100018901bc041801080026162104f3faf668e82ef5124d5187baef26f4682343f69205025fa148e8021b0c050903c26700000a0910ef26f4682343f69220f70bff78ea2f60364a60beb3404ee2f30f11f0a96b6819fefa356b3ce3522799bb95a12316f98a4d15e93ef9116e45d0e7998fda97234ea05be20cf3e0ba226520691de5e3b52306204fabeb8a3bd42e4ff9ceb5b4acd9d5e84cd08b31037b325add5018b1fa59ea6591920e087a9ffb95c80630f12878737ff7b611d4891ef1cd5286c402834fc2e8563847b214f362e42af0caa57efc1523878448b6abc90f98fa3e7e54b0f348b80f0279c8a85fee8b62508de7c9c66fd52e860d68f00676aa33feefd1d139b3f786d951f2e3810683a14a67c58cb02b624695fde63d9dcf3568f1273b904c5e467b96f3fd3ca59d1608c814fb283ee868a1ceeb67e10db60f2787fde2264de01ea79d301e3f7e3314396451b5f8007b9d5d4edbbf14f939493dc7d736b63ef1c3140768486adbe26c616d04570dbb44b85bca69c17cb8d555492d345d27406bd4d93128730e29af66640c74244a795b35ae24ba394bed5cdba67120c8e9a2e5eafd19a22e5525400e8bc1bbea73fcebf5cbfde351ef2167f2a579";
    299         let sig_foo_bundle_hex = "8901b304000108001d162104f3faf668e82ef5124d5187baef26f4682343f692050262b426d7000a0910ef26f4682343f6922fc50bff526ff1fb6de59bd022f4f71389b7d429040fcf4f3c6889b015de95dd1562b9ff197c7cb24040370c7a68c08c0b2430e034456f71a0c3b1c8c4bfacf6dd37e3d3305563b59c157c015d33a360395daefd9f4cd9370fd3e75c201d491a2008bead964f31955cd9bd3b09ef3647d4b92188fedcabbbfdefdb70a5c345c4f94ad1cacfe10b12782731d49ef516d2223dc2e01c4dedaffa558794339ee866244f7bcf4e2daeffb1d2501dd969837163e8eebc9b58fa0d6e75e6e119753c9bd7b621ef4a73f1953bd2ab69e8241d17ae5dcb900cf6f9575d2038152769dece1baf446cd1adcfb6e742ed0980519de3ca4c7360ef70e4cf38cafb504d5b04144fae0786e8d8c65c5c1475ca723bbbb5fed2416f10f0fd82a4e2bd6c5590e8f018c85941f63dd4ca5f3784760facca9a5c68a01b5ddde25887a492475ae611bfbb359a281b0052c1674d1cf6646f84b75293f1820bb5cf5a2a029e02b7c54177fb92e1184b14b646a80d37da28c9715aad37f9609d7c866881a2efe51e931cccc38d438f";
    300 
    301         let key_bundle = hex::decode(&key_bundle_hex).unwrap();
    302         let key_bundle_base64 = base64::encode(&key_bundle);
    303 
    304         let sig_foo_bundle = hex::decode(&sig_foo_bundle_hex).unwrap();
    305         let sig_foo_bundle_base64 = base64::encode(&sig_foo_bundle);
    306 
    307         let data = b"foo";
    308 
    309         let r = match check_key_bundle(&key_bundle) {
    310             Some(v) => {
    311                 //if !check_sig_bundle(&v, sig_foo_bundle, &data[..], 0) {
    312                 //    panic!("invalid");
    313                 //}
    314             },
    315             None => {
    316                 panic!("no public key");
    317             },
    318         };
    319     }
    320 
    321 
    322     #[test]
    323     fn test_pgp_auth_single() {
    324         env_logger::init();
    325 
    326         let key_single_hex = "045fa148e8010c00a990f4048c00e39d0b63980b1d3d8a71e4df8e3090588f50c0a0862c0ed57abdb701250b7de0e9b7c65ed1061bfd9b6a0b8333ec891c230841515b2352bb4054a790858dc5df9b44b82b67a0c787ab1674e74920bd4bab6654dad53445ef49c13ab0a027989ec9357d44c49b848963db50345627586823df8047ef0438d78944ba3f8f4369f92e081439f43ecc5d4fe481d06634cf6704823be3a0faf8956f4801bf05b7d4c3629fa63b37a39f5160ec2b88ae5051480bfeb23edb550c35e5d8754a96f0b52e71c6e6c26bc1311062380725e6797751d0a649f8403992c3b4892b10ffa8a948e75283e8b49e2382945366d4ffce85b52c600c4251f897eb9e05327db3a315411232777bb974a47ee4b6875ac4472d3e87d02c103d2d20d421e8ea26c5349e9c3f0c70c3daebf11befc0ea5815f4bf044e5be0a7c47378e09fd9b1ee88a618cfceedc6f905c2c5e0535f936716e4fa6b4205b9e0b153cb35aad8fca1a45492feca1707443d0f978c1751de9ab90b98eaf43d02e2a2093d567b6b0011010001";
    327         //let key_single_hex = "0462a9f5a916092b06010401da470f0101074061f06baae76d5115553019e50353890e498652fac873d78003e9e192dd9f3e13";
    328 
    329         let sig_foo_single_hex = "04000108001d162104f3faf668e82ef5124d5187baef26f4682343f69205026334796a000a0910ef26f4682343f692dfa30c009187134c366ccd867442d55b2b722c6c3d3ffa273cfbd53ca2f00a7d36b7961a575ad79b75cdb90276edee565d19baa14a95296fcd4b7f3df856d20c3d9c5b8523a1a253e3071a486d437af97f4bf67978c065a3013191bd643893f8d0239cccdca6d31b19aa6e6b225dc339e34d395246cc7d3ea455c8c8b1528532f509194b935f8f81e7a1a22896f5029bda6c54cd38687e5e225a21122180f1cad630885b7a3fdfef8e5b2f5634e9bc55d24aff40c7723a156b88a074919fd904e42077e4c26c81c0e569dde6ebd7558217eea34079252ed5d0fc766c174d3d66c1366b43e463bc0c3ca693e6216020142a33f3f710e17cb1d1f6b657a1f01a59c847604424bfbfc555316815cdd2bf2ea78792a1399b768920a2bb640e5694ae0d9d12ac66c2b50b2913736711a4ad312e0a2d75f8911adce976774c03c851252aa6292141e92c2988a1cebde8c292892aa10c78cd6ec2a43449216ee1ded8a546d1199153ee54c92fb042ad0d8e5d31a2299e942aa500ba0a7c7e46b2edb23d56995947";
    330         //let sig_foo_single_hex = "0401160a0006050262a9f5a9002109108b21a9d88b4a0c7f1621044ab95b491980f89789ae8fde8b21a9d88b4a0c7f2aba0100b7b06c424cdb67bba97463d2eb3035ead329f62c92fb6100b629df003748131200fd17e8b6dc866aa1662b93a17ff599334002de273b800fc7160634516187b41407";
    331 
    332         let key_single = hex::decode(&key_single_hex).unwrap();
    333         let key_single_base64 = base64::encode(&key_single);
    334 
    335         let sig_foo_single = hex::decode(&sig_foo_single_hex).unwrap();
    336         let sig_foo_single_base64 = base64::encode(&sig_foo_single);
    337 
    338         let auth_spec_str = format!("PUBSIG pgp:{}:{}", key_single_base64, sig_foo_single_base64);
    339         let auth_spec = AuthSpec::from_str(&auth_spec_str).unwrap();
    340 
    341         let data = b"foo";
    342 
    343         match auth_check(&auth_spec, &data[..], 0) {
    344             Ok(v) => {
    345             },
    346             Err(e) => {
    347                 panic!("{}", e);
    348             },
    349         }
    350     }
    351 
    352     #[test]
    353     fn test_pgp_auth_bundle() {
    354         let key_bundle_hex = "99018d045fa148e8010c00a990f4048c00e39d0b63980b1d3d8a71e4df8e3090588f50c0a0862c0ed57abdb701250b7de0e9b7c65ed1061bfd9b6a0b8333ec891c230841515b2352bb4054a790858dc5df9b44b82b67a0c787ab1674e74920bd4bab6654dad53445ef49c13ab0a027989ec9357d44c49b848963db50345627586823df8047ef0438d78944ba3f8f4369f92e081439f43ecc5d4fe481d06634cf6704823be3a0faf8956f4801bf05b7d4c3629fa63b37a39f5160ec2b88ae5051480bfeb23edb550c35e5d8754a96f0b52e71c6e6c26bc1311062380725e6797751d0a649f8403992c3b4892b10ffa8a948e75283e8b49e2382945366d4ffce85b52c600c4251f897eb9e05327db3a315411232777bb974a47ee4b6875ac4472d3e87d02c103d2d20d421e8ea26c5349e9c3f0c70c3daebf11befc0ea5815f4bf044e5be0a7c47378e09fd9b1ee88a618cfceedc6f905c2c5e0535f936716e4fa6b4205b9e0b153cb35aad8fca1a45492feca1707443d0f978c1751de9ab90b98eaf43d02e2a2093d567b6b0011010001b41e4d6572204d616e203c6d65726d616e4067726579736b756c6c2e636f6d3e8901d404130108003e162104f3faf668e82ef5124d5187baef26f4682343f69205025fa148e8021b03050903c26700050b0908070206150a09080b020416020301021e01021780000a0910ef26f4682343f692f08b0c0084525b03250ad394c929f0f3f35ef9ecde3bd924ed07cb1faf46aec2646bf19b35bad1d154cd1bfe39234eea38b8936bed85552932a013bfab27ea70866df0953438e0b54b8c1c96022ffb35683713d6f67a59beaa47e09bf45f16ddef92e3b1192c99c8814587efedc2fea20013efafac3b319c2b15a4b450fde0d5519ae5316a83a28dc6877d1ff80f2f1bf8e65bad5dfdd5bc1653269af5a719fb68b1e51731322203596cffcdd50178e064ab37f9340df2fadf5b198dab945da6576e2e711ac28a098c09d60deae7cee98ba9937535779f484a815ba2a4ef211b2e7ef9878ebfe857b02c43c3267e4a1bfea9abddfd26a9c58802744b9ebb038d2057ec22c58277748272329789c4d310532ef27bc199fef1ba8da1bfabc43b1a228e55d5fb82e41abb24741f19320f0fcb0131e832e60e89209c08532eb4dcd5285b90eb50df23638f214aec10e9aa86ec25a97a77c4a96e171c5092dea5a4a6b24d02809b138e1025e84c17d046204e8de43f97d272aadb0fc57041a4fc09c138ac2578b9018d045fa148e8010c00c7b694d3d64e31fba14de4e794469280688b283c7821909da56fac969844826be0da47a19700a95b7742fd50c7172ea9da0a7e12a59bdb7fc84fe1f251816751979b9537fae6c956d92d456333c35c55d15122b4ec372bb898b066c9d737880ffdd2c09310e8fd7bb0d2bf12e698b163a70339c572ddeb3d25ebef46fb0a0d980457552dd5fd1af2167b72282dc04c2a95949a10046e394e3da0308ba7a5a7532575a6762a28a1e196dec2de52fe4c33df26481e128ca46526a18f363c7994ee8c9896d979767f2ff68ee84b175100f687cd96a61965e2ba466163e21d3098a99622a8d62be84df1529cdcbd6569dee09854958fe83b35d7ba7b068c8a573d34cb10bd95e745ddea6992d656a85568c02322850137ebbe8bdc30d366c9c97f5ffcc4a521e54d8c8f7941be396f08408a8b4d2aa3c6f59a7fce9254232794f85b34eb1fa5feff3a5ecfc6649ad70e6b21dd50a0ffeb8ae867d7576d71bf869e40f945a3f2849560a83640d03002767e6a2676295444fd8eb50c280583a09cc7f700110100018901bc041801080026162104f3faf668e82ef5124d5187baef26f4682343f69205025fa148e8021b0c050903c26700000a0910ef26f4682343f69220f70bff78ea2f60364a60beb3404ee2f30f11f0a96b6819fefa356b3ce3522799bb95a12316f98a4d15e93ef9116e45d0e7998fda97234ea05be20cf3e0ba226520691de5e3b52306204fabeb8a3bd42e4ff9ceb5b4acd9d5e84cd08b31037b325add5018b1fa59ea6591920e087a9ffb95c80630f12878737ff7b611d4891ef1cd5286c402834fc2e8563847b214f362e42af0caa57efc1523878448b6abc90f98fa3e7e54b0f348b80f0279c8a85fee8b62508de7c9c66fd52e860d68f00676aa33feefd1d139b3f786d951f2e3810683a14a67c58cb02b624695fde63d9dcf3568f1273b904c5e467b96f3fd3ca59d1608c814fb283ee868a1ceeb67e10db60f2787fde2264de01ea79d301e3f7e3314396451b5f8007b9d5d4edbbf14f939493dc7d736b63ef1c3140768486adbe26c616d04570dbb44b85bca69c17cb8d555492d345d27406bd4d93128730e29af66640c74244a795b35ae24ba394bed5cdba67120c8e9a2e5eafd19a22e5525400e8bc1bbea73fcebf5cbfde351ef2167f2a579";
    355         let sig_foo_bundle_hex = "8901b304000108001d162104f3faf668e82ef5124d5187baef26f4682343f692050262b426d7000a0910ef26f4682343f6922fc50bff526ff1fb6de59bd022f4f71389b7d429040fcf4f3c6889b015de95dd1562b9ff197c7cb24040370c7a68c08c0b2430e034456f71a0c3b1c8c4bfacf6dd37e3d3305563b59c157c015d33a360395daefd9f4cd9370fd3e75c201d491a2008bead964f31955cd9bd3b09ef3647d4b92188fedcabbbfdefdb70a5c345c4f94ad1cacfe10b12782731d49ef516d2223dc2e01c4dedaffa558794339ee866244f7bcf4e2daeffb1d2501dd969837163e8eebc9b58fa0d6e75e6e119753c9bd7b621ef4a73f1953bd2ab69e8241d17ae5dcb900cf6f9575d2038152769dece1baf446cd1adcfb6e742ed0980519de3ca4c7360ef70e4cf38cafb504d5b04144fae0786e8d8c65c5c1475ca723bbbb5fed2416f10f0fd82a4e2bd6c5590e8f018c85941f63dd4ca5f3784760facca9a5c68a01b5ddde25887a492475ae611bfbb359a281b0052c1674d1cf6646f84b75293f1820bb5cf5a2a029e02b7c54177fb92e1184b14b646a80d37da28c9715aad37f9609d7c866881a2efe51e931cccc38d438f";
    356 
    357         let key_bundle = hex::decode(&key_bundle_hex).unwrap();
    358         let key_bundle_base64 = base64::encode(&key_bundle);
    359 
    360         let sig_foo_bundle = hex::decode(&sig_foo_bundle_hex).unwrap();
    361         let sig_foo_bundle_base64 = base64::encode(&sig_foo_bundle);
    362 
    363         let auth_spec_str = format!("PUBSIG pgp:{}:{}", key_bundle_base64, sig_foo_bundle_base64);
    364         let auth_spec = AuthSpec::from_str(&auth_spec_str).unwrap();
    365 
    366         let data = b"foo";
    367 
    368         match auth_check(&auth_spec, &data[..], 0) {
    369             Ok(v) => {
    370             },
    371             Err(e) => {
    372                 panic!("{}", e);
    373             },
    374         }
    375     }
    376 }