X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=chiark-tcl.git;a=blobdiff_plain;f=crypto%2Fcrypto.c;h=ee89f0e8abe2be9b512eb4dad08273c70efe877d;hp=9b79881941eaa6418495489830a3546df09b58a6;hb=7ed5ab23c04b08d569a8515b2eab39338eddd261;hpb=9af88eb2e41e2b6a73643948e31262eee08c5400 diff --git a/crypto/crypto.c b/crypto/crypto.c index 9b79881..ee89f0e 100644 --- a/crypto/crypto.c +++ b/crypto/crypto.c @@ -1,17 +1,27 @@ /* + * crypto - Tcl bindings for parts of the `nettle' crypto library + * Copyright 2006 Ian Jackson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. */ -#include -#include "hbytes.h" -#include "tables.h" -#include "serpent.h" +#include "chiark_tcl_crypto.h" -void memxor(Byte *dest, const Byte *src, int l) { - while (l--) *dest++ ^= *src++; -} - -const PadMethod padmethods[]= { +const PadOp cht_padop_entries[]= { { "un", 0, 0 }, { "ua", 0, 1 }, { "pn", 1, 0 }, @@ -19,35 +29,114 @@ const PadMethod padmethods[]= { { 0 } }; -int do_hbytes_pkcs5(ClientData cd, Tcl_Interp *ip, - const PadMethod *meth, HBytes_Var v, Tcl_Obj *block, - int *ok) { - int rc, blocksize, padlen, old_len, i; - Byte *padding; - const Byte *unpad; +typedef struct { + HBytes_Value *hb; + int pad, blocksize; /* 0 or 1 */ +} PadMethodClientData; + +int cht_do_hbcrypto_pad(ClientData cd, Tcl_Interp *ip, const PadOp *op, + HBytes_Var v, Tcl_Obj *blocksz, const PadMethodInfo *meth, + int methargsc, Tcl_Obj *const *methargsv) { + PadMethodClientData pmcd; + int rc; - if (meth->use_algname) { + if (op->use_algname) { const BlockCipherAlgInfo *alg; - alg= enum_lookup_cached(ip,block,blockcipheralginfos,"cipher alg for pad"); + alg= enum_lookup_cached(ip,blocksz, cht_blockcipheralginfo_entries, + "blockcipher alg for pad"); if (!alg) return TCL_ERROR; - blocksize= alg->blocksize; + pmcd.blocksize= alg->blocksize; } else { - rc= Tcl_GetIntFromObj(ip, block, &blocksize); if (rc) return rc; - if (blocksize < 1 || blocksize > 255) - return staticerr(ip, "block size out of pkcs#5 range 1..255", 0); + rc= Tcl_GetIntFromObj(ip, blocksz, &pmcd.blocksize); if (rc) return rc; + if (pmcd.blocksize < 1) cht_staticerr(ip, "block size must be at least 1", 0); + } + + pmcd.hb= v.hb; + pmcd.pad= op->pad; + + return meth->func(&pmcd,ip,methargsc,methargsv); +} + +int cht_do_padmethodinfo_rfc2406(ClientData cd, Tcl_Interp *ip, + Tcl_Obj *nxthdr_arg, int *ok) { + const PadMethodClientData *pmcd= (const void*)cd; + int i, rc, padlen, old_len; + + if (pmcd->blocksize > 256) + return cht_staticerr(ip, "block size too large for RFC2406 padding", 0); + + if (pmcd->pad) { + Byte *padding; + HBytes_Value nxthdr; + + rc= cht_pat_hb(ip,nxthdr_arg,&nxthdr); + if (rc) return rc; + + if (cht_hb_len(&nxthdr) != 1) return + cht_staticerr(ip, "RFC2406 next header field must be exactly 1 byte", 0); + padlen= pmcd->blocksize-1 - ((cht_hb_len(pmcd->hb)+1) % pmcd->blocksize); + padding= cht_hb_append(pmcd->hb, padlen+2); + for (i=1; i<=padlen; i++) + *padding++ = i; + *padding++ = padlen; + *padding++ = cht_hb_data(&nxthdr)[0]; + *ok= 1; + + } else { + const Byte *padding, *trailer; + HBytes_Value nxthdr; + Tcl_Obj *nxthdr_valobj, *ro; + + *ok= 0; + old_len= cht_hb_len(pmcd->hb); if (old_len % pmcd->blocksize) goto quit; + trailer= cht_hb_unappend(pmcd->hb, 2); if (!trailer) goto quit; + + padlen= trailer[0]; + cht_hb_array(&nxthdr,trailer+1,1); + nxthdr_valobj= cht_ret_hb(ip,nxthdr); + ro= Tcl_ObjSetVar2(ip,nxthdr_arg,0,nxthdr_valobj,TCL_LEAVE_ERR_MSG); + if (!ro) { Tcl_DecrRefCount(nxthdr_valobj); return TCL_ERROR; } + + padding= cht_hb_unappend(pmcd->hb, padlen); + for (i=1; i<=padlen; i++) + if (*padding++ != i) goto quit; + + *ok= 1; + + quit:; + } - if (meth->pad) { - padlen= blocksize - (hbytes_len(v.hb) % blocksize); - padding= hbytes_append(v.hb, padlen); + return TCL_OK; +} + +int cht_do_padmethodinfo_pkcs5(ClientData cd, Tcl_Interp *ip, int *ok) { + const PadMethodClientData *pmcd= (const void*)cd; + int padlen, old_len, i; + + if (pmcd->blocksize > 255) + return cht_staticerr(ip, "block size too large for pkcs#5", 0); + + if (pmcd->pad) { + + Byte *padding; + + padlen= pmcd->blocksize - (cht_hb_len(pmcd->hb) % pmcd->blocksize); + padding= cht_hb_append(pmcd->hb, padlen); memset(padding, padlen, padlen); + } else { - old_len= hbytes_len(v.hb); if (old_len % blocksize) goto bad; - unpad= hbytes_unappend(v.hb, 1); if (!unpad) goto bad; - padlen= *unpad; - if (padlen < 1 || padlen > blocksize) goto bad; - unpad= hbytes_unappend(v.hb, padlen-1); if (!unpad) goto bad; - for (i=0; ihb); if (old_len % pmcd->blocksize) goto bad; + padding= cht_hb_unappend(pmcd->hb, 1); if (!padding) goto bad; + padlen= *padding; + if (padlen < 1 || padlen > pmcd->blocksize) goto bad; + padding= cht_hb_unappend(pmcd->hb, padlen-1); if (!padding) goto bad; + + for (i=0; ihashsize); - alg->oneshot(dest, hbytes_data(&message), hbytes_len(&message)); + dest= cht_hb_arrayspace(result,alg->hashsize); + alg->oneshot(dest, cht_hb_data(&message), cht_hb_len(&message)); return TCL_OK; } @@ -102,34 +191,34 @@ static void key_t_dup(Tcl_Obj *src_obj, Tcl_Obj *dup_obj) { memcpy(dup->value, src->value, src->valuelen); noalg(dup); dup_obj->internalRep.otherValuePtr= dup; - dup_obj->typePtr= &blockcipherkey_type; + dup_obj->typePtr= &cht_blockcipherkey_type; } static void key_t_ustr(Tcl_Obj *o) { - obj_updatestr_array(o, OBJ_CIPHKEY(o)->value, OBJ_CIPHKEY(o)->valuelen); + cht_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; + rc= Tcl_ConvertToType(ip,o,&cht_hbytes_type); if (rc) return rc; val= TALLOC(sizeof(*val)); - val->valuelen= l= hbytes_len(OBJ_HBYTES(o)); + val->valuelen= l= cht_hb_len(OBJ_HBYTES(o)); val->value= TALLOC(l); val->buffers= 0; val->bufferslen= 0; - memcpy(val->value, hbytes_data(OBJ_HBYTES(o)), l); + memcpy(val->value, cht_hb_data(OBJ_HBYTES(o)), l); noalg(val); - objfreeir(o); + cht_objfreeir(o); o->internalRep.otherValuePtr= val; - o->typePtr= &blockcipherkey_type; + o->typePtr= &cht_blockcipherkey_type; return TCL_OK; } -Tcl_ObjType blockcipherkey_type = { +Tcl_ObjType cht_blockcipherkey_type = { "blockcipher-key", key_t_free, key_t_dup, key_t_ustr, key_t_sfa }; @@ -139,7 +228,7 @@ static CiphKeyValue *get_key(Tcl_Interp *ip, Tcl_Obj *key_obj, CiphKeyValue *key; int rc; - rc= Tcl_ConvertToType(ip,key_obj,&blockcipherkey_type); if (rc) return 0; + rc= Tcl_ConvertToType(ip,key_obj,&cht_blockcipherkey_type); if (rc) return 0; key= OBJ_CIPHKEY(key_obj); if (key->alg != alg) { @@ -156,7 +245,7 @@ static CiphKeyValue *get_key(Tcl_Interp *ip, Tcl_Obj *key_obj, return key; } -int do_hbytes_blockcipher(ClientData cd, Tcl_Interp *ip, +int cht_do_hbcrypto_blockcipher(ClientData cd, Tcl_Interp *ip, const BlockCipherOp *op, int objc, Tcl_Obj *const *objv) { return op->func((void*)op,ip,objc,objv); @@ -175,7 +264,7 @@ static int blockcipher_prep(Tcl_Interp *ip, Tcl_Obj *key_obj, CiphKeyValue *key; if (data_len % alg->blocksize) - return staticerr(ip, "block cipher input not whole number of blocks", + return cht_staticerr(ip, "block cipher input not whole number of blocks", "HBYTES BLOCKCIPHER LENGTH"); want_bufferslen= alg->blocksize * (mode->buf_blocks + mode->iv_blocks); @@ -186,9 +275,9 @@ static int blockcipher_prep(Tcl_Interp *ip, Tcl_Obj *key_obj, sched= *schedp; if (!sched) { if (key->valuelen < alg->key_min) - return staticerr(ip, "key too short", "HBYTES BLOCKCIPHER PARAMS"); + return cht_staticerr(ip, "key too short", "HBYTES BLOCKCIPHER PARAMS"); if (key->valuelen > alg->key_max) - return staticerr(ip, "key too long", "HBYTES BLOCKCIPHER PARAMS"); + return cht_staticerr(ip, "key too long", "HBYTES BLOCKCIPHER PARAMS"); sched= TALLOC(alg->schedule_size); (decrypt ? &alg->decrypt : &alg->encrypt)->make_schedule @@ -198,18 +287,18 @@ static int blockcipher_prep(Tcl_Interp *ip, Tcl_Obj *key_obj, 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", 0); - } else if (hbytes_issentinel(iv)) { - if (decrypt) return staticerr(ip,"must supply iv when decrypting", 0); - rc= get_urandom(ip, key->buffers, want_iv); + if (!cht_hb_issentinel(iv)) + return cht_staticerr(ip,"iv supplied but mode does not take one", 0); + } else if (cht_hb_issentinel(iv)) { + if (decrypt) return cht_staticerr(ip,"must supply iv when decrypting", 0); + rc= cht_get_urandom(ip, key->buffers, want_iv); if (rc) return rc; } else { - int iv_supplied= hbytes_len(iv); + int iv_supplied= cht_hb_len(iv); if (iv_supplied > want_iv) - return staticerr(ip, "iv too large for algorithm and mode", + return cht_staticerr(ip, "iv too large for algorithm and mode", "HBYTES BLOCKCIPHER PARAMS"); - memcpy(key->buffers, hbytes_data(iv), iv_supplied); + memcpy(key->buffers, cht_hb_data(iv), iv_supplied); memset(key->buffers + iv_supplied, 0, want_iv - iv_supplied); } @@ -225,14 +314,14 @@ static int blockcipher_prep(Tcl_Interp *ip, Tcl_Obj *key_obj, return TCL_OK; } -int do_blockcipherop_d(ClientData cd, Tcl_Interp *ip, +int cht_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); + return cht_do_blockcipherop_e(cd,ip,v,alg,key_obj,mode,iv,result); } -int do_blockcipherop_e(ClientData cd, Tcl_Interp *ip, +int cht_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) { @@ -247,10 +336,10 @@ int do_blockcipherop_e(ClientData cd, Tcl_Interp *ip, int nblocks; if (!mode->encrypt) - return staticerr(ip, "mode does not support encrypt/decrypt", 0); + return cht_staticerr(ip, "mode does not support encrypt/decrypt", 0); rc= blockcipher_prep(ip,key_obj,&iv,!encrypt, - alg,mode, hbytes_len(v.hb), + alg,mode, cht_hb_len(v.hb), &key,&sched, &ivbuf,&iv_lenbytes, &buffers,&nblocks); @@ -258,17 +347,17 @@ int do_blockcipherop_e(ClientData cd, Tcl_Interp *ip, failure= (encrypt ? mode->encrypt : mode->decrypt) - (hbytes_data(v.hb), nblocks, ivbuf, buffers, alg, encrypt, sched); + (cht_hb_data(v.hb), nblocks, ivbuf, buffers, alg, encrypt, sched); if (failure) - return staticerr(ip, failure, "HBYTES BLOCKCIPHER CRYPTFAIL CRYPT"); + return cht_staticerr(ip, failure, "HBYTES BLOCKCIPHER CRYPTFAIL CRYPT"); - hbytes_array(result, ivbuf, iv_lenbytes); + cht_hb_array(result, ivbuf, iv_lenbytes); return TCL_OK; } -int do_blockcipherop_mac(ClientData cd, Tcl_Interp *ip, +int cht_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) { @@ -281,25 +370,25 @@ int do_blockcipherop_mac(ClientData cd, Tcl_Interp *ip, int rc; if (!mode->mac) - return staticerr(ip, "mode does not support mac generation", 0); + return cht_staticerr(ip, "mode does not support mac generation", 0); rc= blockcipher_prep(ip,key_obj,&iv,0, - alg,mode, hbytes_len(&msg), + alg,mode, cht_hb_len(&msg), &key,&sched, &ivbuf,&iv_lenbytes, &buffers,&nblocks); if (rc) return rc; - failure= mode->mac(hbytes_data(&msg), nblocks, ivbuf, buffers, alg, sched); + failure= mode->mac(cht_hb_data(&msg), nblocks, ivbuf, buffers, alg, sched); if (failure) - return staticerr(ip,failure, "HBYTES BLOCKCIPHER CRYPTFAIL MAC"); + return cht_staticerr(ip,failure, "HBYTES BLOCKCIPHER CRYPTFAIL MAC"); - hbytes_array(result, buffers, alg->blocksize * mode->mac_blocks); + cht_hb_array(result, buffers, alg->blocksize * mode->mac_blocks); return TCL_OK; } -int do_hbytes_hmac(ClientData cd, Tcl_Interp *ip, const HashAlgInfo *alg, +int cht_do_hbcrypto_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 @@ -313,7 +402,7 @@ int do_hbytes_hmac(ClientData cd, Tcl_Interp *ip, const HashAlgInfo *alg, 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", + return cht_staticerr(ip, "requested hmac output size out of range", "HBYTES HMAC PARAMS"); } else { ml= alg->hashsize; @@ -327,7 +416,7 @@ int do_hbytes_hmac(ClientData cd, Tcl_Interp *ip, const HashAlgInfo *alg, assert(!key->beta); if (key->valuelen > alg->blocksize) - return staticerr(ip, "key to hmac longer than hash block size", + return cht_staticerr(ip, "key to hmac longer than hash block size", "HBYTES HMAC PARAMS"); memcpy(key->buffers, key->value, key->valuelen); @@ -345,29 +434,29 @@ int do_hbytes_hmac(ClientData cd, Tcl_Interp *ip, const HashAlgInfo *alg, } assert(key->beta); - dest= hbytes_arrayspace(result, alg->hashsize); + dest= cht_hb_arrayspace(result, alg->hashsize); memcpy(key->buffers, key->alpha, alg->statesize); - alg->update(key->buffers, hbytes_data(&message), hbytes_len(&message)); + alg->update(key->buffers, cht_hb_data(&message), cht_hb_len(&message)); alg->final(key->buffers, dest); memcpy(key->buffers, key->beta, alg->statesize); alg->update(key->buffers, dest, alg->hashsize); alg->final(key->buffers, dest); - hbytes_unappend(result, alg->hashsize - ml); + cht_hb_unappend(result, alg->hashsize - ml); return TCL_OK; } -int do_blockcipherop_prop(ClientData cd, Tcl_Interp *ip, +int cht_do_blockcipherop_prop(ClientData cd, Tcl_Interp *ip, const BlockCipherPropInfo *prop, const BlockCipherAlgInfo *alg, int *result) { *result= *(const int*)((const char*)alg + prop->int_offset); return TCL_OK; } -int do_hbytes_hash_prop(ClientData cd, Tcl_Interp *ip, +int cht_do_hbcrypto_hash_prop(ClientData cd, Tcl_Interp *ip, const HashAlgPropInfo *prop, const HashAlgInfo *alg, int *result) { *result= *(const int*)((const char*)alg + prop->int_offset);