From 82db93d5fc924860e4f1fb4cf24f29b5b335a480 Mon Sep 17 00:00:00 2001 From: Bill Richardson Date: Thu, 24 Sep 2015 10:59:41 -0700 Subject: [PATCH] futility: Add show capability for usbpd1 images The firmware for the USB Type-C power adapters uses raw binary blobs for the public keys and signatures instead of readily-identifiable structs. We've been able to sign these firmware images for some time, but verifying the result generally required testing them on hardware. This CL adds some futilty support for recognizing and verifying those images too. It just tries various sig and hash algorithms, until it finds a combination for which the image is self-consistent (where the pubkey blob verifies the signature blob). BUG=none BRANCH=none TEST=make runtests This change also adds additional tests for usbpd1 images. We ensure that we correctly recognize and verify an MP-signed firmware, plus test signing and verifying usbpd1 images using multiple signature and hash algorithms. Change-Id: I4fbe8b37a694992f635d5469ae1c2449b1610dfd Signed-off-by: Bill Richardson Reviewed-on: https://chromium-review.googlesource.com/302415 Reviewed-by: Randall Spangler --- futility/file_type.inc | 7 +- futility/file_type_usbpd1.c | 322 +++++++++++++++++++++--- tests/futility/data/zinger_mp_image.bin | Bin 0 -> 32768 bytes tests/futility/run_test_scripts.sh | 1 + tests/futility/test_file_types.c | 9 +- tests/futility/test_show_usbpd1.sh | 46 ++++ 6 files changed, 346 insertions(+), 39 deletions(-) create mode 100644 tests/futility/data/zinger_mp_image.bin create mode 100755 tests/futility/test_show_usbpd1.sh diff --git a/futility/file_type.inc b/futility/file_type.inc index 4014c0b4d5..5433d03c64 100644 --- a/futility/file_type.inc +++ b/futility/file_type.inc @@ -71,9 +71,8 @@ FILE_TYPE(CHROMIUMOS_DISK, "disk_img", "chromiumos disk image", NONE, NONE, NONE) - -/* Firmware for Samus' USB Type-C power adapters */ +/* Firmware for USB Type-C power adapters */ FILE_TYPE(USBPD1, "usbpd1", "USB-PD charger image (v1.0)", - NONE, - NONE, + R_(ft_recognize_usbpd1), + S_(ft_show_usbpd1), S_(ft_sign_usbpd1)) diff --git a/futility/file_type_usbpd1.c b/futility/file_type_usbpd1.c index 7230b0cafa..acf3de06bf 100644 --- a/futility/file_type_usbpd1.c +++ b/futility/file_type_usbpd1.c @@ -31,6 +31,55 @@ #include "host_signature2.h" #include "util_misc.h" +/* Return 1 if okay, 0 if not */ +static int parse_size_opts(uint32_t len, + uint32_t *ro_size_ptr, uint32_t *rw_size_ptr, + uint32_t *ro_offset_ptr, uint32_t * rw_offset_ptr) +{ + uint32_t ro_size, rw_size, ro_offset, rw_offset; + + /* Assume the image has both RO and RW, evenly split. */ + ro_offset = 0; + ro_size = rw_size = rw_offset = len / 2; + + /* Unless told otherwise... */ + if (sign_option.ro_size != 0xffffffff) + ro_size = sign_option.ro_size; + if (sign_option.ro_offset != 0xffffffff) + ro_offset = sign_option.ro_offset; + + /* If RO is missing, the whole thing must be RW */ + if (!ro_size) { + rw_size = len; + rw_offset = 0; + } + + /* Unless that's overridden too */ + if (sign_option.rw_size != 0xffffffff) + rw_size = sign_option.rw_size; + if (sign_option.rw_offset != 0xffffffff) + rw_offset = sign_option.rw_offset; + + Debug("ro_size 0x%08x\n", ro_size); + Debug("ro_offset 0x%08x\n", ro_offset); + Debug("rw_size 0x%08x\n", rw_size); + Debug("rw_offset 0x%08x\n", rw_offset); + + /* Now let's do some sanity checks. */ + if (ro_size > len || ro_offset > len - ro_size || + rw_size > len || rw_offset > len - rw_size) { + printf("size/offset values are bogus\n"); + return 0; + } + + *ro_size_ptr = ro_size; + *rw_size_ptr = rw_size; + *ro_offset_ptr = ro_offset; + *rw_offset_ptr = rw_offset; + + return 1; +} + int ft_sign_usbpd1(const char *name, uint8_t *buf, uint32_t len, void *data) { struct vb2_private_key *key_ptr = 0; @@ -51,36 +100,9 @@ int ft_sign_usbpd1(const char *name, uint8_t *buf, uint32_t len, void *data) Debug("%s(): name %s\n", __func__, name); Debug("%s(): len 0x%08x (%d)\n", __func__, len, len); - /* - * Check for size args. Note that we're NOT worrying about rollover, - * overlapping regions, out of bounds, etc. - */ - ro_offset = 0; - ro_size = rw_size = rw_offset = len / 2; - - /* Override some stuff? */ - if (sign_option.ro_size != 0xffffffff) - ro_size = sign_option.ro_size; - if (sign_option.rw_size != 0xffffffff) - rw_size = sign_option.rw_size; - - Debug("ro_size 0x%08x\n", ro_size); - Debug("ro_offset 0x%08x\n", ro_offset); - - /* If RO is missing, the whole thing must be RW */ - if (!ro_size) { - rw_size = len; - rw_offset = 0; - } - - /* Unless that's overridden too */ - if (sign_option.ro_offset != 0xffffffff) - ro_offset = sign_option.ro_offset; - if (sign_option.rw_offset != 0xffffffff) - rw_offset = sign_option.rw_offset; - - Debug("rw_size 0x%08x\n", rw_size); - Debug("rw_offset 0x%08x\n", rw_offset); + /* Get image locations */ + if (!parse_size_opts(len, &ro_size, &rw_size, &ro_offset, &rw_offset)) + goto done; /* Read the signing keypair file */ if (vb2_private_key_read_pem(&key_ptr, sign_option.pem_signpriv)) { @@ -225,3 +247,243 @@ done: return retval; } + + +/* + * Algorithms that we want to try, in order. We've only ever shipped with + * RSA2048 / SHA256, but the others should work in tests. + */ +static enum vb2_signature_algorithm sigs[] = { + VB2_SIG_RSA2048, + VB2_SIG_RSA1024, + VB2_SIG_RSA4096, + VB2_SIG_RSA8192, +}; +enum vb2_hash_algorithm hashes[] = { + VB2_HASH_SHA256, + VB2_HASH_SHA1, + VB2_HASH_SHA512, +}; + +/* + * The size of the public key structure used by usbpd1 is + * 2 x RSANUMBYTES for n and rr fields + * plus 4 for n0inv, aligned on a multiple of 16 + */ +static uint32_t usbpd1_packed_key_size(enum vb2_signature_algorithm sig_alg) +{ + switch (sig_alg) { + case VB2_SIG_RSA1024: + return 272; + case VB2_SIG_RSA2048: + return 528; + case VB2_SIG_RSA4096: + return 1040; + case VB2_SIG_RSA8192: + return 2064; + default: + return 0; + } +} +static void vb2_pubkey_from_usbpd1(struct vb2_public_key *key, + enum vb2_signature_algorithm sig_alg, + enum vb2_hash_algorithm hash_alg, + const uint8_t *o_pubkey, + uint32_t o_pubkey_size) +{ + key->arrsize = vb2_rsa_sig_size(sig_alg) / sizeof(uint32_t); + key->n0inv = *((uint32_t *)o_pubkey + 2 * key->arrsize); + key->n = (uint32_t *)o_pubkey; + key->rr = (uint32_t *)o_pubkey + key->arrsize; + key->sig_alg = sig_alg; + key->hash_alg = hash_alg; + key->desc = 0; + key->version = 0; + key->id = vb2_hash_id(hash_alg); +} + +static int vb2_sig_from_usbpd1(struct vb2_signature **sig, + enum vb2_signature_algorithm sig_alg, + enum vb2_hash_algorithm hash_alg, + const uint8_t *o_sig, + uint32_t o_sig_size, + uint32_t data_size) +{ + struct vb2_signature s = { + .c.magic = VB2_MAGIC_SIGNATURE, + .c.struct_version_major = VB2_SIGNATURE_VERSION_MAJOR, + .c.struct_version_minor = VB2_SIGNATURE_VERSION_MINOR, + .c.fixed_size = sizeof(s), + .sig_alg = sig_alg, + .hash_alg = hash_alg, + .data_size = data_size, + .sig_size = vb2_rsa_sig_size(sig_alg), + .sig_offset = sizeof(s), + }; + uint32_t total_size = sizeof(s) + o_sig_size; + uint8_t *buf = calloc(1, total_size); + if (!buf) + return VB2_ERROR_UNKNOWN; + + memcpy(buf, &s, sizeof(s)); + memcpy(buf + sizeof(s), o_sig, o_sig_size); + + *sig = (struct vb2_signature *)buf; + return VB2_SUCCESS; +} + +static void show_usbpd1_stuff(const char *name, + enum vb2_signature_algorithm sig_alg, + enum vb2_hash_algorithm hash_alg, + const uint8_t *o_pubkey, uint32_t o_pubkey_size) +{ + struct vb2_public_key key; + struct vb2_packed_key *pkey; + uint8_t *sha1sum; + int i; + + vb2_pubkey_from_usbpd1(&key, sig_alg, hash_alg, + o_pubkey, o_pubkey_size); + + if (vb2_public_key_pack(&pkey, &key)) + return; + + sha1sum = DigestBuf((uint8_t *)pkey + pkey->key_offset, + pkey->key_size, SHA1_DIGEST_ALGORITHM); + + printf("USB-PD v1 image: %s\n", name); + printf(" Algorithm: %s %s\n", + vb2_lookup_by_num(vb2_text_vs_sig, sig_alg)->name, + vb2_lookup_by_num(vb2_text_vs_hash, hash_alg)->name); + printf(" Key sha1sum: "); + for (i = 0; i < SHA1_DIGEST_SIZE; i++) + printf("%02x", sha1sum[i]); + printf("\n"); + + free(sha1sum); + free(pkey); +} + + +/* Returns VB2_SUCCESS or random error code */ +static int try_our_own(enum vb2_signature_algorithm sig_alg, + enum vb2_hash_algorithm hash_alg, + const uint8_t *o_pubkey, uint32_t o_pubkey_size, + const uint8_t *o_sig, uint32_t o_sig_size, + const uint8_t *data, uint32_t data_size) +{ + struct vb2_public_key pubkey; + struct vb2_signature *sig; + uint8_t buf[VB2_WORKBUF_RECOMMENDED_SIZE] + __attribute__ ((aligned (VB2_WORKBUF_ALIGN))); + struct vb2_workbuf wb = { + .buf = buf, + .size = sizeof(buf), + }; + int rv = VB2_ERROR_UNKNOWN; + + vb2_pubkey_from_usbpd1(&pubkey, sig_alg, hash_alg, + o_pubkey, o_pubkey_size); + + if ((rv = vb2_sig_from_usbpd1(&sig, sig_alg, hash_alg, + o_sig, o_sig_size, data_size))) + return rv; + + rv = vb2_verify_data(data, data_size, sig, &pubkey, &wb); + + free(sig); + + return rv; +} + +/* Returns VB2_SUCCESS if the image validates itself */ +static int check_self_consistency(const uint8_t *buf, + const char *name, + uint32_t ro_size, uint32_t rw_size, + uint32_t ro_offset, uint32_t rw_offset, + enum vb2_signature_algorithm sig_alg, + enum vb2_hash_algorithm hash_alg) +{ + /* Where are the important bits? */ + uint32_t sig_size = vb2_rsa_sig_size(sig_alg); + uint32_t sig_offset = rw_offset + rw_size - sig_size; + uint32_t pubkey_size = usbpd1_packed_key_size(sig_alg); + uint32_t pubkey_offset = ro_offset + ro_size - pubkey_size; + int rv; + + /* Skip stuff that obviously doesn't work */ + if (sig_size > rw_size || pubkey_size > ro_size) + return VB2_ERROR_UNKNOWN; + + rv = try_our_own(sig_alg, hash_alg, /* algs */ + buf + pubkey_offset, pubkey_size, /* pubkey blob */ + buf + sig_offset, sig_size, /* sig blob */ + buf + rw_offset, rw_size - sig_size); /* RW image */ + + if (rv == VB2_SUCCESS && name) + show_usbpd1_stuff(name, sig_alg, hash_alg, + buf + pubkey_offset, pubkey_size); + + return rv; +} + + +int ft_show_usbpd1(const char *name, uint8_t *buf, uint32_t len, void *data) +{ + uint32_t ro_size, rw_size, ro_offset, rw_offset; + int s, h; + + Debug("%s(): name %s\n", __func__, name); + Debug("%s(): len 0x%08x (%d)\n", __func__, len, len); + + /* Get image locations */ + if (!parse_size_opts(len, &ro_size, &rw_size, &ro_offset, &rw_offset)) + return 1; + + /* TODO: If we don't have a RO image, ask for a public key + * TODO: If we're given an external public key, use it (and its alg) */ + if (!ro_size) { + printf("Can't find the public key\n"); + return 1; + } + + /* TODO: Only loop through the numbers we haven't been given */ + for (s = 0; s < ARRAY_SIZE(sigs); s++) + for (h = 0; h < ARRAY_SIZE(hashes); h++) + if (!check_self_consistency(buf, name, + ro_size, rw_size, + ro_offset, rw_offset, + sigs[s], hashes[h])) + return 0; + + printf("This doesn't appear to be a complete usbpd1 image\n"); + return 1; +} + +enum futil_file_type ft_recognize_usbpd1(uint8_t *buf, uint32_t len) +{ + uint32_t ro_size, rw_size, ro_offset, rw_offset; + int s, h; + + Debug("%s(): len 0x%08x (%d)\n", __func__, len, len); + + /* + * Since we don't use any headers to identify or locate the pubkey and + * signature, in order to identify blob as the right type we have to + * just assume that the RO & RW are 1) both present, and 2) evenly + * split. Then we just try to use what we think might be the pubkey to + * validate what we think might be the signature. + */ + ro_offset = 0; + ro_size = rw_size = rw_offset = len / 2; + + for (s = 0; s < ARRAY_SIZE(sigs); s++) + for (h = 0; h < ARRAY_SIZE(hashes); h++) + if (!check_self_consistency(buf, 0, + ro_size, rw_size, + ro_offset, rw_offset, + sigs[s], hashes[h])) + return FILE_TYPE_USBPD1; + + return FILE_TYPE_UNKNOWN; +} diff --git a/tests/futility/data/zinger_mp_image.bin b/tests/futility/data/zinger_mp_image.bin new file mode 100644 index 0000000000000000000000000000000000000000..68152c02e9756a47bcc3f818eb225ea3e45eb98f GIT binary patch literal 32768 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 z zINdTY=VLv}B(`CeQC|yiw@?D*!G&gcyETq!SGCn}s4+?icQZ$TUj{xMR;AzzW;5-r z$iSYdX5?LDPN~H+zwF2DOO;f2rU~s$uSJQV5e!rh(;yWJ-BjyQtM($bHw>`-F6<`Z zHr~*rMBQcs%<#2YeC!LHN3}czyKQPOp2>cK+CYQPp~0~mX`u7q z@wle&W*3}s2;-ETjw?ss&8KslVUJ`AhZ07ngzbsn8`crG9~qV}4)5T(Oi=^!p4&t1 z_4{^6unw3MP0D7)eq~1jJGaOMlD(ip$2o=RVr+xoS0^dkw0k0&v~3aXWX&Wrr)-xs z6Umj!E*0gCbaUk1sK&^as8(4UyHvaChkTF6$2vBA7j zN!2EAk8YaK9NjjdomfHg_KABYHBW4tbiE&0`}Z&sS@THx+aCG;kiDu7-5z#t=$?@M zp&e|Su02w>rYOE6&52Imx%yX$+4wnlnZK8x{kuPanI=sb6X!3xEZXf+F`6f!>q$zAeM=K6hG+NbX=>w;d z*A$Dzr;sjJZ~jRqnRT4O+h~=a2M?41C81;sqP!^mbI;HHt6`kPIwclQlkWbal@%zN z+9!LQIv{(ZDdyFp)iBOD?z7-H&-SN@%i<=6CDlfUaJA9uWwmX!bS^z}yY+jIrVc0E zUA)XvxJ4}H1ig9Mu@>-3P<7aRRycbiTJZHVwrE(l`mWLf*ht{tx#5Vsm4}>jSBZ{3 zCrG1D2kG+#uJh7RB^(w$OI)H$Hj65H$>0O7c<3p@g7}NlCLOE ztSbIfk&fG9i5K&xsl->*w^*Ljskt!ji=sD+-euNV?3QO(d25@K&fR6b=mBcjUy%5D z#TE9hVmkNP1$_F?o(WGxwM4gS9#=Fd8zbAp+QM73tr1V4guJqe&;4rgip6x^b2?_P zA}N;psR-@WjNT92V|lhXj?*Q6Q?#P^E(_D~8y&Mx(V;vLYdIj?V_^>5qthICPPbJT z#xai)_DxP@#?Ou!KfKTU2dO)`kDW7-= zv_!=LfUP;4Q|`kY)2;>Td7+=#uQ(9ZJmK-kJ<)`nXVn*~ z)erw>)vN1%xNFArpXNQiwLEOWZc*NT?ELn4-G|<#WS`Np+F}alCUP{aB;9|^^5E*I3rM974O{`vsIVKrA)#5cbc~ciM82kJUyZ|JA_;la`VFsTwHBRHqT85 z2#y4vL5C|2u@T&{Y)vhz#?zSEBROmco)!~^r!ciqwM^y}56>NeJ#=!;B$|s9L`PRE#>8t-W%*=HhqD&(E` zeM{ns;_8dYzQF+4A=*xjLsO~;KiCm7N(%*@i3eYZ9Sr$5a-cXuF&YFZLM{(M@g_fmgBDZ8I4QrT5%2ko~B})lj z{Jt0|R~>KJiZFNIQt}=p%J`t@&6*Weag!lBE~S-F>>Bw?^vDZp{`VWg0F@NOx`QBJB-y zK6)G_$N>h~9>4FjpY**pAmg@iPWO}6+j>!Y#6=zSCp(6Czwa%-tS!qX+92e4);YBG|FQx*uJSH85>rRRJ5a$;tzlAICAh^0rX$WE$#aUL9!Ja)Zsva7vI<>}3YAF*0<{ zDGTm-F=onwAkmJ~uxf1IB;#AGt|E(-_!E!7{As~QN4`9?34u05RSF=*Wa}PWyW6S< zPm#hyan77(m$hEwh_VPS;??PqR1O}vN$w#Y!BKw>4L~=Q7p?eA@$Jalm(5(669$D1v^YXDYv{Bort4PD)dO4ubz$G^9Er?U8rz z2XIX%2(P^pNDCn}MfMU4K)%IuG^|^=$?x;{jrLJ$``jOY?ddar?6s#Jf6TR~ZhsCp z+QOm0BnReTkA|nf13lr|OvZ{6U?!s+ajqM{V#DnnR;DKhq=V#pO=3R6T%i+B`JsP0 zjTycozJ={D2O8B=-jtz)^nRya2h@22hs%VosFOv^FxF z*IRTJg#~AYfaRg~Oy9KAs`TKTC`&Y72s5^47`14;sZ&Qxg|##<_u@P{S1l{zwZf9Ra;z2fb&*ueCatiPVug24(00lTbshIaKTyxr)L7bKac#bECt{c1Ie6xuVSxq?+nhI2fpE3W^z<|75Ldp z-8g+M`28eb!0$Jq6rKB3fKK!ja?5gcb#--xfbkf>>O8QC3pY*Y2!=H>R&brL6~lG6V|VG87ct38T3b4#Y9Cs|1bv>dp5Lyix9ZQPantTvmV-^x zmPgf^r$l&PT{E;MN(ciRG~g5duDsmy>KgFXoHb8@Q_FxS$+z2&Qk3p{95Hp^QxEcj ze+2Y=pmw=G;CplgxCuNvFZ)T^zW{dxeD{Ml+wbdvFUYgEZORA852WL+7Xkf4G#$iQVgopUoXTaQ{ zp3J`LdQA(_f~sAz@XJ+Zj)7Kbzp;;oGHHP?yqZ=`xE%a4Y+rH^Z^#qXhxn`@Z}@#h z!zPfJ1V=N5NAt*Y{xFfK`(f?~XL6g(?^CJzY%v!$J988N4C>pfdPizGw8kW%}ry@QgMYA;M=4@HkWaHX%Z& zbkW8ZEbFeW*F$@hx4OO=`Z>HQ1jcg0d!ILnGv_1YiCZ-7R2u8iE1JToCRtj9hfooJ zmC*3Bj$^^KN7Hf$yxLU9X;X_ZrIugW3_g{dS{IKf)}nBya++tE+7wO!-!ew3>p8`o z?(L4)+S%UFbgQSb4sc;zT=edV%32~@s_U8H9K%Tl!@2&y)eMK4tO9p$fSNL1;U)C~ zz8{Bx20erVQvIB_s{SsAjE}NZyCk0h;EL+~$JYCX##&c+sXW>P_Vx(q9I&=v2&Fi< zDT!k^TtGF3r=atH?i=yxBrO=(E8)0JJ%p27ffIo;JP9Y`yYM>vOMDEU#TY2&tw1^K z=%9=50<{r20QhvD=}XBs*HVUC`$&n-fE`N6FOu&yWY@{CrmxuXP61AYdgtiGl>FQ} z=EyOt#vSlY9V9ba;K&cosIjWLYD#TGU%;0>sA{iTP2>wYU*9M57-1g^o!{O^XCk+& z4<%wlC6TDG#@#hkRS##dAPS<|mBPsZ>uZOVd-DL2lzhMM?m%uGRfyBk`Bi<YPc9R~CNdzw^9WU9H9YpeC!($-H!VSjC>Po{pMMrifx=8m)zCxhq zkls;B{0T5I8V^$KB0Vej_`C|Rg%x|hgpnwLzm9qT3Qr7->5CvP>Vmy=*bfBvh)CNi z)6-A_Jh`53uXi=f!&L&!@?g2W_I=mOsZEVu1<>Jtz&St)`qvn42b=|bYp)W_!M@*6 zC$yshX=K7Sy=I`<7X{ASDrdSW zSf#)=HUxYRT&=3#43=bN-4e*BgZTm9ddR&O^jpwtQel_e7zO@+B1<-UFDN&3%c0eM z0Nvd^hz~Pp0h*9>t!FjdL(^BeRq*vu_^RyD7_b?6!VE-G+&pw!5HDW`2=1BnbR0nh9Kh}cBGhl>WH!_lFj-0yS1e1H0HS8RQof7QXW>uXt zmuAg1+($DAv@qc3K0k=}tx(cpD@sF>^yC+Ht5qV~x4K6@j5PS(2nMw#bVR(u}q zpBQ79n^K2U$a%9v!)rEyR}xn3fN$O4=ESE=#2PqV z?ELzsAS3*?ev}`^_~M_}4+t5O2%pqPA;d$x65%6K_MPY99o8JOwv6!*X&3g%dD=k1 zT9ywdT2Uui$y@X|cX6Bwe9%|nEReYqnVPK%_aByhungyB#0@HHsy(vS9F;~% zay6=IXLz!iE3o8yWo;-;<1uq1zHf)kTsZh;N8ogetetmOJCnTNPvyav+%TPsQQ*g4 zE0(pIIh{pIY<&T{u_0V|-m2)uEtSPuK__gD!412M>6Y`#at0Tug{2JLfKon(eXFWs zgR{z65VT|woHs_sa9b4j4vRL0a9N-cVRC-Kh`~GeqX(iZc(oxuow;y{jog%(rgWDG z<^mT>_Mmu>3_$I~js!o2{hm8(`3-mT<%`s)d>$%a1Y1)4`VE!qHmq0z_K_<6#7 zHJW$J{9wUb0PLFe)nagrV>#t3*00#WudKZ1jtx*!7%f?(Mu|7y%HJ}3E@^XV;__R0 zGJe2HdHK9@lv1{^v@{hq__NKS3YGJb61+l2Q&^5@9^h=5ywln^d+ORh4xjFPy0`Po zrElIc?YoHY7C*IZR#W$F+_9Etl;`l$?UAoPoSksyfsln8Zhd`S;DtZCeyzCBzTvy} zH{Pge|NN0TuYdbi#~a}v91KagJLAjDXCB&Mzi{;UvYz^@8_O5m&~oQn*+*7;CXuNODY5aBfg6j7at5Q>=A6CDdTwsXF8o4uLZd7Q&O*gMsuXt6xdSlkRZ$9{N z#=541e>ru?u~L7ec9-ML!wc?sTlbeHPlBTLPY+KC`?2=P)!K<~Jf&mqm>O4lZ%K0J zYsd3m4B@8PpE*}H@4fv?f4DnK{ASxPH{4zQbX|1aI}b6`+q;LP*BidNWByaV!&hJb zzHaSX$EdlokEu-TkaePCXrb(VY6?y#L8`cux(Po=X-9uiT1?Be6BJ5qB%`8aYE49R zNN89%(h^!C>Bh3ze}&us$R9}IfA}?jvSsfhqUp5>`J^+4tm9vO8jy z-|?)pvsH8OE`L|gS52#(S)b1~HE-PdhxIqq6g*UXrT%g8Ia>JDcS7SE%G#fJ&CD0q z`@(i^`5`&+i|x%<77GfQ6D*FN*>;ujY`xi$0-{H&qw*FER7TcZ0aJ~+Sa zQp4*dZ+}{?J9gsq#vgzE!M%o_>h^orzL048P5WMEm%i`^ZR%amdA8j4;@w+bZu<6} znJ<5C%WQ30_uI)=-g$8K3*IMXuTA<>&;9ou$$kA1XKDQNdB1+U+WGvW2f3Te2DZ-h zOq}@jy~B^c?#uWob=B$iy62<5@y1X|SA3V=-JdHRJN@pwTlT)0vi59|E~5O>(TBP# zkMtd#{!-Ig)7$ShsT<9Czy0K~g}?ju>Y`sK-}Ply!E2?%t(%{l^5WBfUby_y66e83 zADplveAUSZ7QKJNu6)p_t?3EMh$mJbjm_UU{B+^-@d?v5w9j3#|GkY}fBB=+suMai z$uFJV>R|7-oOx&N9MvFvYo zp$q@^Jl4*C_5N=a|GydhVRHaEfKRycAYDe||8If!@%aDo_Mo( z{Qr3T|9Jd=g)kofKOX;Yfw<`L`2X?ve-g*>e;ogRABi*j-^Ble-SyuV{||jH2iR6Y zS`CTB|BrxObUpqb^0q!9{)cc|4(A9#^e9TbIMo({QrMT{6FyK_4t2y{)hN~cn`+^1C4$_;{V~D(CYR0e|TRFX*~Xa^l!A(!t(QQCkH`Oy$N!JV|BuK2kH`Oy$N!JV|BuK2kH`Nzipbw$7?1ylD8upi z|Nno+|AU|6-!}dqVlh>VApRdxBBTkB#^e9TV_)3A;`S}-O*5Vy>`$z@zuz2Ki@x~H+1vhl z>o-R>Z^`iwcD=EZyJLoDalf#1%Jb*#8>a0@-)?mRQo^q z{inTnU(zV4Qd-+ZBX@3Gfx zg>x4_aq(=;ok!l^*zoDI?&DX!p=YWOF0OREyz#&qNAd68{;+NmgPwo<{YjIZvNw<3 z8^80ej~3njDgR~YTXS~ZxAb&!Y*WKci#D!2vU1O^qz~3VmVIgZ6LUi^*cVjqVGCYM z4;MbOB}KjQY{|<~)1epCt2W~ER~0#{K5$L>bLiy#H3#dHd+wVs{m1D^0YldPWqT*4 ze(JSh)28^{lOKAzXX2i9THYD<^SXrfs*Wp$tHMp%`Ri}k@TW`7Ax+*DbG|Z_zc%re z@|C|Yy&Qk((K5WS@?J6S+ZFhg?@v6q_b67Ko%8EYUp;W6ru?g~4m7(SzNNW)SN8As z&3Gj7)bH0Vb3U5*N^eiJtGp5Zht9_MdsjR(C-vzA&+L9L!m#_hyTSUWbTZ@msZjXo7?-=r*%I6 z+$m0m)Pc-!>!kXR!v165dmA1<@c4vzRovH`HE%vRjeU1f&0BMxd1T=e3-TLJWE!iB z|Jplfwf>%2&3*8fi;HHy{vEUO)7_ib(i0N0e*5g?XFqI7{AR}Gx4!;jZTCK5Nye-# zrw($JE9+~NAH*eCKG#ou^0sAX?pxxFe&G#e?^<*C;=S{3OUvdoM>7kTe_Qj1 c(l=I4ADr&3 + + for test in $TESTS; do + + infile=${DATADIR}/${test}.unsigned + + for h in $HASHES; do + + pemfile=${TESTKEYS}/key_rsa${s}.pem + outfile=${TMP}.${test}_${s}_${h}.new + + # sign it + ${FUTILITY} sign --type usbpd1 --pem ${pemfile} ${infile} ${outfile} + + # make sure it identifies correctly + ${FUTILITY} verify ${outfile} + + done + done +done + +# cleanup +rm -rf ${TMP}* +exit 0