X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=chiark-tcl.git;a=blobdiff_plain;f=crypto%2Fcrypto.c;h=7cc2914c65908d6b681617d0989d36c2bc54ba82;hp=f6f12e1e70c568adeec252ee9379964834f89679;hb=b845521abfac164a92742f984eafb91d5d7c743d;hpb=bc4e7d2673e44826dd768ad7f91c393349da24de diff --git a/crypto/crypto.c b/crypto/crypto.c index f6f12e1..7cc2914 100644 --- a/crypto/crypto.c +++ b/crypto/crypto.c @@ -57,3 +57,227 @@ int do_hbytes_pkcs5(ClientData cd, Tcl_Interp *ip, *ok= 0; return TCL_OK; } + +int do_hbytes_hash(ClientData cd, Tcl_Interp *ip, const HashAlgInfo *alg, + HBytes_Value message, HBytes_Value *result) { + Byte *dest; + + dest= hbytes_arrayspace(result,alg->hashsize); + alg->oneshot(dest, hbytes_data(&message), hbytes_len(&message)); + return TCL_OK; +} + +#define OBJ_CIPHKEY(o) ((CiphKeyValue*)(o)->internalRep.otherValuePtr) + +typedef struct { + int valuelen, bufferslen; + Byte *value, *buffers; + const void *alg; + void *alpha, *beta; /* key schedules etc.; each may be 0 */ +} CiphKeyValue; + +static void freealg(CiphKeyValue *key) { + TFREE(key->alpha); + TFREE(key->beta); +} + +static void key_t_free(Tcl_Obj *obj) { + CiphKeyValue *key= OBJ_CIPHKEY(obj); + freealg(key); + TFREE(key->value); + TFREE(key->buffers); +} + +static void noalg(CiphKeyValue *key) { + key->alg= 0; + key->alpha= key->beta= 0; +} + +static void key_t_dup(Tcl_Obj *src_obj, Tcl_Obj *dup_obj) { + CiphKeyValue *src= OBJ_CIPHKEY(src_obj); + CiphKeyValue *dup= TALLOC(sizeof(*dup)); + dup->valuelen= src->valuelen; + dup->value= src->valuelen ? TALLOC(src->valuelen) : 0; + dup->buffers= 0; dup->bufferslen= 0; + memcpy(dup->value, src->value, src->valuelen); + noalg(dup); + dup_obj->internalRep.otherValuePtr= dup; +} + +static void key_t_ustr(Tcl_Obj *o) { + obj_updatestr_array(o, OBJ_CIPHKEY(o)->value, OBJ_CIPHKEY(o)->valuelen); +} + +static int key_t_sfa(Tcl_Interp *ip, Tcl_Obj *o) { + int rc, l; + CiphKeyValue *val; + + rc= Tcl_ConvertToType(ip,o,&hbytes_type); if (rc) return rc; + val= TALLOC(sizeof(*val)); + val->valuelen= l= hbytes_len(OBJ_HBYTES(o)); + val->value= TALLOC(l); + val->buffers= 0; + val->bufferslen= 0; + memcpy(val->value, hbytes_data(OBJ_HBYTES(o)), l); + noalg(val); + + objfreeir(o); + o->internalRep.otherValuePtr= val; + o->typePtr= &blockcipherkey_type; + + return TCL_OK; +} + +Tcl_ObjType blockcipherkey_type = { + "blockcipher-key", + key_t_free, key_t_dup, key_t_ustr, key_t_sfa +}; + +CiphKeyValue *get_key(Tcl_Interp *ip, Tcl_Obj *key_obj, + const void *alg, int want_bufferslen) { + CiphKeyValue *key; + int rc; + + rc= Tcl_ConvertToType(ip,key_obj,&blockcipherkey_type); if (rc) return 0; + key= OBJ_CIPHKEY(key_obj); + + if (key->alg != alg) { + freealg(key); + noalg(key); + key->alg= alg; + } + + if (key->bufferslen < want_bufferslen) { + TFREE(key->buffers); + key->buffers= TALLOC(want_bufferslen); + key->bufferslen= want_bufferslen; + } + 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; + void *sched, **schedp; + + want_bufferslen= alg->blocksize * (mode->buf_blocks + mode->iv_blocks); + key= get_key(ip, key_obj, alg, want_bufferslen); + + schedp= (alg->decrypt.make_schedule==alg->encrypt.make_schedule + || encrypt) ? &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 + (sched, key->value, key->valuelen); + *schedp= sched; + } + + iv_want= alg->blocksize * mode->iv_blocks; + if (hbytes_issentinel(&iv)) { + if (!encrypt) return staticerr(ip,"must supply iv when decrypting"); + rc= get_urandom(ip, key->buffers, iv_want); + if (rc) return rc; + } else { + int iv_supplied= hbytes_len(&iv); + if (iv_supplied > iv_want) + 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); + } + + data_len= hbytes_len(v.hb); + if (data_len % alg->blocksize) + return staticerr(ip, "block cipher input not whole number of blocks"); + + 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); + + if (failure) + return staticerr(ip, failure); + + hbytes_array(result, key->buffers, iv_want); + + return TCL_OK; +} + +static void dbuf(const char *m, const Byte *a, int l) { + fprintf(stderr,"dbuf %s l=%d ",m,l); + while (l-->0) fprintf(stderr,"%02x",*a++); + putc('\n',stderr); +} + +int do_hbytes_hmac(ClientData cd, Tcl_Interp *ip, const HashAlgInfo *alg, + HBytes_Value message, Tcl_Obj *key_obj, + Tcl_Obj *maclen_obj, HBytes_Value *result) { + /* key->alpha = state after H(K XOR ipad + * key->beta = state after H(K XOR opad + * key->buffers = room for one block, or one state + */ + CiphKeyValue *key; + Byte *dest; + int i, ml, rc; + + if (maclen_obj) { + rc= Tcl_GetIntFromObj(ip, maclen_obj, &ml); if (rc) return rc; + if (ml<0 || ml>alg->hashsize) + return staticerr(ip, "requested hmac output size out of range"); + } else { + ml= alg->hashsize; + } + + key= get_key(ip, key_obj, alg, + alg->blocksize > alg->statesize + ? alg->blocksize : alg->statesize); + + if (!key->alpha) { + assert(!key->beta); + + if (key->valuelen > alg->blocksize) + return staticerr(ip, "key to hmac longer than hash block size"); + +dbuf("start key",key->value,key->valuelen); + memcpy(key->buffers, key->value, key->valuelen); + memset(key->buffers + key->valuelen, 0, alg->blocksize - key->valuelen); + for (i=0; iblocksize; i++) key->buffers[i] ^= 0x36; + + key->alpha= TALLOC(alg->statesize); + alg->init(key->alpha); +dbuf("inner key",key->buffers,alg->blocksize); + alg->update(key->alpha, key->buffers, alg->blocksize); + + key->beta= TALLOC(alg->statesize); + alg->init(key->beta); + for (i=0; iblocksize; i++) key->buffers[i] ^= (0x5c ^ 0x36); + alg->update(key->beta, key->buffers, alg->blocksize); +dbuf("inner key",key->buffers,alg->blocksize); + } + assert(key->beta); + + dest= hbytes_arrayspace(result, alg->hashsize); + + memcpy(key->buffers, key->alpha, alg->statesize); + alg->update(key->buffers, hbytes_data(&message), hbytes_len(&message)); + alg->final(key->buffers, dest); +dbuf("inner hash",dest,alg->hashsize); + + memcpy(key->buffers, key->beta, alg->statesize); + alg->update(key->buffers, dest, alg->hashsize); + alg->final(key->buffers, dest); +dbuf("outer hash",dest,alg->hashsize); + + hbytes_unappend(result, alg->hashsize - ml); + + return TCL_OK; +}