Diff
EuroTcl/OpenACS 11 - 12 JULY 2024, VIENNA

Differences From Artifact [5957db2945]:

To Artifact [075035113a]:


1
2
3
4

5
6
7
8
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
/*
 * Message Digests Module
 *
 * Provides commands to calculate a message digest using a specified hash function.

 *
 * Copyright (C) 2023 Brian O'Hagan
 *
 */

#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 CHAN_EOF 0x10


/* Digest format and operation */
#define BIN_FORMAT 0x01
#define HEX_FORMAT 0x02
#define TYPE_MD    0x10
#define TYPE_HMAC  0x20
#define TYPE_CMAC  0x40

/*
 * 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 */

|

|
>


















|
|
>


|
|
|
|
|







1
2
3
4
5
6
7
8
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
/*
 * Message Digests (MD) and Message Authentication Code (MAC) Module
 *
 * Provides commands to calculate a message digest (MD) or message
 * authentication code (MAC) using a specified hash function and/or cipher.
 *
 * Copyright (C) 2023 Brian O'Hagan
 *
 */

#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 CHAN_EOF	0x10
#define READ_DELAY	5

/* Digest format and operation */
#define BIN_FORMAT	0x01
#define HEX_FORMAT	0x02
#define TYPE_MD		0x10
#define TYPE_HMAC	0x20
#define TYPE_CMAC	0x40

/*
 * 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 */
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
 *	1 if successful or 0 for failure
 *
 * Side effects:
 *	Adds buf to hash function
 *
 *-------------------------------------------------------------------
 */
int Tls_DigestUpdate(DigestState *statePtr, char *buf, size_t read, int show) {
    int res = 0;

    if (statePtr->format & TYPE_MD) {
	res = EVP_DigestUpdate(statePtr->ctx, buf, read);
    } 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);
    }
    if (!res && show) {
	Tcl_AppendResult(statePtr->interp, "Update failed: ", REASON(), NULL);
	return TCL_ERROR;
    }
    return res;
}

/*







|









|







186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
 *	1 if successful or 0 for failure
 *
 * Side effects:
 *	Adds buf to hash function
 *
 *-------------------------------------------------------------------
 */
int Tls_DigestUpdate(DigestState *statePtr, char *buf, size_t read, int do_result) {
    int res = 0;

    if (statePtr->format & TYPE_MD) {
	res = EVP_DigestUpdate(statePtr->ctx, buf, read);
    } 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);
    }
    if (!res && do_result) {
	Tcl_AppendResult(statePtr->interp, "Update failed: ", REASON(), NULL);
	return TCL_ERROR;
    }
    return res;
}

/*
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
 *	Nothing (can't return error messages)
 *
 * Side effects:
 *	Configure notifier so future events on the channel will be seen by Tcl.
 *
 *----------------------------------------------------------------------
 */
#define READ_DELAY	5
void DigestWatchProc(ClientData clientData, int mask) {
    DigestState *statePtr = (DigestState *) clientData;
    Tcl_Channel parent;
    Tcl_DriverWatchProc *watchProc;

    /* Abort if no channel */
    if (statePtr->self == (Tcl_Channel) NULL) {







<







578
579
580
581
582
583
584

585
586
587
588
589
590
591
 *	Nothing (can't return error messages)
 *
 * Side effects:
 *	Configure notifier so future events on the channel will be seen by Tcl.
 *
 *----------------------------------------------------------------------
 */

void DigestWatchProc(ClientData clientData, int mask) {
    DigestState *statePtr = (DigestState *) clientData;
    Tcl_Channel parent;
    Tcl_DriverWatchProc *watchProc;

    /* Abort if no channel */
    if (statePtr->self == (Tcl_Channel) NULL) {
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
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
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
 *
 * 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, const EVP_CIPHER *cipher, int format, Tcl_Obj *keyObj) {
    char *data;
    int data_len, res;
    unsigned int md_len;
    unsigned char md_buf[EVP_MAX_MD_SIZE];

    /* Validate arg count */
    if (objc != 2) {
	Tcl_WrongNumArgs(interp, 1, objv, "data");
	return TCL_ERROR;
    }

    /* Get data */
    data = Tcl_GetByteArrayFromObj(objv[1], &data_len);
    if (data == NULL || data_len == 0) {
	Tcl_SetResult(interp, "No data", NULL);
	return TCL_ERROR;
    }

    /* Calculate digest based on hash function */
    if (format & TYPE_MD) {
	res = EVP_Digest(data, (size_t) data_len, md_buf, &md_len, md, NULL);

    } else if (format & TYPE_HMAC) {
	unsigned char *key, *hmac = NULL;
	int key_len;

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

    } else if (format & TYPE_CMAC) {
	DigestState *statePtr;

	if ((statePtr = Tls_DigestNew(interp, format)) == NULL) {
	    Tcl_AppendResult(interp, "Memory allocation error", (char *) NULL);
	    return TCL_ERROR;
	}


	if (Tls_DigestInit(interp, statePtr, md, cipher, keyObj) != TCL_OK ||
	    Tls_DigestUpdate(statePtr, data, (size_t) len, 1) == 0 ||
	    Tls_DigestFinialize(interp, statePtr) != TCL_OK) {
	    Tls_DigestFree(statePtr);
	    return TCL_ERROR;
	}
	Tls_DigestFree(statePtr);
	return TCL_OK;
    }

    /* Output digest to result per format (bin or hex) */
    if (res) {
	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++) {
		*ptr++ = hex[(md_buf[i] >> 4) & 0x0F];
		*ptr++ = hex[md_buf[i] & 0x0F];
	    }
	    Tcl_SetObjResult(interp, resultObj);
	}

    } else {
	Tcl_AppendResult(interp, "Hash calculation error:", REASON(), (char *) NULL);
	return TCL_ERROR;
    }
    return TCL_OK;
}

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

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







|
|

|
<
<
|
<
<
<
<
|
<

|





<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
|
|
|
|
>
>
|
|
|
|
|
|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







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
975
976
977
978
979
980
981
982























983
984
985
986
987
988
989
 *
 * Side effects:
 *	Sets result to message digest or error message
 *
 *-------------------------------------------------------------------
 */
int
Tls_DigestData(Tcl_Interp *interp, Tcl_Obj *dataObj, const EVP_MD *md,
	const EVP_CIPHER *cipher, int format, Tcl_Obj *keyObj) {
    char *data;
    int data_len;


    DigestState *statePtr;






    /* Get data */
    data = Tcl_GetByteArrayFromObj(dataObj, &data_len);
    if (data == NULL || data_len == 0) {
	Tcl_SetResult(interp, "No data", NULL);
	return TCL_ERROR;
    }




    /* Create state struct */












    if ((statePtr = Tls_DigestNew(interp, format)) == NULL) {
	Tcl_AppendResult(interp, "Memory allocation error", (char *) NULL);
	return TCL_ERROR;
    }

    /* Calc Digest */
    if (Tls_DigestInit(interp, statePtr, md, cipher, keyObj) != TCL_OK ||
	Tls_DigestUpdate(statePtr, data, (size_t) len, 1) == 0 ||
	Tls_DigestFinialize(interp, statePtr) != TCL_OK) {
	Tls_DigestFree(statePtr);
	return TCL_ERROR;
    }
    Tls_DigestFree(statePtr);























    return TCL_OK;
}

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

/*
 *-------------------------------------------------------------------
1044
1045
1046
1047
1048
1049
1050






1051
1052
1053
1054

1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
 */
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);
    if (chan == (Tcl_Channel) NULL) {

	return TCL_ERROR;
    }

    /* Configure channel */
    if ((res = Tcl_SetChannelOption(interp, chan, "-translation", "binary")) == TCL_ERROR) {
	goto done;
    }
    Tcl_SetChannelBufferSize(chan, BUFFER_SIZE);

    /* Create state data struct */
    if ((statePtr = Tls_DigestNew(interp, format)) == NULL) {
	Tcl_AppendResult(interp, "Memory allocation error", (char *) NULL);
	res = TCL_ERROR;
	goto done;
    }

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

    /* Read file data and update hash function */
    while (!Tcl_Eof(chan)) {







>
>
>
>
>
>




>









<
<
<
<
<
<
<







1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028







1029
1030
1031
1032
1033
1034
1035
 */
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;

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

    /* Open file channel */
    chan = Tcl_FSOpenFileChannel(interp, filename, "rb", 0444);
    if (chan == (Tcl_Channel) NULL) {
	Tls_DigestFree(statePtr);
	return TCL_ERROR;
    }

    /* Configure channel */
    if ((res = Tcl_SetChannelOption(interp, chan, "-translation", "binary")) == TCL_ERROR) {
	goto done;
    }
    Tcl_SetChannelBufferSize(chan, BUFFER_SIZE);








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

    /* Read file data and update hash function */
    while (!Tcl_Eof(chan)) {
1099
1100
1101
1102
1103
1104
1105
1106
1107

1108
1109
1110
1111
1112
1113
1114
1115
}

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

/*
 *-------------------------------------------------------------------
 *
 * DigestObjCmd --
 *

 *	Return message digest using user specified hash function.
 *
 * Returns:
 *	TCL_OK or TCL_ERROR
 *
 * Side effects:
 *	Sets result to message digest or error message
 *







|

>
|







1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
}

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

/*
 *-------------------------------------------------------------------
 *
 * DigestMain --
 *
 *	Return message digest or Message Authentication Code (MAC) of
 *	data using user specified hash function.
 *
 * Returns:
 *	TCL_OK or TCL_ERROR
 *
 * Side effects:
 *	Sets result to message digest or error message
 *
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
	return TCL_ERROR;
    }

    /* Optimal case for a digest and blob of data */
    if (objc == 3 && type == TYPE_MD) {
	digestName = Tcl_GetStringFromObj(objv[1],NULL);
	if ((md = EVP_get_digestbyname(digestName)) != NULL) {
	    return Tls_DigestData(interp, --objc, ++objv, md, NULL, HEX_FORMAT | TYPE_MD, NULL);
	} else {
	    Tcl_AppendResult(interp, "Invalid digest \"", digestName, "\"", NULL);
	    return TCL_ERROR;
	}
    }

    /* Get options */







|







1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
	return TCL_ERROR;
    }

    /* Optimal case for a digest and blob of data */
    if (objc == 3 && type == TYPE_MD) {
	digestName = Tcl_GetStringFromObj(objv[1],NULL);
	if ((md = EVP_get_digestbyname(digestName)) != NULL) {
	    return Tls_DigestData(interp, objv[2], md, NULL, HEX_FORMAT | TYPE_MD, NULL);
	} else {
	    Tcl_AppendResult(interp, "Invalid digest \"", digestName, "\"", NULL);
	    return TCL_ERROR;
	}
    }

    /* Get options */
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
    if (fileObj != NULL) {
	res = Tls_DigestFile(interp, fileObj, md, cipher, format | type, keyObj);
    } else if (channel != NULL) {
	res = Tls_DigestChannel(interp, channel, md, cipher, format | type, keyObj);
    } else if (cmdObj != NULL) {
	res = Tls_DigestInstance(interp, cmdObj, md, cipher, format | type, keyObj);
    } else if (dataObj != NULL) {
	Tcl_Obj *objs[2];
	objs[0] = NULL;
	objs[1] = dataObj;
	res = Tls_DigestData(interp, 2, objs, md, cipher, format | type, keyObj);
    }
    return res;
}

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







<
<
<
|







1173
1174
1175
1176
1177
1178
1179



1180
1181
1182
1183
1184
1185
1186
1187
    if (fileObj != NULL) {
	res = Tls_DigestFile(interp, fileObj, md, cipher, format | type, keyObj);
    } else if (channel != NULL) {
	res = Tls_DigestChannel(interp, channel, md, cipher, format | type, keyObj);
    } else if (cmdObj != NULL) {
	res = Tls_DigestInstance(interp, cmdObj, md, cipher, format | type, keyObj);
    } else if (dataObj != NULL) {



	res = Tls_DigestData(interp, dataObj, md, cipher, format | type, keyObj);
    }
    return res;
}

/*
 *-------------------------------------------------------------------
 *
1265
1266
1267
1268
1269
1270
1271




1272
1273
1274
1275




1276
1277
1278
1279




1280
1281
1282
1283




1284
1285
1286
1287




1288
1289
1290
1291
1292
1293
1294
1295
 *
 * 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(), NULL, HEX_FORMAT | TYPE_MD, NULL);
}

int DigestMD5Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {




    return Tls_DigestData(interp, objc, objv, EVP_md5(), NULL, HEX_FORMAT | TYPE_MD, NULL);
}

int DigestSHA1Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {




    return Tls_DigestData(interp, objc, objv, EVP_sha1(), NULL, HEX_FORMAT | TYPE_MD, NULL);
}

int DigestSHA256Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {




    return Tls_DigestData(interp, objc, objv, EVP_sha256(), NULL, HEX_FORMAT | TYPE_MD, NULL);
}

int DigestSHA512Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {




    return Tls_DigestData(interp, objc, objv, EVP_sha512(), NULL, HEX_FORMAT | TYPE_MD, NULL);
}

/*
 *-------------------------------------------------------------------
 *
 * Tls_DigestCommands --
 *







>
>
>
>
|



>
>
>
>
|



>
>
>
>
|



>
>
>
>
|



>
>
>
>
|







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
1264
1265
1266
1267
1268
1269
1270
1271
 *
 * Side effects:
 *	Sets result to message digest or error message
 *
 *-------------------------------------------------------------------
 */
int DigestMD4Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
    if (objc != 2) {
	Tcl_WrongNumArgs(interp, 1, objv, "data");
	return TCL_ERROR;
    }
    return Tls_DigestData(interp, objv[1], EVP_md4(), NULL, HEX_FORMAT | TYPE_MD, NULL);
}

int DigestMD5Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
    if (objc != 2) {
	Tcl_WrongNumArgs(interp, 1, objv, "data");
	return TCL_ERROR;
    }
    return Tls_DigestData(interp, objv[1], EVP_md5(), NULL, HEX_FORMAT | TYPE_MD, NULL);
}

int DigestSHA1Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
    if (objc != 2) {
	Tcl_WrongNumArgs(interp, 1, objv, "data");
	return TCL_ERROR;
    }
    return Tls_DigestData(interp, objv[1], EVP_sha1(), NULL, HEX_FORMAT | TYPE_MD, NULL);
}

int DigestSHA256Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
    if (objc != 2) {
	Tcl_WrongNumArgs(interp, 1, objv, "data");
	return TCL_ERROR;
    }
    return Tls_DigestData(interp, objv[1], EVP_sha256(), NULL, HEX_FORMAT | TYPE_MD, NULL);
}

int DigestSHA512Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
    if (objc != 2) {
	Tcl_WrongNumArgs(interp, 1, objv, "data");
	return TCL_ERROR;
    }
    return Tls_DigestData(interp, objv[1], EVP_sha512(), NULL, HEX_FORMAT | TYPE_MD, NULL);
}

/*
 *-------------------------------------------------------------------
 *
 * Tls_DigestCommands --
 *