/* * scapitest.c * * HEADER Testing SCAPI API * * Expected SUCCESSes: 2 + 10 + 1 for all tests. * * Returns: * Number of FAILUREs. * * * ASSUMES No key management functions return non-zero success codes. * * XXX Split into individual modules? * XXX Error/fringe conditions should be tested. * * * Test of sc_random. SUCCESSes: 2. * REQUIRES a human to spot check for obvious non-randomness... * * Test of sc_generate_keyed_hash and sc_check_keyed_hash. SUCCESSes: 10. * * Test of sc_encrypt and sc_decrypt. SUCCESSes: 1. */ #include #include #ifdef HAVE_NETINET_IN_H #include #endif #include #include #include #include #include #include #include #include #include #include extern char *optarg; extern int optind, optopt, opterr; #define DEBUG /* */ /* * Globals, &c... */ char *local_progname; int testcount=0; int failcount=0; #define USAGE "Usage: %s [-h][-acHr]" #define OPTIONLIST "achHr" int doalltests = 0, docrypt = 0, dokeyedhash = 0, dorandom = 0; #define ALLOPTIONS (doalltests + docrypt + dokeyedhash + dorandom) #define LOCAL_MAXBUF (1024 * 8) #define NL "\n" #define OUTPUT(o) printf("# %s\n", o); #define SUCCESS(s) \ { \ printf("# Done with %s\n", s); \ } #define FAILED(e, f) \ { \ if (e != SNMPERR_SUCCESS) { \ printf("not ok: %d - %s\n", ++testcount, f); \ failcount += 1; \ } else { \ printf("ok: %d - %s\n", ++testcount, f); \ } \ fflush(stdout); \ } #define BIGSTRING \ (const u_char *) \ " A port may be a pleasant retreat for any mind grown weary of" \ "the struggle for existence. The vast expanse of sky, the" \ "mobile architecture of the clouds, the chameleon coloration" \ "of the sea, the beacons flashing on the shore, together make" \ "a prism which is marvellously calculated to entertain but not" \ "fatigue the eye. The lofty ships with their complex webs of" \ "rigging, swayed to and fro by the swell in harmonious dance," \ "all help to maintain a taste for rhythm and beauty in the" \ "mind. And above all there is a mysterious, aristrocratic kind" \ "of pleasure to be had, for those who have lost all curiosity" \ "or ambition, as they strech on the belvedere or lean over the" \ "mole to watch the arrivals and departures of other men, those" \ "who still have sufficient strength of purpose in them, the" \ "urge to travel or enrich themselves." \ " -- Baudelaire" \ " From _The_Poems_in_Prose_, \"The Port\" (XLI)." #define BIGSECRET \ (const u_char *) \ "Shhhh... Don't tell *anyone* about this. Not a soul." #define BKWDSECRET \ (const u_char *) \ ".luos a toN .siht tuoba *enoyna* llet t'noD ...hhhhS" #define MLCOUNT_MAX 6 /* MAC Length Count Maximum. */ /* * Prototypes. */ void usage(void); int test_docrypt(void); int test_dokeyedhash(void); int test_dorandom(void); int main(int argc, char **argv) { int rval = SNMPERR_SUCCESS; char ch; local_progname = argv[0]; /* * Parse. */ while ((ch = getopt(argc, argv, OPTIONLIST)) != EOF) { switch (ch) { case 'a': doalltests = 1; break; case 'c': docrypt = 1; break; case 'H': dokeyedhash = 1; break; case 'r': dorandom = 1; break; case 'h': rval = 0; default: usage(); exit(rval); } argc -= 1; argv += 1; optind = 1; } /* endwhile getopt */ if ((argc > 1)) { usage(); exit(1000); } else if (ALLOPTIONS != 1) { doalltests = 1; } /* * Test stuff. */ rval = sc_init(); FAILED(rval, "sc_init() return code"); if (docrypt || doalltests) { test_docrypt(); } if (dokeyedhash || doalltests) { test_dokeyedhash(); } if (dorandom || doalltests) { test_dorandom(); } printf("1..%d\n", testcount); return 0; } /* end main() */ void usage(void) { printf( USAGE "" NL " -a All tests." NL " -c Test of sc_encrypt()/sc_decrypt()." NL " -h Help." NL " -H Test sc_{generate,check}_keyed_hash()." NL " -r Test sc_random()." NL "" NL, local_progname); } /* end usage() */ /*******************************************************************-o-****** * test_dorandom * * One large request, one set of short requests. * * Returns: * Number of failures. * * XXX probably should split up into individual options. */ int test_dorandom(void) { int rval = SNMPERR_SUCCESS, origrequest = (1024 * 2), origrequest_short = 19, shortcount = 7, i; size_t nbytes = origrequest; u_char buf[LOCAL_MAXBUF]; OUTPUT("Random test -- large request:"); rval = sc_random(buf, &nbytes); FAILED(rval, "sc_random() return code"); if (nbytes != origrequest) { FAILED(SNMPERR_GENERR, "sc_random() returned different than requested."); } dump_chunk("scapitest", NULL, buf, nbytes); SUCCESS("Random test -- large request."); OUTPUT("Random test -- short requests:"); origrequest_short = 16; for (i = 0; i < shortcount; i++) { nbytes = origrequest_short; rval = sc_random(buf, &nbytes); FAILED(rval, "sc_random() return code"); if (nbytes != origrequest_short) { FAILED(SNMPERR_GENERR, "sc_random() returned different " "than requested."); } dump_chunk("scapitest", NULL, buf, nbytes); } /* endfor */ SUCCESS("Random test -- short requests."); return failcount; } /* end test_dorandom() */ /*******************************************************************-o-****** * test_dokeyedhash * * Returns: * Number of failures. * * * Test keyed hashes with a variety of MAC length requests. * * * NOTE Both tests intentionally use the same secret * * FIX Get input or output from some other package which hashes... * XXX Could cut this in half with a little indirection... */ int test_dokeyedhash(void) { int rval = SNMPERR_SUCCESS, bigstring_len = strlen((const char *) BIGSTRING), secret_len = strlen((const char *) BIGSECRET), properlength, mlcount = 0; /* MAC Length count. */ size_t hblen; /* Hash Buffer length. */ u_int hashbuf_len[MLCOUNT_MAX] = { LOCAL_MAXBUF, USM_MD5_AND_SHA_AUTH_LEN, USM_MD5_AND_SHA_AUTH_LEN, USM_MD5_AND_SHA_AUTH_LEN, 7, 0, }; u_char hashbuf[LOCAL_MAXBUF]; char *s; test_dokeyedhash_again: OUTPUT("Starting Keyed hash test using MD5 --"); memset(hashbuf, 0, LOCAL_MAXBUF); hblen = hashbuf_len[mlcount]; properlength = BYTESIZE(SNMP_TRANS_AUTHLEN_HMACMD5); rval = sc_generate_keyed_hash(usmHMACMD5AuthProtocol, USM_LENGTH_OID_TRANSFORM, BIGSECRET, secret_len, BIGSTRING, bigstring_len, hashbuf, &hblen); FAILED(rval, "sc_generate_keyed_hash() return code"); if (hashbuf_len[mlcount] > properlength) { if (hblen != properlength) { FAILED(SNMPERR_GENERR, "Wrong MD5 hash length returned. (1)"); } } else if (hblen != hashbuf_len[mlcount]) { FAILED(SNMPERR_GENERR, "Wrong MD5 hash length returned. (2)"); } rval = sc_check_keyed_hash(usmHMACMD5AuthProtocol, USM_LENGTH_OID_TRANSFORM, BIGSECRET, secret_len, BIGSTRING, bigstring_len, hashbuf, hblen); FAILED(rval, "sc_check_keyed_hash() return code"); binary_to_hex(hashbuf, hblen, &s); printf("# hash buffer (len=%" NETSNMP_PRIz "u, request=%d): %s\n", hblen, hashbuf_len[mlcount], s); SNMP_FREE(s); OUTPUT("Starting Keyed hash test using SHA1 --"); memset(hashbuf, 0, LOCAL_MAXBUF); hblen = hashbuf_len[mlcount]; properlength = BYTESIZE(SNMP_TRANS_AUTHLEN_HMACSHA1); rval = sc_generate_keyed_hash(usmHMACSHA1AuthProtocol, USM_LENGTH_OID_TRANSFORM, BIGSECRET, secret_len, BIGSTRING, bigstring_len, hashbuf, &hblen); FAILED(rval, "sc_generate_keyed_hash() return code"); if (hashbuf_len[mlcount] > properlength) { if (hblen != properlength) { FAILED(SNMPERR_GENERR, "Wrong SHA1 hash length returned. (1)"); } } else if (hblen != hashbuf_len[mlcount]) { FAILED(SNMPERR_GENERR, "Wrong SHA1 hash length returned. (2)"); } rval = sc_check_keyed_hash(usmHMACSHA1AuthProtocol, USM_LENGTH_OID_TRANSFORM, BIGSECRET, secret_len, BIGSTRING, bigstring_len, hashbuf, hblen); FAILED(rval, "sc_check_keyed_hash() return code"); binary_to_hex(hashbuf, hblen, &s); printf("# hash buffer (len=%" NETSNMP_PRIz "u, request=%d): %s\n", hblen, hashbuf_len[mlcount], s); SNMP_FREE(s); SUCCESS("Keyed hash test using SHA1."); /* * Run the basic hash tests but vary the size MAC requests. */ if (hashbuf_len[++mlcount] != 0) { goto test_dokeyedhash_again; } return failcount; } /* end test_dokeyedhash() */ /*******************************************************************-o-****** * test_docrypt * * Returns: * Number of failures. */ int test_docrypt(void) { int rval = SNMPERR_SUCCESS, bigstring_len = strlen((const char *) BIGSTRING), secret_len = BYTESIZE(SNMP_TRANS_PRIVLEN_1DES), iv_len = BYTESIZE(SNMP_TRANS_PRIVLEN_1DES_IV); size_t buf_len = LOCAL_MAXBUF; size_t cryptbuf_len = LOCAL_MAXBUF; u_char buf[LOCAL_MAXBUF], cryptbuf[LOCAL_MAXBUF], secret[LOCAL_MAXBUF], iv[LOCAL_MAXBUF]; OUTPUT("Starting Test 1DES-CBC --"); memset(buf, 0, LOCAL_MAXBUF); memcpy(secret, BIGSECRET, secret_len); memcpy(iv, BKWDSECRET, iv_len); rval = sc_encrypt(usmDESPrivProtocol, USM_LENGTH_OID_TRANSFORM, secret, secret_len, iv, iv_len, BIGSTRING, bigstring_len, cryptbuf, &cryptbuf_len); FAILED(rval, "sc_encrypt() return code."); rval = sc_decrypt(usmDESPrivProtocol, USM_LENGTH_OID_TRANSFORM, secret, secret_len, iv, iv_len, cryptbuf, cryptbuf_len, buf, &buf_len); FAILED(rval, "sc_decrypt() return code."); /* ignore the pad */ buf_len -= buf[buf_len-1]; FAILED((buf_len != bigstring_len), "Decrypted buffer is the right length."); printf("# original length: %d\n", bigstring_len); printf("# output length: %" NETSNMP_PRIz "u\n", buf_len); FAILED((memcmp(buf, BIGSTRING, bigstring_len) != 0), "Decrypted buffer is the same as the original plaintext."); return failcount; } /* end test_docrypt() */