chiark / gitweb /
214b7fbde7256ea5869c3dc4b4395e8fd969d6a1
[chiark-tcl.git] / crypto / crypto.c
1 /*
2  */
3
4 #include <endian.h>
5
6 #include "hbytes.h"
7 #include "tables.h"
8 #include "serpent.h"
9
10 void memxor(Byte *dest, const Byte *src, int l) {
11   while (l--) *dest++ ^= *src++;
12 }
13
14 const PadMethod padmethods[]= {
15   { "un", 0, 0 },
16   { "ua", 0, 1 },
17   { "pn", 1, 0 },
18   { "pa", 1, 1 },
19   { 0 }
20 };
21
22 int do_hbytes_pkcs5(ClientData cd, Tcl_Interp *ip,
23                     const PadMethod *meth, HBytes_Var v, Tcl_Obj *block,
24                     int *ok) {
25   int rc, blocksize, padlen, old_len, i;
26   Byte *padding;
27   const Byte *unpad;
28   
29   if (meth->use_algname) {
30     const BlockCipherAlgInfo *alg;
31     alg= enum_lookup_cached(ip,block,blockcipheralginfos,"cipher alg for pad");
32     if (!alg) return TCL_ERROR;
33     blocksize= alg->blocksize;
34   } else {
35     rc= Tcl_GetIntFromObj(ip, block, &blocksize);  if (rc) return rc;
36     if (blocksize < 1 || blocksize > 255)
37       return staticerr(ip, "block size out of pkcs#5 range 1..255");
38   }
39
40   if (meth->pad) {
41     padlen= blocksize - (hbytes_len(v.hb) % blocksize);
42     padding= hbytes_append(v.hb, padlen);
43     memset(padding, padlen, padlen);
44   } else {
45     old_len= hbytes_len(v.hb);  if (old_len % blocksize) goto bad;
46     unpad= hbytes_unappend(v.hb, 1);  if (!unpad) goto bad;
47     padlen= *unpad;
48     if (padlen < 1 || padlen > blocksize) goto bad;
49     unpad= hbytes_unappend(v.hb, padlen-1);  if (!unpad) goto bad;
50     for (i=0; i<padlen-1; i++, unpad++) if (*unpad != padlen) goto bad;
51   }
52
53   *ok= 1;
54   return TCL_OK;
55
56  bad:
57   *ok= 0;
58   return TCL_OK;
59 }
60
61 #define OBJ_CIPHKEY(o) ((CiphKeyValue*)(o)->internalRep.otherValuePtr)
62
63 typedef struct {
64   int valuelen, bufferslen;
65   Byte *value, *buffers;
66   const BlockCipherAlgInfo *alg;
67   void *encrypt, *decrypt; /* key schedules; each may be 0 */
68 } CiphKeyValue;
69
70 static void freealg(CiphKeyValue *key) {
71   TFREE(key->encrypt);
72   TFREE(key->decrypt);
73 }
74
75 static void key_t_free(Tcl_Obj *obj) {
76   CiphKeyValue *key= OBJ_CIPHKEY(obj);
77   freealg(key);
78   TFREE(key->value);
79   TFREE(key->buffers);
80 }
81
82 static void noalg(CiphKeyValue *key) {
83   key->alg= 0;
84   key->encrypt= key->decrypt= 0;
85 }
86
87 static void key_t_dup(Tcl_Obj *src_obj, Tcl_Obj *dup_obj) {
88   CiphKeyValue *src= OBJ_CIPHKEY(src_obj);
89   CiphKeyValue *dup= TALLOC(sizeof(*dup));
90   dup->valuelen= src->valuelen;
91   dup->value= src->valuelen ? TALLOC(src->valuelen) : 0;
92   dup->buffers= 0; dup->bufferslen= 0;
93   memcpy(dup->value, src->value, src->valuelen);
94   noalg(dup);
95   dup_obj->internalRep.otherValuePtr= dup;
96 }
97
98 static void key_t_ustr(Tcl_Obj *o) {
99   obj_updatestr_array(o, OBJ_CIPHKEY(o)->value, OBJ_CIPHKEY(o)->valuelen);
100 }
101
102 static int key_t_sfa(Tcl_Interp *ip, Tcl_Obj *o) {
103   int rc, l;
104   CiphKeyValue *val;
105
106   rc= Tcl_ConvertToType(ip,o,&hbytes_type);  if (rc) return rc;
107   val= TALLOC(sizeof(*val));
108   val->valuelen= l= hbytes_len(OBJ_HBYTES(o));
109   val->value= TALLOC(l);
110   val->buffers= 0;
111   val->bufferslen= 0;
112   memcpy(val->value, hbytes_data(OBJ_HBYTES(o)), l);
113   noalg(val);
114
115   objfreeir(o);
116   o->internalRep.otherValuePtr= val;
117   o->typePtr= &blockcipherkey_type;
118
119   return TCL_OK;
120 }
121   
122 Tcl_ObjType blockcipherkey_type = {
123   "blockcipher-key",
124   key_t_free, key_t_dup, key_t_ustr, key_t_sfa
125 };
126
127 int do_hbytes_blockciph(ClientData cd, Tcl_Interp *ip, int encrypt,
128                         HBytes_Var v, const BlockCipherAlgInfo *alg,
129                         Tcl_Obj *key_obj, const BlockCipherModeInfo *mode,
130                         HBytes_Value iv, HBytes_Value *result) {
131   int rc, want_bufferslen, data_len, iv_want;
132   CiphKeyValue *key;
133   const char *failure;
134   void *sched, **schedp;
135
136   rc= Tcl_ConvertToType(ip,key_obj,&blockcipherkey_type);  if (rc) return rc;
137   key= OBJ_CIPHKEY(key_obj);
138
139   if (key->alg != alg) {
140     freealg(key);
141     noalg(key);
142
143     if (key->valuelen < alg->key_min) return staticerr(ip, "key too short");
144     if (key->valuelen > alg->key_max) return staticerr(ip, "key too long");
145     key->alg= alg;
146   }
147
148   schedp= (alg->decrypt.make_schedule!=alg->encrypt.make_schedule
149            && !encrypt) ? &key->decrypt : &key->encrypt;
150   sched= *schedp;
151   if (!sched) {
152     sched= TALLOC(alg->schedule_size);
153     (encrypt ? &alg->encrypt : &alg->decrypt)->make_schedule
154       (sched, key->value, key->valuelen);
155     *schedp= sched;
156   }
157   
158   want_bufferslen= alg->blocksize * (mode->buf_blocks + mode->iv_blocks);
159   if (key->bufferslen < want_bufferslen) {
160     TFREE(key->buffers);
161     key->buffers= TALLOC(want_bufferslen);
162     key->bufferslen= want_bufferslen;
163   }
164
165   iv_want= alg->blocksize * mode->iv_blocks;
166   if (hbytes_issentinel(&iv)) {
167     if (!encrypt) return staticerr(ip,"must supply iv when decrypting");
168     rc= get_urandom(ip, key->buffers, iv_want);
169     if (rc) return rc;
170   } else {
171     int iv_supplied= hbytes_len(&iv);
172     if (iv_supplied > iv_want)
173       return staticerr(ip, "iv too large for algorithm and mode");
174     memcpy(key->buffers, hbytes_data(&iv), iv_supplied);
175     memset(key->buffers + iv_supplied, 0, iv_want - iv_supplied);
176   }
177
178   data_len= hbytes_len(v.hb);
179   if (data_len % alg->blocksize)
180     return staticerr(ip, "block cipher input not whole number of blocks");
181
182   failure=
183     (encrypt ? mode->encrypt : mode->decrypt)
184     (hbytes_data(v.hb), data_len / alg->blocksize,
185      key->buffers, key->buffers + iv_want,
186      alg, encrypt,
187      alg->blocksize, sched);
188
189   if (failure)
190     return staticerr(ip, failure);
191
192   hbytes_array(result, key->buffers, iv_want);
193
194   return TCL_OK;
195 }