Check-in [49f76ba54c]
Overview
Comment:Added Cipher MAC (CMAC) support
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | crypto
Files: files | file ages | folders
SHA3-256: 49f76ba54c78b31e93eacea43c2c682c3c596ba654b2e64cca8cdc363faf47ea
User & Date: bohagan on 2023-11-10 20:09:23
Other Links: branch diff | manifest | tags
Context
2023-11-10
22:23
Added cmac and hmac convenience commands check-in: 992cc75b71 user: bohagan tags: crypto
20:09
Added Cipher MAC (CMAC) support check-in: 49f76ba54c user: bohagan tags: crypto
2023-11-08
03:09
Updated test cases check-in: 9b69dccaca user: bohagan tags: crypto
Changes
9
10
11
12
13
14
15


16
17
18
19
20
21
22
23
24



25
26
27
28
29
30
31
32
33
34
35
36

37
38
39
40

41
42
43
44
45
46
47
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

41
42
43
44
45
46
47
48
49
50
51
52
53







+
+









+
+
+











-
+




+








#include "tlsInt.h"
#include "tclOpts.h"
#include <tcl.h>
#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>
#include <openssl/cmac.h>
#include <openssl/hmac.h>

/* Constants */
const char *hex = "0123456789ABCDEF";

/* Macros */
#define BUFFER_SIZE 65536
#define BIN_FORMAT 0
#define HEX_FORMAT 1
#define CHAN_EOF 0x10
#define TYPE_MD    0x20
#define TYPE_HMAC  0x40
#define TYPE_CMAC  0x80

/*
 * This structure defines the per-instance state of a digest operation.
 */
typedef struct DigestState {
	Tcl_Channel self;	/* This socket channel */
	Tcl_TimerToken timer;	/* Timer for read events */

	int flags;		/* Chan config flags */
	int watchMask;		/* Current WatchProc mask */
	int mode;		/* Current mode of parent channel */
	int format;		/* Output format */
	int format;		/* Digest format and operation */

	Tcl_Interp *interp;	/* Current interpreter */
	EVP_MD_CTX *ctx;	/* MD Context */
	HMAC_CTX *hctx;		/* HMAC Context */
	CMAC_CTX *cctx;		/* CMAC Context */
	Tcl_Command token;	/* Command token */
} DigestState;

/*
 *-------------------------------------------------------------------
 *
 * Tls_DigestNew --
63
64
65
66
67
68
69
70

71
72
73


74
75
76
77
78
79
80
69
70
71
72
73
74
75

76
77
78
79
80
81
82
83
84
85
86
87
88







-
+



+
+







    if (statePtr != NULL) {
	memset(statePtr, 0, sizeof(DigestState));
	statePtr->self	= NULL;		/* This socket channel */
	statePtr->timer = NULL;		/* Timer to flush data */
	statePtr->flags = 0;		/* Chan config flags */
	statePtr->watchMask = 0;	/* Current WatchProc mask */
	statePtr->mode	= 0;		/* Current mode of parent channel */
	statePtr->format = format;	/* Output format */
	statePtr->format = format;	/* Digest format and operation */
	statePtr->interp = interp;	/* Current interpreter */
	statePtr->ctx = NULL;		/* MD Context */
	statePtr->hctx = NULL;		/* HMAC Context */
	statePtr->cctx = NULL;		/* CMAC Context */
	statePtr->token = NULL;		/* Command token */
    }
    return statePtr;
}

/*
 *-------------------------------------------------------------------
 *
97
98
99
100
101
102
103



104
105
106
107
108
109
110
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121







+
+
+








    if (statePtr->ctx != (EVP_MD_CTX *) NULL) {
	EVP_MD_CTX_free(statePtr->ctx);
    }
    if (statePtr->hctx != (HMAC_CTX *) NULL) {
	HMAC_CTX_free(statePtr->hctx);
    }
    if (statePtr->cctx != (CMAC_CTX *) NULL) {
	CMAC_CTX_free(statePtr->cctx);
    }
    ckfree(statePtr);
}

/*******************************************************************/

/*
 *-------------------------------------------------------------------
119
120
121
122
123
124
125
126
127


128
129
130
131

132
133
134

135
136



137
138
139
140
141
142
143
144

145
146

147
148



149
150
151
152
153
154
155
130
131
132
133
134
135
136


137
138
139
140
141

142
143
144

145
146
147
148
149
150
151
152
153
154
155
156
157

158
159

160
161
162
163
164
165
166
167
168
169
170
171
172







-
-
+
+



-
+


-
+


+
+
+







-
+

-
+


+
+
+







 *
 * Side effects:
 *	No result or error message
 *
 *-------------------------------------------------------------------
 */
int Tls_DigestInit(Tcl_Interp *interp, DigestState *statePtr, const EVP_MD *md,
	Tcl_Obj *keyObj) {
    int key_len, res;
	const EVP_CIPHER *cipher, Tcl_Obj *keyObj) {
    int key_len, res = 0;
    const unsigned char *key;

    /* Create message digest context */
    if (keyObj == NULL) {
    if (statePtr->format & TYPE_MD) {
	statePtr->ctx = EVP_MD_CTX_new();
	res = (statePtr->ctx != NULL);
    } else {
    } else if (statePtr->format & TYPE_HMAC) {
	statePtr->hctx = HMAC_CTX_new();
	res = (statePtr->hctx != NULL);
    } else if (statePtr->format & TYPE_CMAC) {
	statePtr->cctx = CMAC_CTX_new();
	res = (statePtr->cctx != NULL);
    }
    if (!res) {
	Tcl_AppendResult(interp, "Create digest context failed: ", REASON(), NULL);
	return TCL_ERROR;
    }

    /* Initialize hash function */
    if (keyObj == NULL) {
    if (statePtr->format & TYPE_MD) {
	res = EVP_DigestInit_ex(statePtr->ctx, md, NULL);
    } else {
    } else if (statePtr->format & TYPE_HMAC) {
	key = Tcl_GetByteArrayFromObj(keyObj, &key_len);
	res = HMAC_Init_ex(statePtr->hctx, (const void *) key, key_len, md, NULL);
    } else if (statePtr->format & TYPE_CMAC) {
	key = Tcl_GetByteArrayFromObj(keyObj, &key_len);
	res = CMAC_Init(statePtr->cctx, (const void *) key, key_len, cipher, NULL);
    }
    if (!res) {
	Tcl_AppendResult(interp, "Initialize digest failed: ", REASON(), NULL);
	return TCL_ERROR;
    }
    return TCL_OK;
}
166
167
168
169
170
171
172
173

174
175

176
177

178


179
180
181
182
183
184
185
183
184
185
186
187
188
189

190
191

192
193

194
195
196
197
198
199
200
201
202
203
204







-
+

-
+

-
+

+
+







 *
 * Side effects:
 *	Adds buf to hash function
 *
 *-------------------------------------------------------------------
 */
int Tls_DigestUpdate(DigestState *statePtr, char *buf, size_t read) {
    int res;
    int res = 0;

    if (statePtr->ctx != NULL) {
    if (statePtr->format & TYPE_MD) {
	res = EVP_DigestUpdate(statePtr->ctx, buf, read);
    } else {
    } else if (statePtr->format & TYPE_HMAC) {
	res = HMAC_Update(statePtr->hctx, buf, read);
    } else if (statePtr->format & TYPE_CMAC) {
	res = CMAC_Update(statePtr->cctx, buf, read);
    }
    return res;
}

/*
 *-------------------------------------------------------------------
 *
196
197
198
199
200
201
202
203

204
205
206

207
208

209




210
211
212
213
214
215
216
217

218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252


253
254
255
256
257
258
259
215
216
217
218
219
220
221

222
223
224

225
226

227
228
229
230
231
232
233
234
235
236
237
238
239

240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257

258
259
260
261
262
263
264
265
266
267
268
269
270
271
272


273
274
275
276
277
278
279
280
281







-
+


-
+

-
+

+
+
+
+







-
+

















-















-
-
+
+







 *
 *-------------------------------------------------------------------
 */

int Tls_DigestFinialize(Tcl_Interp *interp, DigestState *statePtr) {
    unsigned char md_buf[EVP_MAX_MD_SIZE];
    unsigned int md_len;
    int res;
    int res = 0;

    /* Finalize hash function and calculate message digest */
    if (statePtr->ctx != NULL) {
    if (statePtr->format & TYPE_MD) {
	res = EVP_DigestFinal_ex(statePtr->ctx, md_buf, &md_len);
    } else {
    } else if (statePtr->format & TYPE_HMAC) {
	res = HMAC_Final(statePtr->hctx, md_buf, &md_len);
    } else if (statePtr->format & TYPE_CMAC) {
	size_t len;
	res = CMAC_Final(statePtr->cctx, md_buf, &len);
	md_len = (unsigned int) len;
    }
    if (!res) {
	Tcl_AppendResult(interp, "Finalize digest failed: ", REASON(), NULL);
	return TCL_ERROR;
    }

    /* Return message digest as either a binary or hex string */
    if (statePtr->format == BIN_FORMAT) {
    if (statePtr->format & BIN_FORMAT) {
	Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(md_buf, md_len));

    } else {
	Tcl_Obj *resultObj = Tcl_NewObj();
	unsigned char *ptr = Tcl_SetByteArrayLength(resultObj, md_len*2);

	for (unsigned int i = 0; i < md_len; i++) {
	    *ptr++ = hex[(md_buf[i] >> 4) & 0x0F];
	    *ptr++ = hex[md_buf[i] & 0x0F];
	}
	Tcl_SetObjResult(interp, resultObj);
    }
    return TCL_OK;
}

/*******************************************************************/


/*
 *-------------------------------------------------------------------
 *
 * Tls_DigestFile --
 *
 *	Return message digest for file using user specified hash function.
 *
 * Returns:
 *	TCL_OK or TCL_ERROR
 *
 * Side effects:
 *	Result is message digest or error message
 *
 *-------------------------------------------------------------------
 */
int Tls_DigestFile(Tcl_Interp *interp, Tcl_Obj *filename, const EVP_MD *md, int format,
	Tcl_Obj *keyObj) {
int Tls_DigestFile(Tcl_Interp *interp, Tcl_Obj *filename, const EVP_MD *md,
	const EVP_CIPHER *cipher, int format, Tcl_Obj *keyObj) {
    DigestState *statePtr;
    Tcl_Channel chan = NULL;
    unsigned char buf[BUFFER_SIZE];
    int res = TCL_OK, len;

    /* Open file channel */
    chan = Tcl_FSOpenFileChannel(interp, filename, "rb", 0444);
270
271
272
273
274
275
276
277

278
279
280
281
282
283
284
292
293
294
295
296
297
298

299
300
301
302
303
304
305
306







-
+







    /* Create state data struct */
    if ((statePtr = Tls_DigestNew(interp, format)) == NULL) {
	res = TCL_ERROR;
	goto done;
    }

    /* Initialize hash function */
    if ((res = Tls_DigestInit(interp, statePtr, md, keyObj)) != TCL_OK) {
    if ((res = Tls_DigestInit(interp, statePtr, md, cipher, keyObj)) != TCL_OK) {
	goto done;
    }

    /* Read file data and update hash function */
    while (!Tcl_Eof(chan)) {
	len = Tcl_ReadRaw(chan, (char *) buf, BUFFER_SIZE);
	if (len > 0) {
393
394
395
396
397
398
399
400

401
402
403
404
405
406
407
415
416
417
418
419
420
421

422
423
424
425
426
427
428
429







-
+







 *	Read data from transform and write to buf
 *
 *----------------------------------------------------------------------
 */
int DigestInputProc(ClientData clientData, char *buf, int toRead, int *errorCodePtr) {
    DigestState *statePtr = (DigestState *) clientData;
    Tcl_Channel parent;
    int read, res;
    int read, res = 0;
    *errorCodePtr = 0;

    /* Abort if nothing to process */
    if (toRead <= 0 || statePtr->self == (Tcl_Channel) NULL) {
	return 0;
    }

426
427
428
429
430
431
432
433

434
435

436




437
438
439
440
441
442
443
444

445
446
447
448

449
450
451
452
453
454
455
448
449
450
451
452
453
454

455
456

457
458
459
460
461
462
463
464
465
466
467
468
469

470
471
472
473

474
475
476
477
478
479
480
481







-
+

-
+

+
+
+
+







-
+



-
+








    } else if (!(statePtr->flags & CHAN_EOF)) {
	/* EOF */
	unsigned char md_buf[EVP_MAX_MD_SIZE];
	unsigned int md_len = 0;

	/* Finalize hash function and calculate message digest */
	if (statePtr->ctx != NULL) {
	if (statePtr->format & TYPE_MD) {
	    res = EVP_DigestFinal_ex(statePtr->ctx, md_buf, &md_len);
	} else {
	} else if (statePtr->format & TYPE_HMAC) {
	    res = HMAC_Final(statePtr->hctx, md_buf, &md_len);
	} else if (statePtr->format & TYPE_CMAC) {
	    size_t len;
	    res = CMAC_Final(statePtr->cctx, md_buf, &len);
	    md_len = (unsigned int) len;
	}
	if (!res) {
	    Tcl_SetChannelError(statePtr->self, Tcl_ObjPrintf("Digest finalize failed: %s", REASON()));
	    *errorCodePtr = EINVAL;

	/* Write message digest to output channel as byte array or hex string */
	} else if (md_len > 0) {
	    if (statePtr->format == BIN_FORMAT && toRead >= (int) md_len) {
	    if ((statePtr->format & BIN_FORMAT) && toRead >= (int) md_len) {
		read = md_len;
		memcpy(buf, md_buf, read);

	    } else if(statePtr->format == HEX_FORMAT && toRead >= (int) (md_len*2)) {
	    } else if((statePtr->format & HEX_FORMAT) && toRead >= (int) (md_len*2)) {
		unsigned char hex_buf[EVP_MAX_MD_SIZE*2];
		unsigned char *ptr = hex_buf;

		for (unsigned int i = 0; i < md_len; i++) {
		    *ptr++ = hex[(md_buf[i] >> 4) & 0x0F];
		    *ptr++ = hex[md_buf[i] & 0x0F];
		}
747
748
749
750
751
752
753
754
755


756
757
758
759
760
761

762
763
764
765
766
767
768
773
774
775
776
777
778
779


780
781
782
783
784
785
786

787
788
789
790
791
792
793
794







-
-
+
+





-
+







 *
 * Side effects:
 *	Adds transform to channel and sets result to channel id or error message.
 *
 *----------------------------------------------------------------------
 */
static int
Tls_DigestChannel(Tcl_Interp *interp, const char *channel, const EVP_MD *md, int format,
	Tcl_Obj *keyObj) {
Tls_DigestChannel(Tcl_Interp *interp, const char *channel, const EVP_MD *md,
	const EVP_CIPHER *cipher, int format, Tcl_Obj *keyObj) {
    int mode; /* OR-ed combination of TCL_READABLE and TCL_WRITABLE */
    Tcl_Channel chan;
    DigestState *statePtr;

    /* Validate args */
    if (channel == (const char *) NULL || md == (const EVP_MD *) NULL) {
    if (channel == (const char *) NULL) {
	return TCL_ERROR;
    }

    /* Get channel Id */
    chan = Tcl_GetChannel(interp, channel, &mode);
    if (chan == (Tcl_Channel) NULL) {
	return TCL_ERROR;
776
777
778
779
780
781
782
783

784
785
786
787
788
789
790
802
803
804
805
806
807
808

809
810
811
812
813
814
815
816







-
+







	Tcl_AppendResult(interp, "Initialize digest error: memory allocation failure", (char *) NULL);
	return TCL_ERROR;
    }
    statePtr->self = chan;
    statePtr->mode = mode;

    /* Initialize hash function */
    if (Tls_DigestInit(interp, statePtr, md, keyObj) != TCL_OK) {
    if (Tls_DigestInit(interp, statePtr, md, cipher, keyObj) != TCL_OK) {
	return TCL_ERROR;
    }

    /* Configure channel */
    Tcl_SetChannelOption(interp, chan, "-translation", "binary");
    if (Tcl_GetChannelBufferSize(chan) < EVP_MAX_MD_SIZE * 2) {
	Tcl_SetChannelBufferSize(chan, EVP_MAX_MD_SIZE * 2);
948
949
950
951
952
953
954
955
956


957
958
959
960
961
962
963
964
965
966
967

968
969
970
971
972
973
974
974
975
976
977
978
979
980


981
982
983
984
985
986
987
988
989
990
991
992

993
994
995
996
997
998
999
1000







-
-
+
+










-
+







 *	TCL_OK or TCL_ERROR
 *
 * Side effects:
 *	Creates command or error message
 *
 *-------------------------------------------------------------------
 */
int Tls_DigestInstance(Tcl_Interp *interp, Tcl_Obj *cmdObj, const EVP_MD *md, int format,
	Tcl_Obj *keyObj) {
int Tls_DigestInstance(Tcl_Interp *interp, Tcl_Obj *cmdObj, const EVP_MD *md,
	const EVP_CIPHER *cipher, int format, Tcl_Obj *keyObj) {
    DigestState *statePtr;
    char *cmdName = Tcl_GetStringFromObj(cmdObj, NULL);

    /* Create state data struct */
    if ((statePtr = Tls_DigestNew(interp, format)) == NULL) {
	Tcl_AppendResult(interp, "Initialize digest error: memory allocation failure", (char *) NULL);
	return TCL_ERROR;
    }

    /* Initialize hash function */
    if (Tls_DigestInit(interp, statePtr, md, keyObj) != TCL_OK) {
    if (Tls_DigestInit(interp, statePtr, md, cipher, keyObj) != TCL_OK) {
	return TCL_ERROR;
    }

    /* Create instance command */
    statePtr->token = Tcl_CreateObjCommand(interp, cmdName, InstanceObjCmd,
	(ClientData) statePtr, InstanceDelCallback);

993
994
995
996
997
998
999
1000

1001
1002
1003
1004
1005
1006
1007
1019
1020
1021
1022
1023
1024
1025

1026
1027
1028
1029
1030
1031
1032
1033







-
+







 * Side effects:
 *	Sets result to message digest or error message
 *
 *-------------------------------------------------------------------
 */
int
Tls_DigestData(Tcl_Interp *interp, int objc, Tcl_Obj *const objv[],
	const EVP_MD *md, int format, Tcl_Obj *keyObj) {
	const EVP_MD *md, const EVP_CIPHER *cipher, int format, Tcl_Obj *keyObj) {
    char *data;
    int len, res;
    unsigned int md_len;
    unsigned char md_buf[EVP_MAX_MD_SIZE];

    /* Validate arg count */
    if (objc != 2) {
1027
1028
1029
1030
1031
1032
1033
1034

1035
1036
1037
1038
1039
1040
1041
1053
1054
1055
1056
1057
1058
1059

1060
1061
1062
1063
1064
1065
1066
1067







-
+







	hmac = HMAC(md, (const void *) key, key_len, (const unsigned char *) data,
	    (size_t) len, md_buf, &md_len);
	res = (hmac != NULL);
    }

    /* Output digest to result per format (bin or hex) */
    if (res) {
	if (format == BIN_FORMAT) {
	if (format & BIN_FORMAT) {
	    Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(md_buf, md_len));

	} else {
	    Tcl_Obj *resultObj = Tcl_NewObj();
	    unsigned char *ptr = Tcl_SetByteArrayLength(resultObj, md_len*2);

	    for (unsigned int i = 0; i < md_len; i++) {
1068
1069
1070
1071
1072
1073
1074
1075

1076
1077

1078

1079
1080
1081
1082
1083
1084
1085


1086
1087
1088
1089
1090
1091
1092

1093
1094
1095
1096
1097
1098


1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114

1115
1116
1117
1118
1119
1120
1121

1122
1123
1124
1125
1126
1127
1128


















1129
1130
1131
1132

1133
1134

1135
1136

1137
1138
1139
1140
1141

1142
1143
1144








1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162

1163
1164
1165
1166

1167
1168
1169
1170

1171
1172
1173
1174

1175
1176
1177
1178

1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197


1198
1199
1200
1201
1202
1203
1204
1205
1206
1094
1095
1096
1097
1098
1099
1100

1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111


1112
1113
1114
1115
1116
1117
1118
1119

1120
1121
1122
1123
1124


1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149

1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178

1179
1180

1181
1182

1183
1184
1185
1186
1187

1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216

1217
1218
1219
1220

1221
1222
1223
1224

1225
1226
1227
1228

1229
1230
1231
1232

1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263







-
+


+

+





-
-
+
+






-
+




-
-
+
+
















+






-
+







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
+

-
+

-
+




-
+



+
+
+
+
+
+
+
+

















-
+



-
+



-
+



-
+



-
+



















+
+









 * Side effects:
 *	Sets result to message digest or error message
 *
 *-------------------------------------------------------------------
 */
static int
DigestObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
    int idx, len, format = HEX_FORMAT, res = TCL_OK;
    int idx, len, format = HEX_FORMAT, res = TCL_OK, flags = 0;
    const char *digestname, *channel = NULL;
    Tcl_Obj *cmdObj = NULL, *dataObj = NULL, *fileObj = NULL, *keyObj = NULL;
    unsigned char *cipherName = NULL;
    const EVP_MD *md;
    const EVP_CIPHER *cipher;

    /* Clear interp result */
    Tcl_ResetResult(interp);

    /* Validate arg count */
    if (objc < 3 || objc > 7) {
	Tcl_WrongNumArgs(interp, 1, objv, "type ?-bin|-hex? ?-key hmac_key? [-channel chan | -command cmdName | -file filename | ?-data? data]");
    if (objc < 3 || objc > 9) {
	Tcl_WrongNumArgs(interp, 1, objv, "digest ?-bin|-hex? ?-cipher name? ?-key hmac_key? [-channel chan | -command cmdName | -file filename | ?-data? data]");
	return TCL_ERROR;
    }

    /* Get digest */
    digestname = Tcl_GetStringFromObj(objv[1], &len);
    if (digestname == NULL || (md = EVP_get_digestbyname(digestname)) == NULL) {
	Tcl_AppendResult(interp, "Invalid digest type \"", digestname, "\"", NULL);
	Tcl_AppendResult(interp, "Invalid digest \"", digestname, "\"", NULL);
	return TCL_ERROR;
    }

    /* Optimal case for blob of data */
    if (objc == 3) {
	return Tls_DigestData(interp, --objc, ++objv, md, format, NULL);
    if (objc == 3 && type == TYPE_MD) {
	return Tls_DigestData(interp, --objc, ++objv, md, NULL, HEX_FORMAT | TYPE_MD, NULL);
    }

    /* Get options */
    for (idx = 2; idx < objc-1; idx++) {
	char *opt = Tcl_GetStringFromObj(objv[idx], NULL);

	if (opt[0] != '-') {
	    break;
	}

	OPTFLAG("-bin", format, BIN_FORMAT);
	OPTFLAG("-binary", format, BIN_FORMAT);
	OPTFLAG("-hex", format, HEX_FORMAT);
	OPTFLAG("-hexadecimal", format, HEX_FORMAT);
	OPTSTR("-chan", channel);
	OPTSTR("-channel", channel);
	OPTSTR("-cipher", cipherName);
	OPTOBJ("-command", cmdObj);
	OPTOBJ("-data", dataObj);
	OPTOBJ("-file", fileObj);
	OPTOBJ("-filename", fileObj);
	OPTOBJ("-key", keyObj);

	OPTBAD("option", "-bin, -channel, -command, -data, -file, -filename, -hex, or -key");
	OPTBAD("option", "-bin, -channel, -cipher, -command, -data, -file, -filename, -hex, or -key");
	return TCL_ERROR;
    }

    /* If no option for last arg, then its the data */
    if (idx < objc) {
	dataObj = objv[idx];
    }

    /* Get cipher */
    if (cipherName != NULL) {
	cipher = EVP_get_cipherbyname(cipherName);
	if (cipher == NULL) {
	    Tcl_AppendResult(interp, "Invalid cipher \"", cipherName, "\"", NULL);
	    return TCL_ERROR;
	}
    }

    /* Define operation to perform */
    if (cipher != NULL) {
	format |= TYPE_CMAC;
    } else if (keyObj != NULL) {
	format |= TYPE_HMAC;
    } else {
	format |= TYPE_MD;
    }

    /* Calc digest on file, stacked channel, using instance command, or data blob */
    if (fileObj != NULL) {
	res = Tls_DigestFile(interp, fileObj, md, format, keyObj);
	res = Tls_DigestFile(interp, fileObj, md, cipher, format, keyObj);
    } else if (channel != NULL) {
	res = Tls_DigestChannel(interp, channel, md, format, keyObj);
	res = Tls_DigestChannel(interp, channel, md, cipher, format, keyObj);
    } else if (cmdObj != NULL) {
	res = Tls_DigestInstance(interp, cmdObj, md, format, keyObj);
	res = Tls_DigestInstance(interp, cmdObj, md, cipher, format, keyObj);
    } else if (dataObj != NULL) {
	Tcl_Obj *objs[2];
	objs[0] = NULL;
	objs[1] = dataObj;
	res = Tls_DigestData(interp, 2, objs, md, format, keyObj);
	res = Tls_DigestData(interp, 2, objs, md, cipher, format, keyObj);
    }
    return res;
}

int CMACObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
    return DigestObjCmd(clientData, interp, objc, objv);
}

int HMACObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
    return DigestObjCmd(clientData, interp, objc, objv);
}

/*
 *-------------------------------------------------------------------
 *
 * Message Digest Convenience Commands --
 *
 *	Convenience commands for message digests.
 *
 * Returns:
 *	TCL_OK or TCL_ERROR
 *
 * Side effects:
 *	Sets result to message digest or error message
 *
 *-------------------------------------------------------------------
 */
int DigestMD4Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
    return Tls_DigestData(interp, objc, objv, EVP_md4(), HEX_FORMAT, NULL);
    return Tls_DigestData(interp, objc, objv, EVP_md4(), NULL, HEX_FORMAT, NULL);
}

int DigestMD5Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
    return Tls_DigestData(interp, objc, objv, EVP_md5(), HEX_FORMAT, NULL);
    return Tls_DigestData(interp, objc, objv, EVP_md5(), NULL, HEX_FORMAT, NULL);
}

int DigestSHA1Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
    return Tls_DigestData(interp, objc, objv, EVP_sha1(), HEX_FORMAT, NULL);
    return Tls_DigestData(interp, objc, objv, EVP_sha1(), NULL, HEX_FORMAT, NULL);
}

int DigestSHA256Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
    return Tls_DigestData(interp, objc, objv, EVP_sha256(), HEX_FORMAT, NULL);
    return Tls_DigestData(interp, objc, objv, EVP_sha256(), NULL, HEX_FORMAT, NULL);
}

int DigestSHA512Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
    return Tls_DigestData(interp, objc, objv, EVP_sha512(), HEX_FORMAT, NULL);
    return Tls_DigestData(interp, objc, objv, EVP_sha512(), NULL, HEX_FORMAT, NULL);
}

/*
 *-------------------------------------------------------------------
 *
 * Tls_DigestCommands --
 *
 *	Create digest commands
 *
 * Returns:
 *	TCL_OK or TCL_ERROR
 *
 * Side effects:
 *	Creates commands
 *
 *-------------------------------------------------------------------
 */
int Tls_DigestCommands(Tcl_Interp *interp) {
    Tcl_CreateObjCommand(interp, "tls::digest", DigestObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
    Tcl_CreateObjCommand(interp, "tls::cmac", CMACObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
    Tcl_CreateObjCommand(interp, "tls::hmac", HMACObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
    Tcl_CreateObjCommand(interp, "tls::md4", DigestMD4Cmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
    Tcl_CreateObjCommand(interp, "tls::md5", DigestMD5Cmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
    Tcl_CreateObjCommand(interp, "tls::sha1", DigestSHA1Cmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
    Tcl_CreateObjCommand(interp, "tls::sha256", DigestSHA256Cmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
    Tcl_CreateObjCommand(interp, "tls::sha512", DigestSHA512Cmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
    Tcl_CreateObjCommand(interp, "tls::unstack", UnstackObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
    return TCL_OK;
}

68
69
70
71
72
73
74

75
76
77
78
79
80
81
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82







+







 *-------------------------------------------------------------------
 */
static int CiphersObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
    SSL_CTX *ctx = NULL;
    SSL *ssl = NULL;
    STACK_OF(SSL_CIPHER) *sk = NULL;
    int index, verbose = 0, use_supported = 0;
    int min_version, max_version;

    dprintf("Called");

#if OPENSSL_VERSION_NUMBER < 0x10100000L
    OpenSSL_add_all_ciphers(); /* Make sure they're loaded */
#endif