12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715 |
- /*
- * testcrypt: a standalone test program that provides direct access to
- * PuTTY's cryptography and mp_int code.
- */
- /*
- * This program speaks a line-oriented protocol on standard input and
- * standard output. It's a half-duplex protocol: it expects to read
- * one line of command, and then produce a fixed amount of output
- * (namely a line containing a decimal integer, followed by that many
- * lines each containing one return value).
- *
- * The protocol is human-readable enough to make it debuggable, but
- * verbose enough that you probably wouldn't want to speak it by hand
- * at any great length. The Python program test/testcrypt.py wraps it
- * to give a more useful user-facing API, by invoking this binary as a
- * subprocess.
- *
- * (I decided that was a better idea than making this program an
- * actual Python module, partly because you can rewrap the same binary
- * in another scripting language if you prefer, but mostly because
- * it's easy to attach a debugger to testcrypt or to run it under
- * sanitisers or valgrind or what have you.)
- */
- #include <assert.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <errno.h>
- #include "defs.h"
- #include "ssh.h"
- #include "sshkeygen.h"
- #include "misc.h"
- #include "mpint.h"
- #include "crypto/ecc.h"
- #include "crypto/ntru.h"
- #include "proxy/cproxy.h"
- static NORETURN PRINTF_LIKE(1, 2) void fatal_error(const char *p, ...)
- {
- va_list ap;
- fprintf(stderr, "testcrypt: ");
- va_start(ap, p);
- vfprintf(stderr, p, ap);
- va_end(ap);
- fputc('\n', stderr);
- exit(1);
- }
- void out_of_memory(void) { fatal_error("out of memory"); }
- static bool old_keyfile_warning_given;
- void old_keyfile_warning(void) { old_keyfile_warning_given = true; }
- static bufchain random_data_queue;
- static prng *test_prng;
- void random_read(void *buf, size_t size)
- {
- if (test_prng) {
- prng_read(test_prng, buf, size);
- } else {
- if (!bufchain_try_fetch_consume(&random_data_queue, buf, size))
- fatal_error("No random data in queue");
- }
- }
- uint64_t prng_reseed_time_ms(void)
- {
- static uint64_t previous_time = 0;
- return previous_time += 200;
- }
- #define VALUE_TYPES(X) \
- X(string, strbuf *, strbuf_free(v)) \
- X(mpint, mp_int *, mp_free(v)) \
- X(modsqrt, ModsqrtContext *, modsqrt_free(v)) \
- X(monty, MontyContext *, monty_free(v)) \
- X(wcurve, WeierstrassCurve *, ecc_weierstrass_curve_free(v)) \
- X(wpoint, WeierstrassPoint *, ecc_weierstrass_point_free(v)) \
- X(mcurve, MontgomeryCurve *, ecc_montgomery_curve_free(v)) \
- X(mpoint, MontgomeryPoint *, ecc_montgomery_point_free(v)) \
- X(ecurve, EdwardsCurve *, ecc_edwards_curve_free(v)) \
- X(epoint, EdwardsPoint *, ecc_edwards_point_free(v)) \
- X(hash, ssh_hash *, ssh_hash_free(v)) \
- X(key, ssh_key *, ssh_key_free(v)) \
- X(cipher, ssh_cipher *, ssh_cipher_free(v)) \
- X(mac, ssh2_mac *, ssh2_mac_free(v)) \
- X(dh, dh_ctx *, dh_cleanup(v)) \
- X(ecdh, ecdh_key *, ecdh_key_free(v)) \
- X(rsakex, RSAKey *, ssh_rsakex_freekey(v)) \
- X(rsa, RSAKey *, rsa_free(v)) \
- X(prng, prng *, prng_free(v)) \
- X(keycomponents, key_components *, key_components_free(v)) \
- X(pcs, PrimeCandidateSource *, pcs_free(v)) \
- X(pgc, PrimeGenerationContext *, primegen_free_context(v)) \
- X(pockle, Pockle *, pockle_free(v)) \
- X(millerrabin, MillerRabin *, miller_rabin_free(v)) \
- X(ntrukeypair, NTRUKeyPair *, ntru_keypair_free(v)) \
- X(ntruencodeschedule, NTRUEncodeSchedule *, ntru_encode_schedule_free(v)) \
- /* end of list */
- typedef struct Value Value;
- enum ValueType {
- #define VALTYPE_ENUM(n,t,f) VT_##n,
- VALUE_TYPES(VALTYPE_ENUM)
- #undef VALTYPE_ENUM
- };
- typedef enum ValueType ValueType;
- static const char *const type_names[] = {
- #define VALTYPE_NAME(n,t,f) #n,
- VALUE_TYPES(VALTYPE_NAME)
- #undef VALTYPE_NAME
- };
- #define VALTYPE_TYPEDEF(n,t,f) \
- typedef t TD_val_##n; \
- typedef t *TD_out_val_##n;
- VALUE_TYPES(VALTYPE_TYPEDEF)
- #undef VALTYPE_TYPEDEF
- struct Value {
- /*
- * Protocol identifier assigned to this value when it was created.
- * Lives in the same malloced block as this Value object itself.
- */
- ptrlen id;
- /*
- * Type of the value.
- */
- ValueType type;
- /*
- * Union of all the things it could hold.
- */
- union {
- #define VALTYPE_UNION(n,t,f) t vu_##n;
- VALUE_TYPES(VALTYPE_UNION)
- #undef VALTYPE_UNION
- char *bare_string;
- };
- };
- static int valuecmp(void *av, void *bv)
- {
- Value *a = (Value *)av, *b = (Value *)bv;
- return ptrlen_strcmp(a->id, b->id);
- }
- static int valuefind(void *av, void *bv)
- {
- ptrlen *a = (ptrlen *)av;
- Value *b = (Value *)bv;
- return ptrlen_strcmp(*a, b->id);
- }
- static tree234 *values;
- static Value *value_new(ValueType vt)
- {
- static uint64_t next_index = 0;
- char *name = dupprintf("%s%"PRIu64, type_names[vt], next_index++);
- size_t namelen = strlen(name);
- Value *val = snew_plus(Value, namelen+1);
- memcpy(snew_plus_get_aux(val), name, namelen+1);
- val->id.ptr = snew_plus_get_aux(val);
- val->id.len = namelen;
- val->type = vt;
- Value *added = add234(values, val);
- assert(added == val);
- sfree(name);
- return val;
- }
- #define VALTYPE_RETURNFN(n,t,f) \
- void return_val_##n(strbuf *out, t v) { \
- Value *val = value_new(VT_##n); \
- val->vu_##n = v; \
- put_datapl(out, val->id); \
- put_byte(out, '\n'); \
- }
- VALUE_TYPES(VALTYPE_RETURNFN)
- #undef VALTYPE_RETURNFN
- static ptrlen get_word(BinarySource *in)
- {
- ptrlen toret;
- toret.ptr = get_ptr(in);
- toret.len = 0;
- while (get_avail(in) && get_byte(in) != ' ')
- toret.len++;
- return toret;
- }
- typedef uintmax_t TD_uint;
- typedef bool TD_boolean;
- typedef ptrlen TD_val_string_ptrlen;
- typedef char *TD_val_string_asciz;
- typedef BinarySource *TD_val_string_binarysource;
- typedef unsigned *TD_out_uint;
- typedef BinarySink *TD_out_val_string_binarysink;
- typedef const char *TD_opt_val_string_asciz;
- typedef char **TD_out_val_string_asciz;
- typedef char **TD_out_opt_val_string_asciz;
- typedef const char **TD_out_opt_val_string_asciz_const;
- typedef const ssh_hashalg *TD_hashalg;
- typedef const ssh2_macalg *TD_macalg;
- typedef const ssh_keyalg *TD_keyalg;
- typedef const ssh_cipheralg *TD_cipheralg;
- typedef const ssh_kex *TD_dh_group;
- typedef const ssh_kex *TD_ecdh_alg;
- typedef RsaSsh1Order TD_rsaorder;
- typedef key_components *TD_keycomponents;
- typedef const PrimeGenerationPolicy *TD_primegenpolicy;
- typedef struct mpint_list TD_mpint_list;
- typedef struct int16_list *TD_int16_list;
- typedef PockleStatus TD_pocklestatus;
- typedef struct mr_result TD_mr_result;
- typedef Argon2Flavour TD_argon2flavour;
- typedef FingerprintType TD_fptype;
- typedef HttpDigestHash TD_httpdigesthash;
- #define BEGIN_ENUM_TYPE(name) \
- static bool enum_translate_##name(ptrlen valname, TD_##name *out) { \
- static const struct { \
- const char *key; \
- TD_##name value; \
- } mapping[] = {
- #define ENUM_VALUE(name, value) {name, value},
- #define END_ENUM_TYPE(name) \
- }; \
- for (size_t i = 0; i < lenof(mapping); i++) \
- if (ptrlen_eq_string(valname, mapping[i].key)) { \
- if (out) \
- *out = mapping[i].value; \
- return true; \
- } \
- return false; \
- } \
- \
- static TD_##name get_##name(BinarySource *in) { \
- ptrlen valname = get_word(in); \
- TD_##name out; \
- if (enum_translate_##name(valname, &out)) \
- return out; \
- else \
- fatal_error("%s '%.*s': not found", \
- #name, PTRLEN_PRINTF(valname)); \
- }
- #include "testcrypt-enum.h"
- #undef BEGIN_ENUM_TYPE
- #undef ENUM_VALUE
- #undef END_ENUM_TYPE
- static uintmax_t get_uint(BinarySource *in)
- {
- ptrlen word = get_word(in);
- char *string = mkstr(word);
- uintmax_t toret = strtoumax(string, NULL, 0);
- sfree(string);
- return toret;
- }
- static bool get_boolean(BinarySource *in)
- {
- return ptrlen_eq_string(get_word(in), "true");
- }
- static Value *lookup_value(ptrlen word)
- {
- Value *val = find234(values, &word, valuefind);
- if (!val)
- fatal_error("id '%.*s': not found", PTRLEN_PRINTF(word));
- return val;
- }
- static Value *get_value(BinarySource *in)
- {
- return lookup_value(get_word(in));
- }
- typedef void (*finaliser_fn_t)(strbuf *out, void *ctx);
- struct finaliser {
- finaliser_fn_t fn;
- void *ctx;
- };
- static struct finaliser *finalisers;
- static size_t nfinalisers, finalisersize;
- static void add_finaliser(finaliser_fn_t fn, void *ctx)
- {
- sgrowarray(finalisers, finalisersize, nfinalisers);
- finalisers[nfinalisers].fn = fn;
- finalisers[nfinalisers].ctx = ctx;
- nfinalisers++;
- }
- static void run_finalisers(strbuf *out)
- {
- for (size_t i = 0; i < nfinalisers; i++)
- finalisers[i].fn(out, finalisers[i].ctx);
- nfinalisers = 0;
- }
- static void finaliser_return_value(strbuf *out, void *ctx)
- {
- Value *val = (Value *)ctx;
- put_datapl(out, val->id);
- put_byte(out, '\n');
- }
- static void finaliser_sfree(strbuf *out, void *ctx)
- {
- sfree(ctx);
- }
- #define VALTYPE_GETFN(n,t,f) \
- static Value *unwrap_value_##n(Value *val) { \
- ValueType expected = VT_##n; \
- if (expected != val->type) \
- fatal_error("id '%.*s': expected %s, got %s", \
- PTRLEN_PRINTF(val->id), \
- type_names[expected], type_names[val->type]); \
- return val; \
- } \
- static Value *get_value_##n(BinarySource *in) { \
- return unwrap_value_##n(get_value(in)); \
- } \
- static t get_val_##n(BinarySource *in) { \
- return get_value_##n(in)->vu_##n; \
- }
- VALUE_TYPES(VALTYPE_GETFN)
- #undef VALTYPE_GETFN
- static ptrlen get_val_string_ptrlen(BinarySource *in)
- {
- return ptrlen_from_strbuf(get_val_string(in));
- }
- static char *get_val_string_asciz(BinarySource *in)
- {
- return get_val_string(in)->s;
- }
- static strbuf *get_opt_val_string(BinarySource *in);
- static char *get_opt_val_string_asciz(BinarySource *in)
- {
- strbuf *sb = get_opt_val_string(in);
- return sb ? sb->s : NULL;
- }
- static mp_int **get_out_val_mpint(BinarySource *in)
- {
- Value *val = value_new(VT_mpint);
- add_finaliser(finaliser_return_value, val);
- return &val->vu_mpint;
- }
- struct mpint_list {
- size_t n;
- mp_int **integers;
- };
- static struct mpint_list get_mpint_list(BinarySource *in)
- {
- size_t n = get_uint(in);
- struct mpint_list mpl;
- mpl.n = n;
- mpl.integers = snewn(n, mp_int *);
- for (size_t i = 0; i < n; i++)
- mpl.integers[i] = get_val_mpint(in);
- add_finaliser(finaliser_sfree, mpl.integers);
- return mpl;
- }
- typedef struct int16_list {
- size_t n;
- uint16_t *integers;
- } int16_list;
- static void finaliser_int16_list_free(strbuf *out, void *vlist)
- {
- int16_list *list = (int16_list *)vlist;
- sfree(list->integers);
- sfree(list);
- }
- static int16_list *make_int16_list(size_t n)
- {
- int16_list *list = snew(int16_list);
- list->n = n;
- list->integers = snewn(n, uint16_t);
- add_finaliser(finaliser_int16_list_free, list);
- return list;
- }
- static int16_list *get_int16_list(BinarySource *in)
- {
- size_t n = get_uint(in);
- int16_list *list = make_int16_list(n);
- for (size_t i = 0; i < n; i++)
- list->integers[i] = get_uint(in);
- return list;
- }
- static void return_int16_list(strbuf *out, int16_list *list)
- {
- for (size_t i = 0; i < list->n; i++) {
- if (i > 0)
- put_byte(out, ',');
- put_fmt(out, "%d", (int)(int16_t)list->integers[i]);
- }
- put_byte(out, '\n');
- }
- static void finaliser_return_uint(strbuf *out, void *ctx)
- {
- unsigned *uval = (unsigned *)ctx;
- put_fmt(out, "%u\n", *uval);
- sfree(uval);
- }
- static unsigned *get_out_uint(BinarySource *in)
- {
- unsigned *uval = snew(unsigned);
- add_finaliser(finaliser_return_uint, uval);
- return uval;
- }
- static BinarySink *get_out_val_string_binarysink(BinarySource *in)
- {
- Value *val = value_new(VT_string);
- val->vu_string = strbuf_new();
- add_finaliser(finaliser_return_value, val);
- return BinarySink_UPCAST(val->vu_string);
- }
- static void return_val_string_asciz_const(strbuf *out, const char *s);
- static void return_val_string_asciz(strbuf *out, char *s);
- static void finaliser_return_opt_string_asciz(strbuf *out, void *ctx)
- {
- char **valp = (char **)ctx;
- char *val = *valp;
- sfree(valp);
- if (!val)
- put_fmt(out, "NULL\n");
- else
- return_val_string_asciz(out, val);
- }
- static char **get_out_opt_val_string_asciz(BinarySource *in)
- {
- char **valp = snew(char *);
- *valp = NULL;
- add_finaliser(finaliser_return_opt_string_asciz, valp);
- return valp;
- }
- static void finaliser_return_opt_string_asciz_const(strbuf *out, void *ctx)
- {
- const char **valp = (const char **)ctx;
- const char *val = *valp;
- sfree(valp);
- if (!val)
- put_fmt(out, "NULL\n");
- else
- return_val_string_asciz_const(out, val);
- }
- static const char **get_out_opt_val_string_asciz_const(BinarySource *in)
- {
- const char **valp = snew(const char *);
- *valp = NULL;
- add_finaliser(finaliser_return_opt_string_asciz_const, valp);
- return valp;
- }
- static BinarySource *get_val_string_binarysource(BinarySource *in)
- {
- strbuf *sb = get_val_string(in);
- BinarySource *src = snew(BinarySource);
- BinarySource_BARE_INIT(src, sb->u, sb->len);
- add_finaliser(finaliser_sfree, src);
- return src;
- }
- #define GET_CONSUMED_FN(type) \
- typedef TD_val_##type TD_consumed_val_##type; \
- static TD_val_##type get_consumed_val_##type(BinarySource *in) \
- { \
- Value *val = get_value_##type(in); \
- TD_val_##type toret = val->vu_##type; \
- del234(values, val); \
- sfree(val); \
- return toret; \
- }
- GET_CONSUMED_FN(hash)
- GET_CONSUMED_FN(pcs)
- static void return_int(strbuf *out, intmax_t u)
- {
- put_fmt(out, "%"PRIdMAX"\n", u);
- }
- static void return_uint(strbuf *out, uintmax_t u)
- {
- put_fmt(out, "0x%"PRIXMAX"\n", u);
- }
- static void return_boolean(strbuf *out, bool b)
- {
- put_fmt(out, "%s\n", b ? "true" : "false");
- }
- static void return_pocklestatus(strbuf *out, PockleStatus status)
- {
- switch (status) {
- default:
- put_fmt(out, "POCKLE_BAD_STATUS_VALUE\n");
- break;
- #define STATUS_CASE(id) \
- case id: \
- put_fmt(out, "%s\n", #id); \
- break;
- POCKLE_STATUSES(STATUS_CASE);
- #undef STATUS_CASE
- }
- }
- static void return_mr_result(strbuf *out, struct mr_result result)
- {
- if (!result.passed)
- put_fmt(out, "failed\n");
- else if (!result.potential_primitive_root)
- put_fmt(out, "passed\n");
- else
- put_fmt(out, "passed+ppr\n");
- }
- static void return_val_string_asciz_const(strbuf *out, const char *s)
- {
- strbuf *sb = strbuf_new();
- put_data(sb, s, strlen(s));
- return_val_string(out, sb);
- }
- static void return_val_string_asciz(strbuf *out, char *s)
- {
- return_val_string_asciz_const(out, s);
- sfree(s);
- }
- #define NULLABLE_RETURN_WRAPPER(type_name, c_type) \
- static void return_opt_##type_name(strbuf *out, c_type ptr) \
- { \
- if (!ptr) \
- put_fmt(out, "NULL\n"); \
- else \
- return_##type_name(out, ptr); \
- }
- NULLABLE_RETURN_WRAPPER(val_string, strbuf *)
- NULLABLE_RETURN_WRAPPER(val_string_asciz, char *)
- NULLABLE_RETURN_WRAPPER(val_string_asciz_const, const char *)
- NULLABLE_RETURN_WRAPPER(val_cipher, ssh_cipher *)
- NULLABLE_RETURN_WRAPPER(val_mac, ssh2_mac *)
- NULLABLE_RETURN_WRAPPER(val_hash, ssh_hash *)
- NULLABLE_RETURN_WRAPPER(val_key, ssh_key *)
- NULLABLE_RETURN_WRAPPER(val_mpint, mp_int *)
- NULLABLE_RETURN_WRAPPER(int16_list, int16_list *)
- static void handle_hello(BinarySource *in, strbuf *out)
- {
- put_fmt(out, "hello, world\n");
- }
- static void rsa_free(RSAKey *rsa)
- {
- freersakey(rsa);
- sfree(rsa);
- }
- static void free_value(Value *val)
- {
- switch (val->type) {
- #define VALTYPE_FREE(n,t,f) case VT_##n: { t v = val->vu_##n; (f); break; }
- VALUE_TYPES(VALTYPE_FREE)
- #undef VALTYPE_FREE
- }
- sfree(val);
- }
- static void handle_free(BinarySource *in, strbuf *out)
- {
- Value *val = get_value(in);
- del234(values, val);
- free_value(val);
- }
- static void handle_newstring(BinarySource *in, strbuf *out)
- {
- strbuf *sb = strbuf_new();
- while (get_avail(in)) {
- char c = get_byte(in);
- if (c == '%') {
- char hex[3];
- hex[0] = get_byte(in);
- if (hex[0] != '%') {
- hex[1] = get_byte(in);
- hex[2] = '\0';
- c = strtoul(hex, NULL, 16);
- }
- }
- put_byte(sb, c);
- }
- return_val_string(out, sb);
- }
- static void handle_getstring(BinarySource *in, strbuf *out)
- {
- strbuf *sb = get_val_string(in);
- for (size_t i = 0; i < sb->len; i++) {
- char c = sb->s[i];
- if (c > ' ' && c < 0x7F && c != '%') {
- put_byte(out, c);
- } else {
- put_fmt(out, "%%%02X", 0xFFU & (unsigned)c);
- }
- }
- put_byte(out, '\n');
- }
- static void handle_mp_literal(BinarySource *in, strbuf *out)
- {
- ptrlen pl = get_word(in);
- char *str = mkstr(pl);
- mp_int *mp = mp__from_string_literal(str);
- sfree(str);
- return_val_mpint(out, mp);
- }
- static void handle_mp_dump(BinarySource *in, strbuf *out)
- {
- mp_int *mp = get_val_mpint(in);
- for (size_t i = mp_max_bytes(mp); i-- > 0 ;)
- put_fmt(out, "%02X", mp_get_byte(mp, i));
- put_byte(out, '\n');
- }
- static void handle_checkenum(BinarySource *in, strbuf *out)
- {
- ptrlen type = get_word(in);
- ptrlen value = get_word(in);
- bool ok = false;
- #define BEGIN_ENUM_TYPE(name) \
- if (ptrlen_eq_string(type, #name)) \
- ok = enum_translate_##name(value, NULL);
- #define ENUM_VALUE(name, value)
- #define END_ENUM_TYPE(name)
- #include "testcrypt-enum.h"
- #undef BEGIN_ENUM_TYPE
- #undef ENUM_VALUE
- #undef END_ENUM_TYPE
- put_dataz(out, ok ? "ok\n" : "bad\n");
- }
- static void random_queue(ptrlen pl)
- {
- bufchain_add(&random_data_queue, pl.ptr, pl.len);
- }
- static size_t random_queue_len(void)
- {
- return bufchain_size(&random_data_queue);
- }
- static void random_clear(void)
- {
- if (test_prng) {
- prng_free(test_prng);
- test_prng = NULL;
- }
- bufchain_clear(&random_data_queue);
- }
- static void random_make_prng(const ssh_hashalg *hashalg, ptrlen seed)
- {
- random_clear();
- test_prng = prng_new(hashalg);
- prng_seed_begin(test_prng);
- put_datapl(test_prng, seed);
- prng_seed_finish(test_prng);
- }
- mp_int *monty_identity_wrapper(MontyContext *mc)
- {
- return mp_copy(monty_identity(mc));
- }
- mp_int *monty_modulus_wrapper(MontyContext *mc)
- {
- return mp_copy(monty_modulus(mc));
- }
- strbuf *ssh_hash_digest_wrapper(ssh_hash *h)
- {
- strbuf *sb = strbuf_new();
- void *p = strbuf_append(sb, ssh_hash_alg(h)->hlen);
- ssh_hash_digest(h, p);
- return sb;
- }
- strbuf *ssh_hash_final_wrapper(ssh_hash *h)
- {
- strbuf *sb = strbuf_new();
- void *p = strbuf_append(sb, ssh_hash_alg(h)->hlen);
- ssh_hash_final(h, p);
- return sb;
- }
- void ssh_cipher_setiv_wrapper(ssh_cipher *c, ptrlen iv)
- {
- if (iv.len != ssh_cipher_alg(c)->blksize)
- fatal_error("ssh_cipher_setiv: needs exactly %d bytes",
- ssh_cipher_alg(c)->blksize);
- ssh_cipher_setiv(c, iv.ptr);
- }
- void ssh_cipher_setkey_wrapper(ssh_cipher *c, ptrlen key)
- {
- if (key.len != ssh_cipher_alg(c)->padded_keybytes)
- fatal_error("ssh_cipher_setkey: needs exactly %d bytes",
- ssh_cipher_alg(c)->padded_keybytes);
- ssh_cipher_setkey(c, key.ptr);
- }
- strbuf *ssh_cipher_encrypt_wrapper(ssh_cipher *c, ptrlen input)
- {
- if (input.len % ssh_cipher_alg(c)->blksize)
- fatal_error("ssh_cipher_encrypt: needs a multiple of %d bytes",
- ssh_cipher_alg(c)->blksize);
- strbuf *sb = strbuf_dup(input);
- ssh_cipher_encrypt(c, sb->u, sb->len);
- return sb;
- }
- strbuf *ssh_cipher_decrypt_wrapper(ssh_cipher *c, ptrlen input)
- {
- if (input.len % ssh_cipher_alg(c)->blksize)
- fatal_error("ssh_cipher_decrypt: needs a multiple of %d bytes",
- ssh_cipher_alg(c)->blksize);
- strbuf *sb = strbuf_dup(input);
- ssh_cipher_decrypt(c, sb->u, sb->len);
- return sb;
- }
- strbuf *ssh_cipher_encrypt_length_wrapper(ssh_cipher *c, ptrlen input,
- unsigned long seq)
- {
- if (input.len != 4)
- fatal_error("ssh_cipher_encrypt_length: needs exactly 4 bytes");
- strbuf *sb = strbuf_dup(input);
- ssh_cipher_encrypt_length(c, sb->u, sb->len, seq);
- return sb;
- }
- strbuf *ssh_cipher_decrypt_length_wrapper(ssh_cipher *c, ptrlen input,
- unsigned long seq)
- {
- if (input.len != 4)
- fatal_error("ssh_cipher_decrypt_length: needs exactly 4 bytes");
- strbuf *sb = strbuf_dup(input);
- ssh_cipher_decrypt_length(c, sb->u, sb->len, seq);
- return sb;
- }
- strbuf *ssh2_mac_genresult_wrapper(ssh2_mac *m)
- {
- strbuf *sb = strbuf_new();
- void *u = strbuf_append(sb, ssh2_mac_alg(m)->len);
- ssh2_mac_genresult(m, u);
- return sb;
- }
- ssh_key *ssh_key_base_key_wrapper(ssh_key *key)
- {
- /* To avoid having to explain the borrowed reference to Python,
- * just clone the key unconditionally */
- return ssh_key_clone(ssh_key_base_key(key));
- }
- void ssh_key_ca_public_blob_wrapper(ssh_key *key, BinarySink *out)
- {
- /* Wrap to avoid null-pointer dereference */
- if (!key->vt->is_certificate)
- fatal_error("ssh_key_ca_public_blob: needs a certificate");
- ssh_key_ca_public_blob(key, out);
- }
- void ssh_key_cert_id_string_wrapper(ssh_key *key, BinarySink *out)
- {
- /* Wrap to avoid null-pointer dereference */
- if (!key->vt->is_certificate)
- fatal_error("ssh_key_cert_id_string: needs a certificate");
- ssh_key_cert_id_string(key, out);
- }
- static bool ssh_key_check_cert_wrapper(
- ssh_key *key, bool host, ptrlen principal, uint64_t time, ptrlen optstr,
- BinarySink *error)
- {
- /* Wrap to avoid null-pointer dereference */
- if (!key->vt->is_certificate)
- fatal_error("ssh_key_cert_id_string: needs a certificate");
- ca_options opts;
- opts.permit_rsa_sha1 = true;
- opts.permit_rsa_sha256 = true;
- opts.permit_rsa_sha512 = true;
- while (optstr.len) {
- ptrlen word = ptrlen_get_word(&optstr, ",");
- ptrlen key = word, value = PTRLEN_LITERAL("");
- const char *comma = memchr(word.ptr, '=', word.len);
- if (comma) {
- key.len = comma - (const char *)word.ptr;
- value.ptr = comma + 1;
- value.len = word.len - key.len - 1;
- }
- if (ptrlen_eq_string(key, "permit_rsa_sha1"))
- opts.permit_rsa_sha1 = ptrlen_eq_string(value, "true");
- if (ptrlen_eq_string(key, "permit_rsa_sha256"))
- opts.permit_rsa_sha256 = ptrlen_eq_string(value, "true");
- if (ptrlen_eq_string(key, "permit_rsa_sha512"))
- opts.permit_rsa_sha512 = ptrlen_eq_string(value, "true");
- }
- return ssh_key_check_cert(key, host, principal, time, &opts, error);
- }
- bool dh_validate_f_wrapper(dh_ctx *dh, mp_int *f)
- {
- return dh_validate_f(dh, f) == NULL;
- }
- void ssh_hash_update(ssh_hash *h, ptrlen pl)
- {
- put_datapl(h, pl);
- }
- void ssh2_mac_update(ssh2_mac *m, ptrlen pl)
- {
- put_datapl(m, pl);
- }
- static RSAKey *rsa_new(void)
- {
- RSAKey *rsa = snew(RSAKey);
- memset(rsa, 0, sizeof(RSAKey));
- return rsa;
- }
- strbuf *ecdh_key_getkey_wrapper(ecdh_key *ek, ptrlen remoteKey)
- {
- /* Fold the boolean return value in C into the string return value
- * for this purpose, by returning NULL on failure */
- strbuf *sb = strbuf_new();
- if (!ecdh_key_getkey(ek, remoteKey, BinarySink_UPCAST(sb))) {
- strbuf_free(sb);
- return NULL;
- }
- return sb;
- }
- static void int16_list_resize(int16_list *list, unsigned p)
- {
- list->integers = sresize(list->integers, p, uint16_t);
- for (size_t i = list->n; i < p; i++)
- list->integers[i] = 0;
- }
- #if 0
- static int16_list ntru_ring_to_list_and_free(uint16_t *out, unsigned p)
- {
- struct mpint_list mpl;
- mpl.n = p;
- mpl->integers = snewn(p, mp_int *);
- for (unsigned i = 0; i < p; i++)
- mpl->integers[i] = mp_from_integer((int16_t)out[i]);
- sfree(out);
- add_finaliser(finaliser_sfree, mpl->integers);
- return mpl;
- }
- #endif
- int16_list *ntru_ring_multiply_wrapper(
- int16_list *a, int16_list *b, unsigned p, unsigned q)
- {
- int16_list_resize(a, p);
- int16_list_resize(b, p);
- int16_list *out = make_int16_list(p);
- ntru_ring_multiply(out->integers, a->integers, b->integers, p, q);
- return out;
- }
- int16_list *ntru_ring_invert_wrapper(int16_list *in, unsigned p, unsigned q)
- {
- int16_list_resize(in, p);
- int16_list *out = make_int16_list(p);
- unsigned success = ntru_ring_invert(out->integers, in->integers, p, q);
- if (!success)
- return NULL;
- return out;
- }
- int16_list *ntru_mod3_wrapper(int16_list *in, unsigned p, unsigned q)
- {
- int16_list_resize(in, p);
- int16_list *out = make_int16_list(p);
- ntru_mod3(out->integers, in->integers, p, q);
- return out;
- }
- int16_list *ntru_round3_wrapper(int16_list *in, unsigned p, unsigned q)
- {
- int16_list_resize(in, p);
- int16_list *out = make_int16_list(p);
- ntru_round3(out->integers, in->integers, p, q);
- return out;
- }
- int16_list *ntru_bias_wrapper(int16_list *in, unsigned bias,
- unsigned p, unsigned q)
- {
- int16_list_resize(in, p);
- int16_list *out = make_int16_list(p);
- ntru_bias(out->integers, in->integers, bias, p, q);
- return out;
- }
- int16_list *ntru_scale_wrapper(int16_list *in, unsigned scale,
- unsigned p, unsigned q)
- {
- int16_list_resize(in, p);
- int16_list *out = make_int16_list(p);
- ntru_scale(out->integers, in->integers, scale, p, q);
- return out;
- }
- NTRUEncodeSchedule *ntru_encode_schedule_wrapper(int16_list *in)
- {
- return ntru_encode_schedule(in->integers, in->n);
- }
- void ntru_encode_wrapper(NTRUEncodeSchedule *sched, int16_list *rs,
- BinarySink *bs)
- {
- ntru_encode(sched, rs->integers, bs);
- }
- int16_list *ntru_decode_wrapper(NTRUEncodeSchedule *sched, ptrlen data)
- {
- int16_list *out = make_int16_list(ntru_encode_schedule_nvals(sched));
- ntru_decode(sched, out->integers, data);
- return out;
- }
- int16_list *ntru_gen_short_wrapper(unsigned p, unsigned w)
- {
- int16_list *out = make_int16_list(p);
- ntru_gen_short(out->integers, p, w);
- return out;
- }
- int16_list *ntru_pubkey_wrapper(NTRUKeyPair *keypair)
- {
- unsigned p = ntru_keypair_p(keypair);
- int16_list *out = make_int16_list(p);
- memcpy(out->integers, ntru_pubkey(keypair), p*sizeof(uint16_t));
- return out;
- }
- int16_list *ntru_encrypt_wrapper(int16_list *plaintext, int16_list *pubkey,
- unsigned p, unsigned q)
- {
- int16_list *out = make_int16_list(p);
- ntru_encrypt(out->integers, plaintext->integers, pubkey->integers, p, q);
- return out;
- }
- int16_list *ntru_decrypt_wrapper(int16_list *ciphertext, NTRUKeyPair *keypair)
- {
- unsigned p = ntru_keypair_p(keypair);
- int16_list *out = make_int16_list(p);
- ntru_decrypt(out->integers, ciphertext->integers, keypair);
- return out;
- }
- strbuf *rsa_ssh1_encrypt_wrapper(ptrlen input, RSAKey *key)
- {
- /* Fold the boolean return value in C into the string return value
- * for this purpose, by returning NULL on failure */
- strbuf *sb = strbuf_new();
- put_datapl(sb, input);
- put_padding(sb, key->bytes - input.len, 0);
- if (!rsa_ssh1_encrypt(sb->u, input.len, key)) {
- strbuf_free(sb);
- return NULL;
- }
- return sb;
- }
- strbuf *rsa_ssh1_decrypt_pkcs1_wrapper(mp_int *input, RSAKey *key)
- {
- /* Again, return "" on failure */
- strbuf *sb = strbuf_new();
- if (!rsa_ssh1_decrypt_pkcs1(input, key, sb))
- strbuf_clear(sb);
- return sb;
- }
- strbuf *des_encrypt_xdmauth_wrapper(ptrlen key, ptrlen data)
- {
- if (key.len != 7)
- fatal_error("des_encrypt_xdmauth: key must be 7 bytes long");
- if (data.len % 8 != 0)
- fatal_error("des_encrypt_xdmauth: data must be a multiple of 8 bytes");
- strbuf *sb = strbuf_dup(data);
- des_encrypt_xdmauth(key.ptr, sb->u, sb->len);
- return sb;
- }
- strbuf *des_decrypt_xdmauth_wrapper(ptrlen key, ptrlen data)
- {
- if (key.len != 7)
- fatal_error("des_decrypt_xdmauth: key must be 7 bytes long");
- if (data.len % 8 != 0)
- fatal_error("des_decrypt_xdmauth: data must be a multiple of 8 bytes");
- strbuf *sb = strbuf_dup(data);
- des_decrypt_xdmauth(key.ptr, sb->u, sb->len);
- return sb;
- }
- strbuf *des3_encrypt_pubkey_wrapper(ptrlen key, ptrlen data)
- {
- if (key.len != 16)
- fatal_error("des3_encrypt_pubkey: key must be 16 bytes long");
- if (data.len % 8 != 0)
- fatal_error("des3_encrypt_pubkey: data must be a multiple of 8 bytes");
- strbuf *sb = strbuf_dup(data);
- des3_encrypt_pubkey(key.ptr, sb->u, sb->len);
- return sb;
- }
- strbuf *des3_decrypt_pubkey_wrapper(ptrlen key, ptrlen data)
- {
- if (key.len != 16)
- fatal_error("des3_decrypt_pubkey: key must be 16 bytes long");
- if (data.len % 8 != 0)
- fatal_error("des3_decrypt_pubkey: data must be a multiple of 8 bytes");
- strbuf *sb = strbuf_dup(data);
- des3_decrypt_pubkey(key.ptr, sb->u, sb->len);
- return sb;
- }
- strbuf *des3_encrypt_pubkey_ossh_wrapper(ptrlen key, ptrlen iv, ptrlen data)
- {
- if (key.len != 24)
- fatal_error("des3_encrypt_pubkey_ossh: key must be 24 bytes long");
- if (iv.len != 8)
- fatal_error("des3_encrypt_pubkey_ossh: iv must be 8 bytes long");
- if (data.len % 8 != 0)
- fatal_error("des3_encrypt_pubkey_ossh: data must be a multiple of 8 bytes");
- strbuf *sb = strbuf_dup(data);
- des3_encrypt_pubkey_ossh(key.ptr, iv.ptr, sb->u, sb->len);
- return sb;
- }
- strbuf *des3_decrypt_pubkey_ossh_wrapper(ptrlen key, ptrlen iv, ptrlen data)
- {
- if (key.len != 24)
- fatal_error("des3_decrypt_pubkey_ossh: key must be 24 bytes long");
- if (iv.len != 8)
- fatal_error("des3_encrypt_pubkey_ossh: iv must be 8 bytes long");
- if (data.len % 8 != 0)
- fatal_error("des3_decrypt_pubkey_ossh: data must be a multiple of 8 bytes");
- strbuf *sb = strbuf_dup(data);
- des3_decrypt_pubkey_ossh(key.ptr, iv.ptr, sb->u, sb->len);
- return sb;
- }
- strbuf *aes256_encrypt_pubkey_wrapper(ptrlen key, ptrlen iv, ptrlen data)
- {
- if (key.len != 32)
- fatal_error("aes256_encrypt_pubkey: key must be 32 bytes long");
- if (iv.len != 16)
- fatal_error("aes256_encrypt_pubkey: iv must be 16 bytes long");
- if (data.len % 16 != 0)
- fatal_error("aes256_encrypt_pubkey: data must be a multiple of 16 bytes");
- strbuf *sb = strbuf_dup(data);
- aes256_encrypt_pubkey(key.ptr, iv.ptr, sb->u, sb->len);
- return sb;
- }
- strbuf *aes256_decrypt_pubkey_wrapper(ptrlen key, ptrlen iv, ptrlen data)
- {
- if (key.len != 32)
- fatal_error("aes256_decrypt_pubkey: key must be 32 bytes long");
- if (iv.len != 16)
- fatal_error("aes256_encrypt_pubkey: iv must be 16 bytes long");
- if (data.len % 16 != 0)
- fatal_error("aes256_decrypt_pubkey: data must be a multiple of 16 bytes");
- strbuf *sb = strbuf_dup(data);
- aes256_decrypt_pubkey(key.ptr, iv.ptr, sb->u, sb->len);
- return sb;
- }
- strbuf *prng_read_wrapper(prng *pr, size_t size)
- {
- strbuf *sb = strbuf_new();
- prng_read(pr, strbuf_append(sb, size), size);
- return sb;
- }
- void prng_seed_update(prng *pr, ptrlen data)
- {
- put_datapl(pr, data);
- }
- bool crcda_detect(ptrlen packet, ptrlen iv)
- {
- if (iv.len != 0 && iv.len != 8)
- fatal_error("crcda_detect: iv must be empty or 8 bytes long");
- if (packet.len % 8 != 0)
- fatal_error("crcda_detect: packet must be a multiple of 8 bytes");
- struct crcda_ctx *ctx = crcda_make_context();
- bool toret = detect_attack(ctx, packet.ptr, packet.len,
- iv.len ? iv.ptr : NULL);
- crcda_free_context(ctx);
- return toret;
- }
- ssh_key *ppk_load_s_wrapper(BinarySource *src, char **comment,
- const char *passphrase, const char **errorstr)
- {
- ssh2_userkey *uk = ppk_load_s(src, passphrase, errorstr);
- if (uk == SSH2_WRONG_PASSPHRASE) {
- /* Fudge this special return value */
- *errorstr = "SSH2_WRONG_PASSPHRASE";
- return NULL;
- }
- if (uk == NULL)
- return NULL;
- ssh_key *toret = uk->key;
- *comment = uk->comment;
- sfree(uk);
- return toret;
- }
- int rsa1_load_s_wrapper(BinarySource *src, RSAKey *rsa, char **comment,
- const char *passphrase, const char **errorstr)
- {
- int toret = rsa1_load_s(src, rsa, passphrase, errorstr);
- *comment = rsa->comment;
- rsa->comment = NULL;
- return toret;
- }
- strbuf *ppk_save_sb_wrapper(
- ssh_key *key, const char *comment, const char *passphrase,
- unsigned fmt_version, Argon2Flavour flavour,
- uint32_t mem, uint32_t passes, uint32_t parallel)
- {
- /*
- * For repeatable testing purposes, we never want a timing-dependent
- * choice of password hashing parameters, so this is easy.
- */
- ppk_save_parameters save_params;
- memset(&save_params, 0, sizeof(save_params));
- save_params.fmt_version = fmt_version;
- save_params.argon2_flavour = flavour;
- save_params.argon2_mem = mem;
- save_params.argon2_passes_auto = false;
- save_params.argon2_passes = passes;
- save_params.argon2_parallelism = parallel;
- ssh2_userkey uk;
- uk.key = key;
- uk.comment = dupstr(comment);
- strbuf *toret = ppk_save_sb(&uk, passphrase, &save_params);
- sfree(uk.comment);
- return toret;
- }
- strbuf *rsa1_save_sb_wrapper(RSAKey *key, const char *comment,
- const char *passphrase)
- {
- key->comment = dupstr(comment);
- strbuf *toret = rsa1_save_sb(key, passphrase);
- sfree(key->comment);
- key->comment = NULL;
- return toret;
- }
- #define return_void(out, expression) (expression)
- static ProgressReceiver null_progress = { .vt = &null_progress_vt };
- mp_int *primegen_generate_wrapper(
- PrimeGenerationContext *ctx, PrimeCandidateSource *pcs)
- {
- return primegen_generate(ctx, pcs, &null_progress);
- }
- RSAKey *rsa1_generate(int bits, bool strong, PrimeGenerationContext *pgc)
- {
- RSAKey *rsakey = snew(RSAKey);
- rsa_generate(rsakey, bits, strong, pgc, &null_progress);
- rsakey->comment = NULL;
- return rsakey;
- }
- ssh_key *rsa_generate_wrapper(int bits, bool strong,
- PrimeGenerationContext *pgc)
- {
- return &rsa1_generate(bits, strong, pgc)->sshk;
- }
- ssh_key *dsa_generate_wrapper(int bits, PrimeGenerationContext *pgc)
- {
- struct dsa_key *dsakey = snew(struct dsa_key);
- dsa_generate(dsakey, bits, pgc, &null_progress);
- return &dsakey->sshk;
- }
- ssh_key *ecdsa_generate_wrapper(int bits)
- {
- struct ecdsa_key *ek = snew(struct ecdsa_key);
- if (!ecdsa_generate(ek, bits)) {
- sfree(ek);
- return NULL;
- }
- return &ek->sshk;
- }
- ssh_key *eddsa_generate_wrapper(int bits)
- {
- struct eddsa_key *ek = snew(struct eddsa_key);
- if (!eddsa_generate(ek, bits)) {
- sfree(ek);
- return NULL;
- }
- return &ek->sshk;
- }
- size_t key_components_count(key_components *kc) { return kc->ncomponents; }
- const char *key_components_nth_name(key_components *kc, size_t n)
- {
- return (n >= kc->ncomponents ? NULL :
- kc->components[n].name);
- }
- strbuf *key_components_nth_str(key_components *kc, size_t n)
- {
- if (n >= kc->ncomponents)
- return NULL;
- if (kc->components[n].type != KCT_TEXT &&
- kc->components[n].type != KCT_BINARY)
- return NULL;
- return strbuf_dup(ptrlen_from_strbuf(kc->components[n].str));
- }
- mp_int *key_components_nth_mp(key_components *kc, size_t n)
- {
- return (n >= kc->ncomponents ? NULL :
- kc->components[n].type != KCT_MPINT ? NULL :
- mp_copy(kc->components[n].mp));
- }
- PockleStatus pockle_add_prime_wrapper(Pockle *pockle, mp_int *p,
- struct mpint_list mpl, mp_int *witness)
- {
- return pockle_add_prime(pockle, p, mpl.integers, mpl.n, witness);
- }
- strbuf *argon2_wrapper(Argon2Flavour flavour, uint32_t mem, uint32_t passes,
- uint32_t parallel, uint32_t taglen,
- ptrlen P, ptrlen S, ptrlen K, ptrlen X)
- {
- strbuf *out = strbuf_new();
- argon2(flavour, mem, passes, parallel, taglen, P, S, K, X, out);
- return out;
- }
- strbuf *openssh_bcrypt_wrapper(ptrlen passphrase, ptrlen salt,
- unsigned rounds, unsigned outbytes)
- {
- strbuf *out = strbuf_new();
- openssh_bcrypt(passphrase, salt, rounds,
- strbuf_append(out, outbytes), outbytes);
- return out;
- }
- strbuf *get_implementations_commasep(ptrlen alg)
- {
- strbuf *out = strbuf_new();
- put_datapl(out, alg);
- if (ptrlen_startswith(alg, PTRLEN_LITERAL("aesgcm"), NULL)) {
- put_fmt(out, ",%.*s_sw", PTRLEN_PRINTF(alg));
- put_fmt(out, ",%.*s_ref_poly", PTRLEN_PRINTF(alg));
- #if HAVE_CLMUL
- put_fmt(out, ",%.*s_clmul", PTRLEN_PRINTF(alg));
- #endif
- #if HAVE_NEON_PMULL
- put_fmt(out, ",%.*s_neon", PTRLEN_PRINTF(alg));
- #endif
- } else if (ptrlen_startswith(alg, PTRLEN_LITERAL("aes"), NULL)) {
- put_fmt(out, ",%.*s_sw", PTRLEN_PRINTF(alg));
- #if HAVE_AES_NI
- put_fmt(out, ",%.*s_ni", PTRLEN_PRINTF(alg));
- #endif
- #if HAVE_NEON_CRYPTO
- put_fmt(out, ",%.*s_neon", PTRLEN_PRINTF(alg));
- #endif
- } else if (ptrlen_startswith(alg, PTRLEN_LITERAL("sha256"), NULL) ||
- ptrlen_startswith(alg, PTRLEN_LITERAL("sha1"), NULL)) {
- put_fmt(out, ",%.*s_sw", PTRLEN_PRINTF(alg));
- #if HAVE_SHA_NI
- put_fmt(out, ",%.*s_ni", PTRLEN_PRINTF(alg));
- #endif
- #if HAVE_NEON_CRYPTO
- put_fmt(out, ",%.*s_neon", PTRLEN_PRINTF(alg));
- #endif
- } else if (ptrlen_startswith(alg, PTRLEN_LITERAL("sha512"), NULL)) {
- put_fmt(out, ",%.*s_sw", PTRLEN_PRINTF(alg));
- #if HAVE_NEON_SHA512
- put_fmt(out, ",%.*s_neon", PTRLEN_PRINTF(alg));
- #endif
- }
- return out;
- }
- #define OPTIONAL_PTR_FUNC(type) \
- typedef TD_val_##type TD_opt_val_##type; \
- static TD_opt_val_##type get_opt_val_##type(BinarySource *in) { \
- ptrlen word = get_word(in); \
- if (ptrlen_eq_string(word, "NULL")) \
- return NULL; \
- return unwrap_value_##type(lookup_value(word))->vu_##type; \
- }
- OPTIONAL_PTR_FUNC(cipher)
- OPTIONAL_PTR_FUNC(mpint)
- OPTIONAL_PTR_FUNC(string)
- /*
- * HERE BE DRAGONS: the horrible C preprocessor business that reads
- * testcrypt-func.h and generates a marshalling wrapper for each
- * exported function.
- *
- * In an ideal world, we would start from a specification like this in
- * testcrypt-func.h
- *
- * FUNC(val_foo, example, ARG(val_bar, bar), ARG(uint, n))
- *
- * and generate a wrapper function looking like this:
- *
- * static void handle_example(BinarySource *in, strbuf *out) {
- * TD_val_bar bar = get_val_bar(in);
- * TD_uint n = get_uint(in);
- * return_val_foo(out, example(bar, n));
- * }
- *
- * which would read the marshalled form of each function argument in
- * turn from the input BinarySource via the get_<type>() function
- * family defined in this file; assign each argument to a local
- * variable; call the underlying C function with all those arguments;
- * and then call a function of the return_<type>() family to marshal
- * the output value into the output strbuf to be sent to standard
- * output.
- *
- * With a more general macro processor such as m4, or custom code in
- * Perl or Python, or a helper program like llvm-tblgen, we could just
- * do that directly, reading function specifications from
- * testcrypt-func.h and writing out exactly the above. But we don't
- * have a fully general macro processor (since everything in that
- * category introduces an extra build dependency that's awkward on
- * plain Windows, or requires compiling and running a helper program
- * which is awkward in a cross-compile). We only have cpp. And in cpp,
- * a macro can't expand one of its arguments differently in two parts
- * of its own expansion. So we have to be more clever.
- *
- * In place of the above code, I instead generate three successive
- * declarations for each function. In simplified form they would look
- * like this:
- *
- * typedef struct ARGS_example {
- * TD_val_bar bar;
- * TD_uint n;
- * } ARGS_example;
- *
- * static inline ARGS_example get_args_example(BinarySource *in) {
- * ARGS_example args;
- * args.bar = get_val_bar(in);
- * args.n = get_uint(in);
- * return args;
- * }
- *
- * static void handle_example(BinarySource *in, strbuf *out) {
- * ARGS_example args = get_args_example(in);
- * return_val_foo(out, example(args.bar, args.n));
- * }
- *
- * Each of these mentions the arguments and their types just _once_,
- * so each one can be generated by a single expansion of the FUNC(...)
- * specification in testcrypt-func.h, with FUNC and ARG and VOID
- * defined to appropriate values.
- *
- * Or ... *nearly*. In fact, I left out several details there, but
- * it's a good starting point to understand the full version.
- *
- * To begin with, several of the variable names shown above are
- * actually named with an ugly leading underscore, to minimise the
- * chance of them colliding with real parameter names. (You could
- * easily imagine 'out' being the name of a parameter to one of the
- * wrapped functions.) Also, we memset the whole structure to zero at
- * the start of get_args_example() to avoid compiler warnings about
- * uninitialised stuff, and insert a precautionary '(void)args;' in
- * handle_example to avoid a similar warning about _unused_ stuff.
- *
- * The big problem is the commas that have to appear between arguments
- * in the final call to the actual C function. Those can't be
- * generated by expanding the ARG macro itself, or you'd get one too
- * many - either a leading comma or a trailing comma. Trailing commas
- * are legal in a Python function call, but unfortunately C is not yet
- * so enlightened. (C permits a trailing comma in a struct or array
- * initialiser, and is coming round to it in enums, but hasn't yet
- * seen the light about function calls or function prototypes.)
- *
- * So the commas must appear _between_ ARG(...) specifiers. And that
- * means they unavoidably appear in _every_ expansion of FUNC() (or
- * rather, every expansion that uses the variadic argument list at
- * all). Therefore, we need to ensure they're harmless in the other
- * two functions as well.
- *
- * In the get_args_example() function above, there's no real problem.
- * The list of assignments can perfectly well be separated by commas
- * instead of semicolons, so that it becomes a single expression-
- * statement instead of a sequence of them; the comma operator still
- * defines a sequence point, so it's fine.
- *
- * But what about the structure definition of ARGS_example?
- *
- * To get round that, we fill the structure with pointless extra
- * cruft, in the form of an extra 'int' field before and after each
- * actually useful argument field. So the real structure definition
- * ends up looking more like this:
- *
- * typedef struct ARGS_example {
- * int _predummy_bar;
- * TD_val_bar bar;
- * int _postdummy_bar, _predummy_n;
- * TD_uint n;
- * int _postdummy_n;
- * } ARGS_example;
- *
- * Those extra 'int' fields are ignored completely at run time. They
- * might cause a runtime space cost if the struct doesn't get
- * completely optimised away when get_args_example is inlined into
- * handle_example, but even if so, that's OK, this is a test program
- * whose memory usage isn't critical. The real point is that, in
- * between each pair of real arguments, there's a declaration
- * containing *two* int variables, and in between them is the vital
- * comma that we need!
- *
- * So in that pass through testcrypt-func.h, the ARG(type, name) macro
- * has to expand to the weird piece of text
- *
- * _predummy_name; // terminating the previous int declaration
- * TD_type name; // declaring the thing we actually wanted
- * int _postdummy_name // new declaration ready to see a comma
- *
- * so that a comma-separated list of pieces of expansion like that
- * will fall into just the right form to be the core of the above
- * expanded structure definition. Then we just need to put in the
- * 'int' after the open brace, and the ';' before the closing brace,
- * and we've got everything we need to make it all syntactically legal.
- *
- * Finally, what if a wrapped function has _no_ arguments? Two out of
- * three uses of the argument list here need some kind of special case
- * for that. That's why you have to write 'VOID' explicitly in an
- * empty argument list in testcrypt-func.h: we make VOID expand to
- * whatever is needed to avoid a syntax error in that special case.
- */
- /*
- * Workarounds for an awkwardness in Visual Studio's preprocessor,
- * which disagrees with everyone else about what happens if you expand
- * __VA_ARGS__ into the argument list of another macro. gcc and clang
- * will treat the commas expanding from __VA_ARGS__ as argument
- * separators, whereas VS will make them all part of a single argument
- * to the secondary macro. We want the former behaviour, so we use
- * the following workaround to enforce it.
- *
- * Each of these JUXTAPOSE macros simply places its arguments side by
- * side. But the arguments are macro-expanded before JUXTAPOSE is
- * called at all, so we can do this:
- *
- * JUXTAPOSE(macroname, (__VA_ARGS__))
- * -> JUXTAPOSE(macroname, (foo, bar, baz))
- * -> macroname (foo, bar, baz)
- *
- * and this preliminary expansion causes the commas to be treated
- * normally by the time VS gets round to expanding the inner macro.
- *
- * We need two differently named JUXTAPOSE macros, because we have to
- * do this trick twice: once to turn FUNC and FUNC_WRAPPED in
- * testcrypt-funcs.h into the underlying common FUNC_INNER, and again
- * to expand the final function call. And you can't expand a macro
- * inside text expanded from the _same_ macro, so we have to do the
- * outer and inner instances of this trick using macros of different
- * names.
- */
- #define JUXTAPOSE1(first, second) first second
- #define JUXTAPOSE2(first, second) first second
- #define FUNC(outtype, fname, ...) \
- JUXTAPOSE1(FUNC_INNER, (outtype, fname, fname, __VA_ARGS__))
- #define FUNC_WRAPPED(outtype, fname, ...) \
- JUXTAPOSE1(FUNC_INNER, (outtype, fname, fname##_wrapper, __VA_ARGS__))
- #define ARG(type, arg) _predummy_##arg; TD_##type arg; int _postdummy_##arg
- #define VOID _voiddummy
- #define FUNC_INNER(outtype, fname, realname, ...) \
- typedef struct ARGS_##fname { \
- int __VA_ARGS__; \
- } ARGS_##fname;
- #include "testcrypt-func.h"
- #undef FUNC_INNER
- #undef ARG
- #undef VOID
- #define ARG(type, arg) _args.arg = get_##type(_in)
- #define VOID ((void)0)
- #define FUNC_INNER(outtype, fname, realname, ...) \
- static inline ARGS_##fname get_args_##fname(BinarySource *_in) { \
- ARGS_##fname _args; \
- memset(&_args, 0, sizeof(_args)); \
- __VA_ARGS__; \
- return _args; \
- }
- #include "testcrypt-func.h"
- #undef FUNC_INNER
- #undef ARG
- #undef VOID
- #define ARG(type, arg) _args.arg
- #define VOID
- #define FUNC_INNER(outtype, fname, realname, ...) \
- static void handle_##fname(BinarySource *_in, strbuf *_out) { \
- ARGS_##fname _args = get_args_##fname(_in); \
- (void)_args; /* suppress warning if no actual arguments */ \
- return_##outtype(_out, JUXTAPOSE2(realname, (__VA_ARGS__))); \
- }
- #include "testcrypt-func.h"
- #undef FUNC_INNER
- #undef ARG
- static void process_line(BinarySource *in, strbuf *out)
- {
- ptrlen id = get_word(in);
- #define DISPATCH_INTERNAL(cmdname, handler) do { \
- if (ptrlen_eq_string(id, cmdname)) { \
- handler(in, out); \
- return; \
- } \
- } while (0)
- #define DISPATCH_COMMAND(cmd) DISPATCH_INTERNAL(#cmd, handle_##cmd)
- DISPATCH_COMMAND(hello);
- DISPATCH_COMMAND(free);
- DISPATCH_COMMAND(newstring);
- DISPATCH_COMMAND(getstring);
- DISPATCH_COMMAND(mp_literal);
- DISPATCH_COMMAND(mp_dump);
- DISPATCH_COMMAND(checkenum);
- #undef DISPATCH_COMMAND
- #define FUNC_INNER(outtype, fname, realname, ...) \
- DISPATCH_INTERNAL(#fname,handle_##fname);
- #include "testcrypt-func.h"
- #undef FUNC_INNER
- #undef DISPATCH_INTERNAL
- fatal_error("command '%.*s': unrecognised", PTRLEN_PRINTF(id));
- }
- static void free_all_values(void)
- {
- for (Value *val; (val = delpos234(values, 0)) != NULL ;)
- free_value(val);
- freetree234(values);
- }
- void dputs(const char *buf)
- {
- fputs(buf, stderr);
- }
- int main(int argc, char **argv)
- {
- const char *infile = NULL, *outfile = NULL;
- bool doing_opts = true;
- while (--argc > 0) {
- char *p = *++argv;
- if (p[0] == '-' && doing_opts) {
- if (!strcmp(p, "-o")) {
- if (--argc <= 0) {
- fprintf(stderr, "'-o' expects a filename\n");
- return 1;
- }
- outfile = *++argv;
- } else if (!strcmp(p, "--")) {
- doing_opts = false;
- } else if (!strcmp(p, "--help")) {
- printf("usage: testcrypt [INFILE] [-o OUTFILE]\n");
- printf(" also: testcrypt --help display this text\n");
- return 0;
- } else {
- fprintf(stderr, "unknown command line option '%s'\n", p);
- return 1;
- }
- } else if (!infile) {
- infile = p;
- } else {
- fprintf(stderr, "can only handle one input file name\n");
- return 1;
- }
- }
- FILE *infp = stdin;
- if (infile) {
- infp = fopen(infile, "r");
- if (!infp) {
- fprintf(stderr, "%s: open: %s\n", infile, strerror(errno));
- return 1;
- }
- }
- FILE *outfp = stdout;
- if (outfile) {
- outfp = fopen(outfile, "w");
- if (!outfp) {
- fprintf(stderr, "%s: open: %s\n", outfile, strerror(errno));
- return 1;
- }
- }
- values = newtree234(valuecmp);
- atexit(free_all_values);
- for (char *line; (line = chomp(fgetline(infp))) != NULL ;) {
- BinarySource src[1];
- BinarySource_BARE_INIT(src, line, strlen(line));
- strbuf *sb = strbuf_new();
- process_line(src, sb);
- run_finalisers(sb);
- size_t lines = 0;
- for (size_t i = 0; i < sb->len; i++)
- if (sb->s[i] == '\n')
- lines++;
- fprintf(outfp, "%"SIZEu"\n%s", lines, sb->s);
- fflush(outfp);
- strbuf_free(sb);
- sfree(line);
- }
- if (infp != stdin)
- fclose(infp);
- if (outfp != stdin)
- fclose(outfp);
- return 0;
- }
|