From: ian Date: Wed, 18 Sep 2002 23:07:41 +0000 (+0000) Subject: New blockcipher mac stuff. New hbytes clockincrement. X-Git-Tag: debian/1.1.1~151 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=chiark-tcl.git;a=commitdiff_plain;h=ac8c0b3b18075ae4273779544eda01c09cfb5145;ds=sidebyside New blockcipher mac stuff. New hbytes clockincrement. --- diff --git a/base/chiark-tcl.h b/base/chiark-tcl.h index ce00dd9..ac19c8f 100644 --- a/base/chiark-tcl.h +++ b/base/chiark-tcl.h @@ -18,6 +18,11 @@ * hbytes trimleft VALUE removes any leading 0 octets * hbytes repeat VALUE COUNT => COUNT copies of VALUE * + * hbytes clockincrement VAR INTEGER adds INTEGER to VAR mod 256^|VAR| + * INTEGER must be -255 .. 255 + * => carry (-255 to 255, + * and -1,0,1 if VAR not empty) + * * hbytes h2ulong HEX => ulong (HEX len must be 4) * hbytes ulong2h UL => hex * @@ -48,6 +53,7 @@ * hbytes pkcs5 pa|ua VAR ALG => worked? (always 1 for p) * hbytes pkcs5 pn|un VAR BLOCKSIZE => worked? (always 1 for p) * hbytes blockcipher d|e VAR ALG KEY MODE [IV] => IV + * hbytes blockcipher mac MSG ALG KEY MODE IV => final block * * hbytes hash ALG MESSAGE => hash * hbytes hmac ALG MESSAGE KEY [MACLENGTH] => mac @@ -232,13 +238,13 @@ typedef struct { void (*crypt)(const void *schedule, const void *in, void *out); /* in and out may be the same, but if they aren't they may not overlap */ /* in and out for crypt will have been through block_byteswap */ -} BlockCipherDirectionInfo; +} BlockCipherPerDirectionInfo; typedef struct { const char *name; int blocksize, schedule_size, key_min, key_max; void (*byteswap)(void *block); - BlockCipherDirectionInfo encrypt, decrypt; + BlockCipherPerDirectionInfo encrypt, decrypt; } BlockCipherAlgInfo; extern const BlockCipherAlgInfo blockcipheralginfos[]; @@ -247,16 +253,31 @@ extern const BlockCipherAlgInfo blockcipheralginfos[]; typedef struct { const char *name; - int iv_blocks, buf_blocks; - const char *(*encrypt)(Byte *data, int blocks, + int iv_blocks, buf_blocks, mac_blocks; + + /* Each function is allowed to use up to buf_blocks * blocksize + * bytes of space in buf. data is blocks * blocksize bytes + * long. data should be modified in place by encrypt and decrypt; + * modes may not change the size of data. iv is always provided and + * is always of length iv_blocks * blocksize; encrypt and + * decrypt may modify the iv value (in which case the Tcl caller + * will get the modified IV) but this is not recommended. mac + * should leave the mac, which must be mac_blocks * blocksize + * bytes, in buf. (Therefore mac_blocks must be at least + * buf_blocks.) + */ + const char *(*encrypt)(Byte *data, int nblocks, const Byte *iv, Byte *buf, const BlockCipherAlgInfo *alg, int encr, - int blocksize, const void *sch); - const char *(*decrypt)(Byte *data, int blocks, + const void *sch); + const char *(*decrypt)(Byte *data, int nblocks, const Byte *iv, Byte *buf, const BlockCipherAlgInfo *alg, int encr, - int blocksize, const void *sch); - /* in each case, *iv is provided, but may be modified */ + const void *sch); + const char *(*mac)(const Byte *data, int nblocks, + const Byte *iv, Byte *buf, + const BlockCipherAlgInfo *alg, + const void *sch); } BlockCipherModeInfo; extern const BlockCipherModeInfo blockciphermodeinfos[]; diff --git a/base/tables-examples.tct b/base/tables-examples.tct index d2e0038..c130136 100644 --- a/base/tables-examples.tct +++ b/base/tables-examples.tct @@ -121,6 +121,10 @@ Table hbytes HBytes_SubCommand v hb count int => hb + clockincrement + value hbv + change int + => int random length int => hb @@ -130,13 +134,8 @@ Table hbytes HBytes_SubCommand block obj => int blockcipher - encrypt charfrom("de","encrypt/decrypt") - v hbv - alg enum(BlockCipherAlgInfo, "alg") - key obj - mode enum(BlockCipherModeInfo, "mode") - ?iv hb - => hb + op enum(BlockCipherOp, "op") + ... obj hash alg enum(HashAlgInfo, "hash alg") message hb @@ -161,3 +160,29 @@ Table dgram_socket DgramSocket_SubCommand on-receive sock sockid ?script obj + +Table blockcipherop BlockCipherOp + e 1 + v hbv + alg enum(BlockCipherAlgInfo, "alg") + key obj + mode enum(BlockCipherModeInfo, "mode") + ?iv hb + => hb + d 0 + v hbv + alg enum(BlockCipherAlgInfo, "alg") + key obj + mode enum(BlockCipherModeInfo, "mode") + ?iv hb + => hb + mac -1 + msg hb + alg enum(BlockCipherAlgInfo, "alg") + key obj + mode enum(BlockCipherModeInfo, "mode") + iv hb + => hb + +EntryExtra BlockCipherOp + int encrypt; diff --git a/crypto/bcmode.c b/crypto/bcmode.c index f7c808d..f6a2a4b 100644 --- a/crypto/bcmode.c +++ b/crypto/bcmode.c @@ -6,7 +6,8 @@ static const char *mode_cbc_encrypt(Byte *data, int blocks, const Byte *iv, Byte *chain, const BlockCipherAlgInfo *alg, int encr, - int blocksize, const void *sch) { + const void *sch) { + int blocksize= alg->blocksize; memcpy(chain,iv,blocksize); alg->byteswap(chain); @@ -26,7 +27,8 @@ static const char *mode_cbc_encrypt(Byte *data, int blocks, static const char *mode_cbc_decrypt(Byte *data, int blocks, const Byte *iv, Byte *chain, const BlockCipherAlgInfo *alg, int encr, - int blocksize, const void *sch) { + const void *sch) { + int blocksize= alg->blocksize; int cchain= 0; memcpy(chain,iv,blocksize); @@ -46,10 +48,51 @@ static const char *mode_cbc_decrypt(Byte *data, int blocks, return 0; } +static void cbcmac_core(const Byte *data, int blocks, + const Byte *iv, Byte *buf, + const BlockCipherAlgInfo *alg, + const void *sch) { + int blocksize= alg->blocksize; + + memcpy(buf,iv,blocksize); + alg->byteswap(buf); + + while (blocks > 0) { + memcpy(buf + blocksize, data, blocksize); + alg->byteswap(buf + blocksize); + memxor(buf, buf + blocksize, blocksize); + + alg->encrypt.crypt(sch, buf, buf); + + blocks--; data += blocksize; + } +} + +static const char *mode_cbc_mac(const Byte *data, int blocks, + const Byte *iv, Byte *buf, + const BlockCipherAlgInfo *alg, + const void *sch) { + cbcmac_core(data,blocks,iv,buf,alg,sch); + alg->byteswap(buf); + return 0; +} + +static const char *mode_cbc_mac2(const Byte *data, int blocks, + const Byte *iv, Byte *buf, + const BlockCipherAlgInfo *alg, + const void *sch) { + cbcmac_core(data,blocks,iv,buf,alg,sch); + alg->encrypt.crypt(sch, buf, buf); + alg->byteswap(buf); + return 0; +} + static const char *mode_ecb(Byte *data, int blocks, const Byte *iv, Byte *chain, const BlockCipherAlgInfo *alg, int encr, - int blocksize, const void *sch) { + const void *sch) { + int blocksize= alg->blocksize; + while (blocks > 0) { alg->byteswap(data); (encr ? &alg->encrypt : &alg->decrypt)->crypt(sch, data, data); @@ -60,7 +103,8 @@ static const char *mode_ecb(Byte *data, int blocks, } const BlockCipherModeInfo blockciphermodeinfos[]= { - { "cbc", 1, 2, mode_cbc_encrypt, mode_cbc_decrypt }, - { "ecb", 0, 0, mode_ecb, mode_ecb }, + { "cbc", 1, 2, 1, mode_cbc_encrypt, mode_cbc_decrypt, mode_cbc_mac }, + { "cbc-mac2", 1, 2, 1, 0, 0, mode_cbc_mac2 }, + { "ecb", 0, 0, 0, mode_ecb, mode_ecb, 0 }, { 0 } }; diff --git a/crypto/crypto.c b/crypto/crypto.c index 3961158..efddecd 100644 --- a/crypto/crypto.c +++ b/crypto/crypto.c @@ -155,62 +155,141 @@ static CiphKeyValue *get_key(Tcl_Interp *ip, Tcl_Obj *key_obj, return key; } -int do_hbytes_blockcipher(ClientData cd, Tcl_Interp *ip, int encrypt, - HBytes_Var v, const BlockCipherAlgInfo *alg, - Tcl_Obj *key_obj, const BlockCipherModeInfo *mode, - HBytes_Value iv, HBytes_Value *result) { - int rc, want_bufferslen, data_len, iv_want; - CiphKeyValue *key; - const char *failure; +int do_hbytes_blockcipher(ClientData cd, Tcl_Interp *ip, + const BlockCipherOp *op, + int objc, Tcl_Obj *const *objv) { + return op->func((void*)op,ip,objc,objv); +} + +static int blockcipher_prep(Tcl_Interp *ip, Tcl_Obj *key_obj, + const HBytes_Value *iv, int decrypt, + const BlockCipherAlgInfo *alg, + const BlockCipherModeInfo *mode, int data_len, + const CiphKeyValue **key_r, const void **sched_r, + const Byte **iv_r, int *iv_lenbytes_r, + Byte **buffers_r, int *nblocks_r) { void *sched, **schedp; + int want_bufferslen, want_iv; + int rc; + CiphKeyValue *key; + + if (data_len % alg->blocksize) + return staticerr(ip, "block cipher input not whole number of blocks"); want_bufferslen= alg->blocksize * (mode->buf_blocks + mode->iv_blocks); key= get_key(ip, key_obj, alg, want_bufferslen); if (!key) return TCL_ERROR; schedp= (alg->decrypt.make_schedule==alg->encrypt.make_schedule - || encrypt) ? &key->alpha : &key->beta; + || !decrypt) ? &key->alpha : &key->beta; sched= *schedp; if (!sched) { if (key->valuelen < alg->key_min) return staticerr(ip, "key too short"); if (key->valuelen > alg->key_max) return staticerr(ip, "key too long"); sched= TALLOC(alg->schedule_size); - (encrypt ? &alg->encrypt : &alg->decrypt)->make_schedule + (decrypt ? &alg->decrypt : &alg->encrypt)->make_schedule (sched, key->value, key->valuelen); *schedp= sched; } - iv_want= alg->blocksize * mode->iv_blocks; - if (!iv_want) { - if (!hbytes_issentinel(&iv)) + want_iv= alg->blocksize * mode->iv_blocks; + if (!want_iv) { + if (!hbytes_issentinel(iv)) return staticerr(ip,"iv supplied but mode does not take one"); - } else if (hbytes_issentinel(&iv)) { - if (!encrypt) return staticerr(ip,"must supply iv when decrypting"); - rc= get_urandom(ip, key->buffers, iv_want); + } else if (hbytes_issentinel(iv)) { + if (decrypt) return staticerr(ip,"must supply iv when decrypting"); + rc= get_urandom(ip, key->buffers, want_iv); if (rc) return rc; } else { - int iv_supplied= hbytes_len(&iv); - if (iv_supplied > iv_want) + int iv_supplied= hbytes_len(iv); + if (iv_supplied > want_iv) return staticerr(ip, "iv too large for algorithm and mode"); - memcpy(key->buffers, hbytes_data(&iv), iv_supplied); - memset(key->buffers + iv_supplied, 0, iv_want - iv_supplied); + memcpy(key->buffers, hbytes_data(iv), iv_supplied); + memset(key->buffers + iv_supplied, 0, want_iv - iv_supplied); } - data_len= hbytes_len(v.hb); - if (data_len % alg->blocksize) - return staticerr(ip, "block cipher input not whole number of blocks"); + *key_r= key; + *sched_r= sched; + + *iv_r= key->buffers; + *iv_lenbytes_r= want_iv; + + *buffers_r= key->buffers + want_iv; + *nblocks_r= data_len / alg->blocksize; + + return TCL_OK; +} + +int do_blockcipherop_d(ClientData cd, Tcl_Interp *ip, + HBytes_Var v, const BlockCipherAlgInfo *alg, + Tcl_Obj *key_obj, const BlockCipherModeInfo *mode, + HBytes_Value iv, HBytes_Value *result) { + return do_blockcipherop_e(cd,ip,v,alg,key_obj,mode,iv,result); +} +int do_blockcipherop_e(ClientData cd, Tcl_Interp *ip, + HBytes_Var v, const BlockCipherAlgInfo *alg, + Tcl_Obj *key_obj, const BlockCipherModeInfo *mode, + HBytes_Value iv, HBytes_Value *result) { + const BlockCipherOp *op= (const void*)cd; + int encrypt= op->encrypt; + int rc, iv_lenbytes; + const CiphKeyValue *key; + const char *failure; + const Byte *ivbuf; + Byte *buffers; + const void *sched; + int nblocks; + + if (!mode->encrypt) + return staticerr(ip, "mode does not support encrypt/decrypt"); + + rc= blockcipher_prep(ip,key_obj,&iv,!encrypt, + alg,mode, hbytes_len(v.hb), + &key,&sched, + &ivbuf,&iv_lenbytes, + &buffers,&nblocks); + if (rc) return rc; + failure= (encrypt ? mode->encrypt : mode->decrypt) - (hbytes_data(v.hb), data_len / alg->blocksize, - key->buffers, key->buffers + iv_want, - alg, encrypt, - alg->blocksize, sched); + (hbytes_data(v.hb), nblocks, ivbuf, buffers, alg, encrypt, sched); if (failure) return staticerr(ip, failure); - hbytes_array(result, key->buffers, iv_want); + hbytes_array(result, ivbuf, iv_lenbytes); + + return TCL_OK; +} + +int do_blockcipherop_mac(ClientData cd, Tcl_Interp *ip, + HBytes_Value msg, const BlockCipherAlgInfo *alg, + Tcl_Obj *key_obj, const BlockCipherModeInfo *mode, + HBytes_Value iv, HBytes_Value *result) { + const CiphKeyValue *key; + const char *failure; + const Byte *ivbuf; + Byte *buffers; + const void *sched; + int nblocks, iv_lenbytes; + int rc; + + if (!mode->mac) + return staticerr(ip, "mode does not support mac generation"); + + rc= blockcipher_prep(ip,key_obj,&iv,0, + alg,mode, hbytes_len(&msg), + &key,&sched, + &ivbuf,&iv_lenbytes, + &buffers,&nblocks); + if (rc) return rc; + + failure= mode->mac(hbytes_data(&msg), nblocks, ivbuf, buffers, alg, sched); + if (failure) + return staticerr(ip,failure); + + hbytes_array(result, buffers, alg->blocksize * mode->mac_blocks); return TCL_OK; } diff --git a/hbytes/hbytes.h b/hbytes/hbytes.h index ce00dd9..ac19c8f 100644 --- a/hbytes/hbytes.h +++ b/hbytes/hbytes.h @@ -18,6 +18,11 @@ * hbytes trimleft VALUE removes any leading 0 octets * hbytes repeat VALUE COUNT => COUNT copies of VALUE * + * hbytes clockincrement VAR INTEGER adds INTEGER to VAR mod 256^|VAR| + * INTEGER must be -255 .. 255 + * => carry (-255 to 255, + * and -1,0,1 if VAR not empty) + * * hbytes h2ulong HEX => ulong (HEX len must be 4) * hbytes ulong2h UL => hex * @@ -48,6 +53,7 @@ * hbytes pkcs5 pa|ua VAR ALG => worked? (always 1 for p) * hbytes pkcs5 pn|un VAR BLOCKSIZE => worked? (always 1 for p) * hbytes blockcipher d|e VAR ALG KEY MODE [IV] => IV + * hbytes blockcipher mac MSG ALG KEY MODE IV => final block * * hbytes hash ALG MESSAGE => hash * hbytes hmac ALG MESSAGE KEY [MACLENGTH] => mac @@ -232,13 +238,13 @@ typedef struct { void (*crypt)(const void *schedule, const void *in, void *out); /* in and out may be the same, but if they aren't they may not overlap */ /* in and out for crypt will have been through block_byteswap */ -} BlockCipherDirectionInfo; +} BlockCipherPerDirectionInfo; typedef struct { const char *name; int blocksize, schedule_size, key_min, key_max; void (*byteswap)(void *block); - BlockCipherDirectionInfo encrypt, decrypt; + BlockCipherPerDirectionInfo encrypt, decrypt; } BlockCipherAlgInfo; extern const BlockCipherAlgInfo blockcipheralginfos[]; @@ -247,16 +253,31 @@ extern const BlockCipherAlgInfo blockcipheralginfos[]; typedef struct { const char *name; - int iv_blocks, buf_blocks; - const char *(*encrypt)(Byte *data, int blocks, + int iv_blocks, buf_blocks, mac_blocks; + + /* Each function is allowed to use up to buf_blocks * blocksize + * bytes of space in buf. data is blocks * blocksize bytes + * long. data should be modified in place by encrypt and decrypt; + * modes may not change the size of data. iv is always provided and + * is always of length iv_blocks * blocksize; encrypt and + * decrypt may modify the iv value (in which case the Tcl caller + * will get the modified IV) but this is not recommended. mac + * should leave the mac, which must be mac_blocks * blocksize + * bytes, in buf. (Therefore mac_blocks must be at least + * buf_blocks.) + */ + const char *(*encrypt)(Byte *data, int nblocks, const Byte *iv, Byte *buf, const BlockCipherAlgInfo *alg, int encr, - int blocksize, const void *sch); - const char *(*decrypt)(Byte *data, int blocks, + const void *sch); + const char *(*decrypt)(Byte *data, int nblocks, const Byte *iv, Byte *buf, const BlockCipherAlgInfo *alg, int encr, - int blocksize, const void *sch); - /* in each case, *iv is provided, but may be modified */ + const void *sch); + const char *(*mac)(const Byte *data, int nblocks, + const Byte *iv, Byte *buf, + const BlockCipherAlgInfo *alg, + const void *sch); } BlockCipherModeInfo; extern const BlockCipherModeInfo blockciphermodeinfos[]; diff --git a/hbytes/ulongs.c b/hbytes/ulongs.c index 4962731..f8b8c9e 100644 --- a/hbytes/ulongs.c +++ b/hbytes/ulongs.c @@ -6,6 +6,30 @@ /* nice simple functions */ +int do_hbytes_clockincrement(ClientData cd, Tcl_Interp *ip, + HBytes_Var value, int change, int *result) { + Byte *data; + int len, bv; + + if (change<-255 || change>255) + return staticerr(ip,"clockincrement change must be in range -255..255"); + + len= hbytes_len(value.hb); + data= hbytes_data(value.hb) + len; + while (len && change) { + bv= *--data; + bv += change; + *data= bv; + if (bv<0) change= -1; + else if (bv>255) change= +1; + else change= 0; + len--; + } + *result= change; + + return TCL_OK; +} + int do_ulong_int2ul(ClientData cd, Tcl_Interp *ip, int v, unsigned long *result) { if (v<0) return staticerr(ip,"cannot convert -ve integer to ulong");