* 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
*
* 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
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[];
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[];
v hb
count int
=> hb
+ clockincrement
+ value hbv
+ change int
+ => int
random
length int
=> hb
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
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;
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);
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);
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);
}
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 }
};
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;
}
* 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
*
* 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
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[];
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[];
/* 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");