Index: configure
==================================================================
--- configure
+++ configure
@@ -5394,11 +5394,11 @@
# This defines PKG(_STUB)_SOURCES, PKG(_STUB)_OBJECTS, PKG_HEADERS
# and PKG_TCL_SOURCES.
#-----------------------------------------------------------------------
- vars="tls.c tlsBIO.c tlsIO.c tlsX509.c"
+ vars="tls.c tlsBIO.c tlsDigest.c tlsIO.c tlsX509.c"
for i in $vars; do
case $i in
\$*)
# allow $-var names
PKG_SOURCES="$PKG_SOURCES $i"
Index: configure.ac
==================================================================
--- configure.ac
+++ configure.ac
@@ -69,11 +69,11 @@
# and runtime Tcl library files in TEA_ADD_TCL_SOURCES.
# This defines PKG(_STUB)_SOURCES, PKG(_STUB)_OBJECTS, PKG_HEADERS
# and PKG_TCL_SOURCES.
#-----------------------------------------------------------------------
-TEA_ADD_SOURCES([tls.c tlsBIO.c tlsIO.c tlsX509.c])
+TEA_ADD_SOURCES([tls.c tlsBIO.c tlsDigest.c tlsIO.c tlsX509.c])
TEA_ADD_HEADERS([generic/tls.h])
TEA_ADD_INCLUDES([])
TEA_ADD_LIBS([])
TEA_ADD_CFLAGS([])
TEA_ADD_STUB_SOURCES([])
Index: doc/tls.html
==================================================================
--- doc/tls.html
+++ doc/tls.html
@@ -29,13 +29,20 @@
tls::status ?-local? channel
tls::connection channel
tls::import channel ?options?
tls::unimport channel
- tls::ciphers protocol ?verbose? ?supported?
+ tls::ciphers ?protocol? ?verbose? ?supported?
tls::protocols
tls::version
+
+ tls::digest type ?-bin|-hex? ?-key hmac_key? [-file filename | -chan channel | ?-data? data]
+ tls::digests
+ tls::md4 data
+ tls::md5 data
+ tls::sha1 data
+ tls::sha256 data
COMMANDS
CALLBACK OPTIONS
HTTPS EXAMPLE
@@ -50,11 +57,11 @@
tls - binding to OpenSSL
toolkit.
-package require Tcl 8.4
+
package require Tcl 8.5
package require tls
tls::init ?options?
tls::socket ?options? host port
tls::socket ?-server command? ?options? port
@@ -62,13 +69,20 @@
tls::connection channel
tls::handshake channel
tls::import channel ?options?
tls::unimport channel
-tls::ciphers protocol ?verbose? ?supported?
-tls::protocols
-tls::version
+tls::ciphers ?protocol? ?verbose? ?supported?
+tls::protocols
+tls::version
+
+tls::digest type ?-bin|-hex? ?-key hmac_key? [-file filename | -chan channel | ?-data? data]
+tls::digests
+tls::md4 data
+tls::md5 data
+tls::sha1 data
+tls::sha256 data
This extension provides a generic binding to Server cache mode (client, server, or both).
tls::ciphers
- protocol ?verbose? ?supported?
- Returns a list of supported ciphers available for protocol,
- where protocol must be one of ssl2, ssl3, tls1, tls1.1,
- tls1.2, or tls1.3. If verbose is specified as
- true then a verbose, human readable list is returned with
- additional information on the cipher. If supported
+ ?protocol? ?verbose? ?supported?
+
Without any args, returns a list of all ciphers. With
+ protocol, only the ciphers supported for that protocol
+ are returned where protocol must be one of ssl2, ssl3,
+ tls1, tls1.1, tls1.2, or tls1.3. If verbose is
+ specified as true then a verbose, human readable list is returned
+ with additional information on the cipher. If supported
is specified as true, then only the ciphers supported for protocol
will be listed.
tls::protocols
Returns a list of supported protocols. Valid values are:
@@ -430,10 +445,39 @@
and tls1.3. Exact list depends on OpenSSL version and
compile time flags.
tls::version
Returns the OpenSSL version string.
+
+
+ tls::digest type ?-bin|-hex?
+ ?-key hmac_key? [-file filename | -chan channel | ?-data? data]
+ Calculate the message digest for data or file filename
+ using type hash algorithm. Returns value as a hex string
+ (default) or as a binary value with -bin option. Using
+ -chan option, a stacked channel is created and data read
+ from the channel is used to calculate a message digest with the result
+ returned with the last read operation before EOF. Use -key to
+ specify the key and return a Hashed Message Authentication Code (HMAC).
+ Type can be any OpenSSL supported hash algorithm including: md4,
+ md5, sha1, sha256, sha512, sha3-256,
+ etc. See tls::digests command for a full list.
+
+ tls::digests
+ Returns a list of the hash algorithms for tls::digest command.
+
+ tls::md4 data
+ Returns the MD4 message-digest for data as a hex string.
+
+ tls::md5 data
+ Returns the MD5 message-digest for data as a hex string.
+
+ tls::sha1 data
+ Returns the SHA1 secure hash algorithm digest for data as a hex string.
+
+ tls::sha256 data
+ Returns the SHA-2 SHA256 secure hash algorithm digest for data as a hex string.
Index: generic/tclOpts.h
==================================================================
--- generic/tclOpts.h
+++ generic/tclOpts.h
@@ -5,10 +5,16 @@
* external vars: opt, idx, objc, objv
*/
#ifndef _TCL_OPTS_H
#define _TCL_OPTS_H
+
+#define OPTFLAG(option, var, val) \
+ if (strcmp(opt, (option)) == 0) { \
+ var = val; \
+ continue; \
+ }
#define OPT_PROLOG(option) \
if (strcmp(opt, (option)) == 0) { \
if (++idx >= objc) { \
Tcl_AppendResult(interp, \
@@ -15,13 +21,15 @@
"no argument given for ", \
(option), " option", \
(char *) NULL); \
return TCL_ERROR; \
}
+
#define OPT_POSTLOG() \
continue; \
}
+
#define OPTOBJ(option, var) \
OPT_PROLOG(option) \
var = objv[idx]; \
OPT_POSTLOG()
@@ -44,11 +52,11 @@
&(var)) != TCL_OK) { \
return TCL_ERROR; \
} \
OPT_POSTLOG()
-#define OPTBYTE(option, var, lvar) \
+#define OPTBYTE(option, var, lvar) \
OPT_PROLOG(option) \
var = Tcl_GetByteArrayFromObj(objv[idx], &(lvar));\
OPT_POSTLOG()
#define OPTBAD(type, list) \
Index: generic/tls.c
==================================================================
--- generic/tls.c
+++ generic/tls.c
@@ -24,10 +24,14 @@
#include "tlsInt.h"
#include "tclOpts.h"
#include
#include
+#include
+#include
+#include
+#include
#include
#include
/* Min OpenSSL version */
#if OPENSSL_VERSION_NUMBER < 0x10101000L
@@ -444,11 +448,11 @@
Tcl_ListObjAppendElement(interp, cmdPtr,
Tcl_NewStringObj(Tcl_GetChannelName(statePtr->self), -1));
if (msg != NULL) {
Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewStringObj(msg, -1));
- } else if ((msg = Tcl_GetStringFromObj(Tcl_GetObjResult(interp), (Tcl_Size *)NULL)) != NULL) {
+ } else if ((msg = Tcl_GetStringFromObj(Tcl_GetObjResult(interp), NULL)) != NULL) {
Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewStringObj(msg, -1));
} else {
listPtr = Tcl_NewListObj(0, NULL);
while ((err = ERR_get_error()) != 0) {
@@ -551,19 +555,19 @@
Tcl_Release((ClientData) statePtr);
/* If successful, pass back password string and truncate if too long */
if (code == TCL_OK) {
- Tcl_Size len;
+ int len;
char *ret = (char *) Tcl_GetStringFromObj(Tcl_GetObjResult(interp), &len);
- if (len > (Tcl_Size) size-1) {
- len = (Tcl_Size) size-1;
+ if (len > size-1) {
+ len = size-1;
}
strncpy(buf, ret, (size_t) len);
buf[len] = '\0';
Tcl_Release((ClientData) interp);
- return((int) len);
+ return(len);
}
Tcl_Release((ClientData) interp);
return -1;
}
@@ -613,15 +617,15 @@
Tcl_ListObjAppendElement(interp, cmdPtr,
Tcl_NewStringObj(Tcl_GetChannelName(statePtr->self), -1));
/* Session id */
session_id = SSL_SESSION_get_id(session, &ulen);
- Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewByteArrayObj(session_id, (Tcl_Size) ulen));
+ Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewByteArrayObj(session_id, (int) ulen));
/* Session ticket */
SSL_SESSION_get0_ticket(session, &ticket, &len2);
- Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewByteArrayObj(ticket, (Tcl_Size) len2));
+ Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewByteArrayObj(ticket, (int) len2));
/* Lifetime - number of seconds */
Tcl_ListObjAppendElement(interp, cmdPtr,
Tcl_NewLongObj((long) SSL_SESSION_get_ticket_lifetime_hint(session)));
@@ -902,11 +906,11 @@
/* Create command to eval */
cmdPtr = Tcl_DuplicateObj(statePtr->vcmd);
Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewStringObj("hello", -1));
Tcl_ListObjAppendElement(interp, cmdPtr,
Tcl_NewStringObj(Tcl_GetChannelName(statePtr->self), -1));
- Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewStringObj(servername, (Tcl_Size) len));
+ Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewStringObj(servername, (int) len));
/* Eval callback command */
Tcl_IncrRefCount(cmdPtr);
if ((code = EvalCallback(interp, statePtr, cmdPtr)) > 1) {
res = SSL_CLIENT_HELLO_RETRY;
@@ -926,10 +930,63 @@
/********************/
/*
*-------------------------------------------------------------------
*
+ * Hash List Command --
+ *
+ * Return a list of all valid hash algorithms or message digests.
+ *
+ * Results:
+ * A standard Tcl result list.
+ *
+ * Side effects:
+ * None.
+ *
+ *-------------------------------------------------------------------
+ */
+void ListCallback(const OBJ_NAME *obj, void *arg) {
+ Tcl_Obj *objPtr = (Tcl_Obj *) arg;
+ if (1 || !obj->alias) {
+ /* Filter out signed digests (a.k.a signature algorithms) */
+ if (strstr(obj->name, "rsa") == NULL && strstr(obj->name, "RSA") == NULL) {
+ Tcl_ListObjAppendElement(NULL, objPtr, Tcl_NewStringObj(obj->name,-1));
+ }
+ }
+}
+
+int
+DigestListCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
+ Tcl_Obj *objPtr = Tcl_NewListObj(0, NULL);
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ OpenSSL_add_all_digests(); /* Make sure they're loaded */
+#endif
+
+ OBJ_NAME_do_all(OBJ_NAME_TYPE_MD_METH, ListCallback, (void *) objPtr);
+ Tcl_ResetResult(interp);
+ Tcl_SetObjResult(interp, objPtr);
+
+ return TCL_OK;
+ clientData = clientData;
+ objc = objc;
+ objv = objv;
+}
+
+/*
+ * Valid SSL and TLS Protocol Versions
+ */
+static const char *protocols[] = {
+ "ssl2", "ssl3", "tls1", "tls1.1", "tls1.2", "tls1.3", NULL
+};
+enum protocol {
+ TLS_SSL2, TLS_SSL3, TLS_TLS1, TLS_TLS1_1, TLS_TLS1_2, TLS_TLS1_3, TLS_NONE
+};
+
+/*
+ *-------------------------------------------------------------------
+ *
* CiphersObjCmd -- list available ciphers
*
* This procedure is invoked to process the "tls::ciphers" command
* to list available ciphers, based upon protocol selected.
*
@@ -939,16 +996,10 @@
* Side effects:
* constructs and destroys SSL context (CTX)
*
*-------------------------------------------------------------------
*/
-static const char *protocols[] = {
- "ssl2", "ssl3", "tls1", "tls1.1", "tls1.2", "tls1.3", NULL
-};
-enum protocol {
- TLS_SSL2, TLS_SSL3, TLS_TLS1, TLS_TLS1_1, TLS_TLS1_2, TLS_TLS1_3, TLS_NONE
-};
static int
CiphersObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
Tcl_Obj *objPtr = NULL;
SSL_CTX *ctx = NULL;
@@ -958,15 +1009,28 @@
int index, verbose = 0, use_supported = 0;
const SSL_METHOD *method;
dprintf("Called");
- if ((objc < 2) || (objc > 4)) {
- Tcl_WrongNumArgs(interp, 1, objv, "protocol ?verbose? ?supported?");
+ if ((objc < 1) || (objc > 4)) {
+ Tcl_WrongNumArgs(interp, 1, objv, "?protocol? ?verbose? ?supported?");
return TCL_ERROR;
}
- if (Tcl_GetIndexFromObj(interp, objv[1], protocols, "protocol", 0, &index) != TCL_OK) {
+ if (objc == 1) {
+ /* List all ciphers */
+ Tcl_Obj *objPtr = Tcl_NewListObj(0, NULL);
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ OpenSSL_add_all_ciphers(); /* Make sure they're loaded */
+#endif
+
+ OBJ_NAME_do_all(OBJ_NAME_TYPE_CIPHER_METH, ListCallback, (void *) objPtr);
+ Tcl_ResetResult(interp);
+ Tcl_SetObjResult(interp, objPtr);
+ return TCL_OK;
+
+ } else if (Tcl_GetIndexFromObj(interp, objv[1], protocols, "protocol", 0, &index) != TCL_OK) {
return TCL_ERROR;
}
if ((objc > 2) && Tcl_GetBooleanFromObj(interp, objv[2], &verbose) != TCL_OK) {
return TCL_ERROR;
}
@@ -1079,10 +1143,11 @@
}
}
SSL_free(ssl);
SSL_CTX_free(ctx);
+ Tcl_ResetResult(interp);
Tcl_SetObjResult(interp, objPtr);
return TCL_OK;
clientData = clientData;
}
@@ -1142,10 +1207,38 @@
}
/*
*-------------------------------------------------------------------
*
+ * VersionObjCmd -- return version string from OpenSSL.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * None.
+ *
+ *-------------------------------------------------------------------
+ */
+static int
+VersionObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
+ Tcl_Obj *objPtr;
+
+ dprintf("Called");
+
+ objPtr = Tcl_NewStringObj(OPENSSL_VERSION_TEXT, -1);
+ Tcl_SetObjResult(interp, objPtr);
+
+ return TCL_OK;
+ clientData = clientData;
+ objc = objc;
+ objv = objv;
+}
+
+/*
+ *-------------------------------------------------------------------
+ *
* HandshakeObjCmd --
*
* This command is used to verify whether the handshake is complete
* or not.
*
@@ -1171,11 +1264,11 @@
return(TCL_ERROR);
}
ERR_clear_error();
- chan = Tcl_GetChannel(interp, Tcl_GetStringFromObj(objv[1], (Tcl_Size *)NULL), NULL);
+ chan = Tcl_GetChannel(interp, Tcl_GetStringFromObj(objv[1], NULL), NULL);
if (chan == (Tcl_Channel) NULL) {
return(TCL_ERROR);
}
/* Make sure to operate on the topmost channel */
@@ -1249,20 +1342,19 @@
SSL_CTX *ctx = NULL;
Tcl_Obj *script = NULL;
Tcl_Obj *password = NULL;
Tcl_Obj *vcmd = NULL;
Tcl_DString upperChannelTranslation, upperChannelBlocking, upperChannelEncoding, upperChannelEOFChar;
- int idx;
- Tcl_Size len;
+ int idx, len;
int flags = TLS_TCL_INIT;
int server = 0; /* is connection incoming or outgoing? */
char *keyfile = NULL;
char *certfile = NULL;
unsigned char *key = NULL;
- Tcl_Size key_len = 0;
+ int key_len = 0;
unsigned char *cert = NULL;
- Tcl_Size cert_len = 0;
+ int cert_len = 0;
char *ciphers = NULL;
char *ciphersuites = NULL;
char *CAfile = NULL;
char *CAdir = NULL;
char *DHparams = NULL;
@@ -1295,20 +1387,20 @@
return TCL_ERROR;
}
ERR_clear_error();
- chan = Tcl_GetChannel(interp, Tcl_GetStringFromObj(objv[1], (Tcl_Size *)NULL), NULL);
+ chan = Tcl_GetChannel(interp, Tcl_GetStringFromObj(objv[1], NULL), NULL);
if (chan == (Tcl_Channel) NULL) {
return TCL_ERROR;
}
/* Make sure to operate on the topmost channel */
chan = Tcl_GetTopChannel(chan);
for (idx = 2; idx < objc; idx++) {
- char *opt = Tcl_GetStringFromObj(objv[idx], (Tcl_Size *)NULL);
+ char *opt = Tcl_GetStringFromObj(objv[idx], NULL);
if (opt[0] != '-')
break;
OPTOBJ("-alpn", alpn);
@@ -1424,12 +1516,12 @@
Tls_Free((char *) statePtr);
return TCL_ERROR;
}
ctx = ((State *)Tcl_GetChannelInstanceData(chan))->ctx;
} else {
- if ((ctx = CTX_Init(statePtr, server, proto, keyfile, certfile, key, cert, (int) key_len,
- (int) cert_len, CAdir, CAfile, ciphers, ciphersuites, level, DHparams)) == NULL) {
+ if ((ctx = CTX_Init(statePtr, server, proto, keyfile, certfile, key, cert, key_len,
+ cert_len, CAdir, CAfile, ciphers, ciphersuites, level, DHparams)) == NULL) {
Tls_Free((char *) statePtr);
return TCL_ERROR;
}
}
@@ -1515,12 +1607,11 @@
http/1.1, h2, h3, ftp, imap, pop3, xmpp-client, xmpp-server, mqtt, irc, etc. */
if (alpn) {
/* Convert a TCL list into a protocol-list in wire-format */
unsigned char *protos, *p;
unsigned int protos_len = 0;
- Tcl_Size cnt, i;
- int j;
+ int i, len, cnt;
Tcl_Obj **list;
if (Tcl_ListObjGetElements(interp, alpn, &cnt, &list) != TCL_OK) {
Tls_Free((char *) statePtr);
return TCL_ERROR;
@@ -1533,20 +1624,20 @@
Tcl_AppendResult(interp, "ALPN protocol name too long", (char *) NULL);
Tcl_SetErrorCode(interp, "TLS", "IMPORT", "ALPN", "FAILED", (char *) NULL);
Tls_Free((char *) statePtr);
return TCL_ERROR;
}
- protos_len += 1 + (int) len;
+ protos_len += 1 + len;
}
/* Build the complete protocol-list */
protos = ckalloc(protos_len);
/* protocol-lists consist of 8-bit length-prefixed, byte strings */
- for (j = 0, p = protos; j < cnt; j++) {
- char *str = Tcl_GetStringFromObj(list[j], &len);
- *p++ = (unsigned char) len;
- memcpy(p, str, (size_t) len);
+ for (i = 0, p = protos; i < cnt; i++) {
+ char *str = Tcl_GetStringFromObj(list[i], &len);
+ *p++ = len;
+ memcpy(p, str, len);
p += len;
}
/* SSL_set_alpn_protos makes a copy of the protocol-list */
/* Note: This functions reverses the return value convention */
@@ -1720,48 +1811,48 @@
const SSL_METHOD *method;
dprintf("Called");
if (!proto) {
- Tcl_AppendResult(interp, "no valid protocol selected", (char *) NULL);
+ Tcl_AppendResult(interp, "no valid protocol selected", NULL);
return NULL;
}
/* create SSL context */
#if OPENSSL_VERSION_NUMBER >= 0x10100000L || defined(NO_SSL2) || defined(OPENSSL_NO_SSL2)
if (ENABLED(proto, TLS_PROTO_SSL2)) {
- Tcl_AppendResult(interp, "SSL2 protocol not supported", (char *) NULL);
+ Tcl_AppendResult(interp, "SSL2 protocol not supported", NULL);
return NULL;
}
#endif
#if defined(NO_SSL3) || defined(OPENSSL_NO_SSL3)
if (ENABLED(proto, TLS_PROTO_SSL3)) {
- Tcl_AppendResult(interp, "SSL3 protocol not supported", (char *) NULL);
+ Tcl_AppendResult(interp, "SSL3 protocol not supported", NULL);
return NULL;
}
#endif
#if defined(NO_TLS1) || defined(OPENSSL_NO_TLS1)
if (ENABLED(proto, TLS_PROTO_TLS1)) {
- Tcl_AppendResult(interp, "TLS 1.0 protocol not supported", (char *) NULL);
+ Tcl_AppendResult(interp, "TLS 1.0 protocol not supported", NULL);
return NULL;
}
#endif
#if defined(NO_TLS1_1) || defined(OPENSSL_NO_TLS1_1)
if (ENABLED(proto, TLS_PROTO_TLS1_1)) {
- Tcl_AppendResult(interp, "TLS 1.1 protocol not supported", (char *) NULL);
+ Tcl_AppendResult(interp, "TLS 1.1 protocol not supported", NULL);
return NULL;
}
#endif
#if defined(NO_TLS1_2) || defined(OPENSSL_NO_TLS1_2)
if (ENABLED(proto, TLS_PROTO_TLS1_2)) {
- Tcl_AppendResult(interp, "TLS 1.2 protocol not supported", (char *) NULL);
+ Tcl_AppendResult(interp, "TLS 1.2 protocol not supported", NULL);
return NULL;
}
#endif
#if defined(NO_TLS1_3) || defined(OPENSSL_NO_TLS1_3)
if (ENABLED(proto, TLS_PROTO_TLS1_3)) {
- Tcl_AppendResult(interp, "TLS 1.3 protocol not supported", (char *) NULL);
+ Tcl_AppendResult(interp, "TLS 1.3 protocol not supported", NULL);
return NULL;
}
#endif
if (proto == 0) {
/* Use full range */
@@ -1845,10 +1936,14 @@
/* Force cipher selection order by server */
if (!isServer) {
SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
}
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ OpenSSL_add_all_algorithms(); /* Load ciphers and digests */
+#endif
SSL_CTX_set_app_data(ctx, (void*)interp); /* remember the interpreter */
SSL_CTX_set_options(ctx, SSL_OP_ALL); /* all SSL bug workarounds */
SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION); /* disable compression even if supported */
SSL_CTX_set_options(ctx, off); /* disable protocol versions */
@@ -2062,11 +2157,11 @@
Tcl_WrongNumArgs(interp, 1, objv, "?-local? channel");
return TCL_ERROR;
}
/* Get channel Id */
- channelName = Tcl_GetStringFromObj(objv[(objc == 2 ? 1 : 2)], (Tcl_Size *) NULL);
+ channelName = Tcl_GetStringFromObj(objv[(objc == 2 ? 1 : 2)], NULL);
chan = Tcl_GetChannel(interp, channelName, &mode);
if (chan == (Tcl_Channel) NULL) {
return TCL_ERROR;
}
@@ -2132,11 +2227,11 @@
/* Verify mode depth */
LAPPEND_INT(interp, objPtr, "verifyDepth", SSL_get_verify_depth(statePtr->ssl));
/* Report the selected protocol as a result of the negotiation */
SSL_get0_alpn_selected(statePtr->ssl, &proto, &len);
- LAPPEND_STR(interp, objPtr, "alpn", (char *)proto, (Tcl_Size) len);
+ LAPPEND_STR(interp, objPtr, "alpn", (char *)proto, (int) len);
LAPPEND_STR(interp, objPtr, "protocol", SSL_get_version(statePtr->ssl), -1);
/* Valid for non-RSA signature and TLS 1.3 */
if (objc == 2) {
res = SSL_get_peer_signature_nid(statePtr->ssl, &nid);
@@ -2182,11 +2277,11 @@
if (objc != 2) {
Tcl_WrongNumArgs(interp, 1, objv, "channel");
return(TCL_ERROR);
}
- chan = Tcl_GetChannel(interp, Tcl_GetStringFromObj(objv[1], (Tcl_Size *)NULL), NULL);
+ chan = Tcl_GetChannel(interp, Tcl_GetStringFromObj(objv[1], NULL), NULL);
if (chan == (Tcl_Channel) NULL) {
return(TCL_ERROR);
}
/* Make sure to operate on the topmost channel */
@@ -2289,16 +2384,16 @@
const unsigned char *session_id, *proto;
char buffer[SSL_MAX_MASTER_KEY_LENGTH];
/* Report the selected protocol as a result of the ALPN negotiation */
SSL_SESSION_get0_alpn_selected(session, &proto, &len2);
- LAPPEND_STR(interp, objPtr, "alpn", (char *) proto, (Tcl_Size) len2);
+ LAPPEND_STR(interp, objPtr, "alpn", (char *) proto, (int) len2);
/* Report the selected protocol as a result of the NPN negotiation */
#ifdef USE_NPN
SSL_get0_next_proto_negotiated(ssl, &proto, &ulen);
- LAPPEND_STR(interp, objPtr, "npn", (char *) proto, (Tcl_Size) ulen);
+ LAPPEND_STR(interp, objPtr, "npn", (char *) proto, (int) ulen);
#endif
/* Resumable session */
LAPPEND_BOOL(interp, objPtr, "resumable", SSL_SESSION_is_resumable(session));
@@ -2308,30 +2403,30 @@
/* Timeout value - SSL_CTX_get_timeout (in seconds) */
LAPPEND_LONG(interp, objPtr, "timeout", SSL_SESSION_get_timeout(session));
/* Session id - TLSv1.2 and below only */
session_id = SSL_SESSION_get_id(session, &ulen);
- LAPPEND_BARRAY(interp, objPtr, "session_id", session_id, (Tcl_Size) ulen);
+ LAPPEND_BARRAY(interp, objPtr, "session_id", session_id, (int) ulen);
/* Session context */
session_id = SSL_SESSION_get0_id_context(session, &ulen);
- LAPPEND_BARRAY(interp, objPtr, "session_context", session_id, (Tcl_Size) ulen);
+ LAPPEND_BARRAY(interp, objPtr, "session_context", session_id, (int) ulen);
/* Session ticket - client only */
SSL_SESSION_get0_ticket(session, &ticket, &len2);
- LAPPEND_BARRAY(interp, objPtr, "session_ticket", ticket, (Tcl_Size) len2);
+ LAPPEND_BARRAY(interp, objPtr, "session_ticket", ticket, (int) len2);
/* Session ticket lifetime hint (in seconds) */
LAPPEND_LONG(interp, objPtr, "lifetime", SSL_SESSION_get_ticket_lifetime_hint(session));
/* Ticket app data */
SSL_SESSION_get0_ticket_appdata(session, &ticket, &len2);
- LAPPEND_BARRAY(interp, objPtr, "ticket_app_data", ticket, (Tcl_Size) len2);
+ LAPPEND_BARRAY(interp, objPtr, "ticket_app_data", ticket, (int) len2);
/* Get master key */
len2 = SSL_SESSION_get_master_key(session, buffer, SSL_MAX_MASTER_KEY_LENGTH);
- LAPPEND_BARRAY(interp, objPtr, "master_key", buffer, (Tcl_Size) len2);
+ LAPPEND_BARRAY(interp, objPtr, "master_key", buffer, (int) len2);
/* Compression id */
unsigned int id = SSL_SESSION_get_compress_id(session);
LAPPEND_STR(interp, objPtr, "compression_id", id == 1 ? "zlib" : "none", -1);
}
@@ -2393,38 +2488,10 @@
}
/*
*-------------------------------------------------------------------
*
- * VersionObjCmd -- return version string from OpenSSL.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * None.
- *
- *-------------------------------------------------------------------
- */
-static int
-VersionObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
- Tcl_Obj *objPtr;
-
- dprintf("Called");
-
- objPtr = Tcl_NewStringObj(OPENSSL_VERSION_TEXT, -1);
- Tcl_SetObjResult(interp, objPtr);
-
- return TCL_OK;
- clientData = clientData;
- objc = objc;
- objv = objv;
-}
-
-/*
- *-------------------------------------------------------------------
- *
* MiscObjCmd -- misc commands
*
* Results:
* A standard Tcl result.
*
@@ -2435,12 +2502,11 @@
*/
static int
MiscObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
static const char *commands [] = { "req", "strreq", NULL };
enum command { C_REQ, C_STRREQ, C_DUMMY };
- Tcl_Size cmd;
- int isStr;
+ int cmd, isStr;
char buffer[16384];
dprintf("Called");
if (objc < 2) {
@@ -2459,12 +2525,11 @@
case C_STRREQ: {
EVP_PKEY *pkey=NULL;
X509 *cert=NULL;
X509_NAME *name=NULL;
Tcl_Obj **listv;
- Tcl_Size listc;
- int i;
+ int listc,i;
BIO *out=NULL;
char *k_C="",*k_ST="",*k_L="",*k_O="",*k_OU="",*k_CN="",*k_Email="";
char *keyout,*pemout,*str;
@@ -2491,11 +2556,12 @@
Tcl_SetVar(interp,keyout,"",0);
Tcl_SetVar(interp,pemout,"",0);
}
if (objc>=6) {
- if (Tcl_ListObjGetElements(interp, objv[5], &listc, &listv) != TCL_OK) {
+ if (Tcl_ListObjGetElements(interp, objv[5],
+ &listc, &listv) != TCL_OK) {
return TCL_ERROR;
}
if ((listc%2) != 0) {
Tcl_SetResult(interp,"Information list must have even number of arguments",NULL);
@@ -2771,23 +2837,27 @@
return TCL_ERROR;
}
#endif
if (TlsLibInit(0) != TCL_OK) {
- Tcl_AppendResult(interp, "could not initialize SSL library", (char *) NULL);
+ Tcl_AppendResult(interp, "could not initialize SSL library", NULL);
return TCL_ERROR;
}
- Tcl_CreateObjCommand(interp, "tls::ciphers", CiphersObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateObjCommand(interp, "tls::connection", ConnectionInfoObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateObjCommand(interp, "tls::handshake", HandshakeObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateObjCommand(interp, "tls::import", ImportObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
+ Tcl_CreateObjCommand(interp, "tls::misc", MiscObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateObjCommand(interp, "tls::unimport", UnimportObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateObjCommand(interp, "tls::status", StatusObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
- Tcl_CreateObjCommand(interp, "tls::version", VersionObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
- Tcl_CreateObjCommand(interp, "tls::misc", MiscObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateObjCommand(interp, "tls::ciphers", CiphersObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
Tcl_CreateObjCommand(interp, "tls::protocols", ProtocolsObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
+ Tcl_CreateObjCommand(interp, "tls::version", VersionObjCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
+
+ Tls_DigestCommands(interp);
+ Tcl_CreateObjCommand(interp, "tls::digests", DigestListCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL);
if (interp) {
Tcl_Eval(interp, tlsTclInitScript);
}
Index: generic/tlsBIO.c
==================================================================
--- generic/tlsBIO.c
+++ generic/tlsBIO.c
@@ -6,24 +6,23 @@
#include "tlsInt.h"
static int BioWrite(BIO *bio, const char *buf, int bufLen) {
Tcl_Channel chan;
- Tcl_Size ret;
+ int ret;
int tclEofChan, tclErrno;
chan = Tls_GetParent((State *) BIO_get_data(bio), 0);
dprintf("[chan=%p] BioWrite(%p, , %d)", (void *)chan, (void *) bio, bufLen);
- ret = Tcl_WriteRaw(chan, buf, (Tcl_Size) bufLen);
+ ret = (int) Tcl_WriteRaw(chan, buf, bufLen);
tclEofChan = Tcl_Eof(chan);
tclErrno = Tcl_GetErrno();
- dprintf("[chan=%p] BioWrite(%d) -> %" TCL_SIZE_MODIFIER "d [tclEof=%d; tclErrno=%d]",
- (void *) chan, bufLen, ret, tclEofChan, Tcl_GetErrno());
+ dprintf("[chan=%p] BioWrite(%d) -> %d [tclEof=%d; tclErrno=%d]", (void *) chan, bufLen, ret, tclEofChan, Tcl_GetErrno());
BIO_clear_flags(bio, BIO_FLAGS_WRITE | BIO_FLAGS_SHOULD_RETRY);
if (tclEofChan && ret <= 0) {
dprintf("Got EOF while reading, returning a Connection Reset error which maps to Soft EOF");
@@ -53,11 +52,11 @@
dprintf("Setting should retry read flag");
BIO_set_retry_read(bio);
}
}
- return((int) ret);
+ return(ret);
}
static int BioRead(BIO *bio, char *buf, int bufLen) {
Tcl_Channel chan;
Tcl_Size ret = 0;
@@ -69,17 +68,16 @@
if (buf == NULL) {
return 0;
}
- ret = Tcl_ReadRaw(chan, buf, (Tcl_Size) bufLen);
+ ret = Tcl_ReadRaw(chan, buf, bufLen);
tclEofChan = Tcl_Eof(chan);
tclErrno = Tcl_GetErrno();
- dprintf("[chan=%p] BioRead(%d) -> %" TCL_SIZE_MODIFIER "d [tclEof=%d; tclErrno=%d]",
- (void *) chan, bufLen, ret, tclEofChan, tclErrno);
+ dprintf("[chan=%p] BioRead(%d) -> %d [tclEof=%d; tclErrno=%d]", (void *) chan, bufLen, ret, tclEofChan, tclErrno);
BIO_clear_flags(bio, BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY);
if (tclEofChan && ret <= 0) {
dprintf("Got EOF while reading, returning a Connection Reset error which maps to Soft EOF");
@@ -110,14 +108,13 @@
BIO_set_retry_write(bio);
}
}
- dprintf("BioRead(%p, , %d) [%p] returning %" TCL_SIZE_MODIFIER "d", (void *) bio,
- bufLen, (void *) chan, ret);
+ dprintf("BioRead(%p, , %d) [%p] returning %i", (void *) bio, bufLen, (void *) chan, ret);
- return((int) ret);
+ return(ret);
}
static int BioPuts(BIO *bio, const char *str) {
dprintf("BioPuts(%p, ) called", bio, str);
ADDED generic/tlsDigest.c
Index: generic/tlsDigest.c
==================================================================
--- /dev/null
+++ generic/tlsDigest.c
@@ -0,0 +1,893 @@
+/*
+ * Digest Command and Stacked Transform Channel
+ *
+ * Copyright (C) 2023 Brian O'Hagan
+ *
+ */
+
+#include "tlsInt.h"
+#include "tclOpts.h"
+#include
+#include
+#include
+#include
+
+/* Constants */
+const char *hex = "0123456789ABCDEF";
+#define REASON() ERR_reason_error_string(ERR_get_error())
+#define BUFFER_SIZE 65536
+#define BIN_FORMAT 0
+#define HEX_FORMAT 1
+
+/*
+ * This structure describes the per-instance state of an SSL channel.
+ *
+ * The SSL processing context is maintained here, in the ClientData
+ */
+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 */
+
+ Tcl_Interp *interp; /* Current interpreter */
+ EVP_MD_CTX *ctx; /* MD Context */
+} DigestState;
+
+/*******************************************************************/
+
+/*
+ *-------------------------------------------------------------------
+ *
+ * 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 DigestFile(Tcl_Interp *interp, Tcl_Obj *filename, const EVP_MD *md, int format,
+ Tcl_Obj *keyObj) {
+ EVP_MD_CTX *ctx = (EVP_MD_CTX *) NULL;
+ HMAC_CTX *hctx = (HMAC_CTX *) NULL;
+ Tcl_Channel chan;
+ unsigned char buf[BUFFER_SIZE];
+ unsigned char md_buf[EVP_MAX_MD_SIZE];
+ unsigned int md_len;
+ unsigned char *key;
+ int key_len, res;
+
+ /* Open file channel */
+ chan = Tcl_FSOpenFileChannel(interp, filename, "rb", 0444);
+ if (chan == (Tcl_Channel) NULL) {
+ return TCL_ERROR;
+ }
+
+ /* Configure channel */
+ if (Tcl_SetChannelOption(interp, chan, "-translation", "binary") == TCL_ERROR) {
+ goto error;
+ }
+ Tcl_SetChannelBufferSize(chan, BUFFER_SIZE);
+
+ /* Create message digest context */
+ if (keyObj == NULL) {
+ ctx = EVP_MD_CTX_new();
+ res = (ctx != NULL);
+ } else {
+ hctx = HMAC_CTX_new();
+ res = (hctx != NULL);
+ }
+ if (!res) {
+ Tcl_AppendResult(interp, "Create digest context failed: ", REASON(), NULL);
+ goto error;
+ }
+
+ /* Initialize hash function */
+ if (keyObj == NULL) {
+ res = EVP_DigestInit_ex(ctx, md, NULL);
+ } else {
+ key = Tcl_GetByteArrayFromObj(keyObj, &key_len);
+ res = HMAC_Init_ex(hctx, (const void *) key, key_len, md, NULL);
+ }
+ if (!res) {
+ Tcl_AppendResult(interp, "Initialize digest failed: ", REASON(), NULL);
+ goto error;
+ }
+
+ /* Read file data and update hash function */
+ while (!Tcl_Eof(chan)) {
+ int len = Tcl_ReadRaw(chan, (char *) buf, BUFFER_SIZE);
+ if (keyObj == NULL) {
+ res = EVP_DigestUpdate(ctx, &buf, (size_t) len);
+ } else {
+ res = HMAC_Update(hctx, &buf[0], (size_t) len);
+ }
+ if (len > 0 && !res) {
+ Tcl_AppendResult(interp, "Update digest failed: ", REASON(), NULL);
+ goto error;
+ }
+ }
+
+ /* Close channel */
+ if (Tcl_Close(interp, chan) == TCL_ERROR) {
+ chan = (Tcl_Channel) NULL;
+ goto error;
+ }
+ chan = (Tcl_Channel) NULL;
+
+ /* Finalize hash function and calculate message digest */
+ if (keyObj == NULL) {
+ res = EVP_DigestFinal_ex(ctx, md_buf, &md_len);
+ } else {
+ res = HMAC_Final(hctx, md_buf, &md_len);
+ }
+ if (!res) {
+ Tcl_AppendResult(interp, "Finalize digest failed: ", REASON(), NULL);
+ goto error;
+ }
+
+ /* Done with struct */
+ EVP_MD_CTX_free(ctx);
+ ctx = NULL;
+
+ /* Return message digest as either a binary or hex string */
+ 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);
+ }
+ return TCL_OK;
+
+error:
+ if (chan != (Tcl_Channel) NULL) {
+ Tcl_Close(interp, chan);
+ }
+ if (ctx != (EVP_MD_CTX *) NULL) {
+ EVP_MD_CTX_free(ctx);
+ }
+ if (hctx != (HMAC_CTX *) NULL) {
+ HMAC_CTX_free(hctx);
+ }
+ return TCL_ERROR;
+}
+
+/*******************************************************************/
+
+/*
+ *-------------------------------------------------------------------
+ *
+ * DigestBlockModeProc --
+ *
+ * This procedure is invoked by the generic IO level
+ * to set blocking and nonblocking modes.
+ *
+ * Returns:
+ * 0 if successful or POSIX error code if failed.
+ *
+ * Side effects:
+ * Sets the device into blocking or nonblocking mode.
+ * Can call Tcl_SetChannelError.
+ *
+ *-------------------------------------------------------------------
+ */
+static int DigestBlockModeProc(ClientData clientData, int mode) {
+ DigestState *statePtr = (DigestState *) clientData;
+
+ if (mode == TCL_MODE_NONBLOCKING) {
+ statePtr->flags |= TLS_TCL_ASYNC;
+ } else {
+ statePtr->flags &= ~(TLS_TCL_ASYNC);
+ }
+ return 0;
+}
+
+/*
+ *-------------------------------------------------------------------
+ *
+ * DigestFree --
+ *
+ * This procedure removes a digest state structure
+ *
+ * Returns:
+ * Nothing
+ *
+ * Side effects:
+ * Removes structure
+ *
+ *-------------------------------------------------------------------
+ */
+void DigestFree (DigestState *statePtr) {
+ if (statePtr == (DigestState *) NULL) return;
+
+ if (statePtr->ctx != NULL) {
+ EVP_MD_CTX_free(statePtr->ctx);
+ }
+ ckfree(statePtr);
+}
+
+/*
+ *-------------------------------------------------------------------
+ *
+ * DigestCloseProc --
+ *
+ * This procedure is invoked by the generic IO level to perform
+ * channel-type-specific cleanup when digest channel is closed.
+ *
+ * Returns:
+ * TCL_OK or TCL_ERROR
+ *
+ * Side effects:
+ * Writes digest to output and closes the channel.
+ *
+ *-------------------------------------------------------------------
+ */
+int DigestCloseProc(ClientData clientData, Tcl_Interp *interp) {
+ DigestState *statePtr = (DigestState *) clientData;
+ int result = 0;
+
+ /* Cancel active timer, if any */
+ if (statePtr->timer != (Tcl_TimerToken) NULL) {
+ Tcl_DeleteTimerHandler(statePtr->timer);
+ statePtr->timer = (Tcl_TimerToken) NULL;
+ }
+
+ /* Clean-up */
+ DigestFree(statePtr);
+ return result;
+}
+
+/*
+ * Same as DigestCloseProc but with individual read and write close control
+ */
+static int DigestClose2Proc(ClientData instanceData, Tcl_Interp *interp, int flags) {
+
+ if ((flags & (TCL_CLOSE_READ | TCL_CLOSE_WRITE)) == 0) {
+ return DigestCloseProc(instanceData, interp);
+ }
+ return EINVAL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DigestInputProc --
+ *
+ * Called by the generic IO system to read data from transform.
+ *
+ * Returns:
+ * Total bytes read
+ *
+ * Side effects:
+ * 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;
+ *errorCodePtr = 0;
+
+ if (toRead <= 0 || statePtr->self == (Tcl_Channel) NULL) {
+ return 0;
+ }
+
+ /* Get bytes from underlying channel */
+ parent = Tcl_GetStackedChannel(statePtr->self);
+ read = Tcl_ReadRaw(parent, buf, toRead);
+
+ /* Add to message digest */
+ if (read > 0) {
+ /* OK */
+ if (!EVP_DigestUpdate(statePtr->ctx, buf, read)) {
+ Tcl_SetChannelError(statePtr->self, Tcl_ObjPrintf("Digest update failed: %s", REASON()));
+ *errorCodePtr = EINVAL;
+ return -1;
+ }
+ *errorCodePtr = EAGAIN;
+ read = -1;
+
+ } else if (read < 0) {
+ /* Error */
+ *errorCodePtr = Tcl_GetErrno();
+
+ } else if (!(statePtr->flags & 0x10)) {
+ /* EOF */
+ *errorCodePtr = 0;
+ unsigned char md_buf[EVP_MAX_MD_SIZE];
+ unsigned int md_len = 0;
+
+ /* Get message digest */
+ if (!EVP_DigestFinal_ex(statePtr->ctx, md_buf, &md_len)) {
+ *errorCodePtr = EINVAL;
+
+ /* Write message digest to output channel as byte array or hex string */
+ } else if (md_len > 0) {
+ if (statePtr->format == BIN_FORMAT) {
+ read = md_len;
+ memcpy(buf, md_buf, read);
+
+ } else {
+ 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];
+ }
+ read = md_len*2;
+ memcpy(buf, hex_buf, read);
+ }
+ }
+ statePtr->flags |= 0x10;
+ }
+ return read;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DigestOutputProc --
+ *
+ * Called by the generic IO system to write data to transform.
+ *
+ * Returns:
+ * Total bytes written
+ *
+ * Side effects:
+ * Get data from buf and update digest
+ *
+ *----------------------------------------------------------------------
+ */
+ int DigestOutputProc(ClientData clientData, const char *buf, int toWrite, int *errorCodePtr) {
+ DigestState *statePtr = (DigestState *) clientData;
+ *errorCodePtr = 0;
+
+ if (toWrite <= 0 || statePtr->self == (Tcl_Channel) NULL) {
+ return 0;
+ }
+ return toWrite;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DigestSetOptionProc --
+ *
+ * Called by the generic IO system to set channel option to value.
+ *
+ * Returns:
+ * TCL_OK if successful or TCL_ERROR if failed.
+ *
+ * Side effects:
+ * Updates channel option to new value.
+ *
+ *----------------------------------------------------------------------
+ */
+static int DigestSetOptionProc(ClientData clientData, Tcl_Interp *interp, const char *optionName,
+ const char *optionValue) {
+ DigestState *statePtr = (DigestState *) clientData;
+ Tcl_Channel parent;
+ Tcl_DriverSetOptionProc *setOptionProc;
+
+ if (statePtr->self == (Tcl_Channel) NULL) {
+ return TCL_ERROR;
+ }
+
+ /* Delegate options downstream */
+ parent = Tcl_GetStackedChannel(statePtr->self);
+ setOptionProc = Tcl_ChannelSetOptionProc(Tcl_GetChannelType(parent));
+ if (setOptionProc != NULL) {
+ return (*setOptionProc)(Tcl_GetChannelInstanceData(parent), interp, optionName, optionValue);
+ } else {
+ return TCL_ERROR;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DigestGetOptionProc --
+ *
+ * Called by the generic IO system to get channel option's value.
+ *
+ * Returns:
+ * TCL_OK if successful or TCL_ERROR if failed.
+ *
+ * Side effects:
+ * Sets result to option's value
+ *
+ *----------------------------------------------------------------------
+ */
+static int DigestGetOptionProc(ClientData clientData, Tcl_Interp *interp, const char *optionName,
+ Tcl_DString *optionValue) {
+ DigestState *statePtr = (DigestState *) clientData;
+ Tcl_Channel parent;
+ Tcl_DriverGetOptionProc *getOptionProc;
+
+ if (statePtr->self == (Tcl_Channel) NULL) {
+ return TCL_ERROR;
+ }
+
+ /* Delegate options downstream */
+ parent = Tcl_GetStackedChannel(statePtr->self);
+ getOptionProc = Tcl_ChannelGetOptionProc(Tcl_GetChannelType(parent));
+ if (getOptionProc != NULL) {
+ return (*getOptionProc)(Tcl_GetChannelInstanceData(parent), interp, optionName, optionValue);
+ } else if (optionName == (char*) NULL) {
+ /* Request is query for all options, this is ok. */
+ return TCL_OK;
+ }
+
+ /* Request for a specific option has to fail, we don't have any. */
+ return Tcl_BadChannelOption(interp, optionName, "");
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DigestTimerHandler --
+ *
+ * Called by the notifier via timer to flush out pending input data.
+ *
+ * Returns:
+ * Nothing
+ *
+ * Side effects:
+ * May call Tcl_NotifyChannel
+ *
+ *----------------------------------------------------------------------
+ */
+static void DigestTimerHandler(ClientData clientData) {
+ DigestState *statePtr = (DigestState *) clientData;
+
+ if (statePtr->self == (Tcl_Channel) NULL) {
+ return;
+ }
+
+ /* Clear timer token */
+ statePtr->timer = (Tcl_TimerToken) NULL;
+
+ /* Fire event if there is pending data, skip otherwise */
+ if ((statePtr->watchMask & TCL_READABLE) && (Tcl_InputBuffered(statePtr->self) > 0)) {
+ Tcl_NotifyChannel(statePtr->self, TCL_READABLE);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DigestWatchProc --
+ *
+ * Initialize the notifier to watch for events from this channel.
+ *
+ * Returns:
+ * Nothing
+ *
+ * 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;
+
+ if (statePtr->self == (Tcl_Channel) NULL) {
+ return;
+ }
+
+ /* Store OR-ed combination of TCL_READABLE, TCL_WRITABLE and TCL_EXCEPTION */
+ statePtr->watchMask = mask;
+
+ /* Propagate mask info to parent channel */
+ parent = Tcl_GetStackedChannel(statePtr->self);
+ watchProc = Tcl_ChannelWatchProc(Tcl_GetChannelType(parent));
+ watchProc(Tcl_GetChannelInstanceData(parent), mask);
+
+ /* Remove pending timer */
+ if (statePtr->timer != (Tcl_TimerToken) NULL) {
+ Tcl_DeleteTimerHandler(statePtr->timer);
+ statePtr->timer = (Tcl_TimerToken) NULL;
+ }
+
+ /* If there is data pending, set new timer to call Tcl_NotifyChannel */
+ if ((mask & TCL_READABLE) && (Tcl_InputBuffered(statePtr->self) > 0)) {
+ statePtr->timer = Tcl_CreateTimerHandler(READ_DELAY, DigestTimerHandler, (ClientData) statePtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DigestGetHandleProc --
+ *
+ * Called from Tcl_GetChannelHandle to retrieve OS specific file handle
+ * from inside this channel. Not used for transformations?
+ *
+ * Returns:
+ * If direction is TCL_READABLE return the handle used for input, or if
+ * TCL_WRITABLE return the handle used for output.
+ *
+ * Side effects:
+ * None
+ *
+ *----------------------------------------------------------------------
+ */
+int DigestGetHandleProc(ClientData clientData, int direction, ClientData *handlePtr) {
+ DigestState *statePtr = (DigestState *) clientData;
+
+ if (statePtr->self == (Tcl_Channel) NULL) {
+ return TCL_ERROR;
+ }
+ return Tcl_GetChannelHandle(Tcl_GetStackedChannel(statePtr->self), direction, handlePtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DigestNotifyProc --
+ *
+ * Called by Tcl to inform us of activity on the underlying channel.
+ *
+ * Returns:
+ * Unchanged interestMask which is an OR-ed combination of TCL_READABLE or TCL_WRITABLE
+ *
+ * Side effects:
+ * Cancels any pending timer.
+ *
+ *----------------------------------------------------------------------
+ */
+int DigestNotifyProc(ClientData clientData, int interestMask) {
+ DigestState *statePtr = (DigestState *) clientData;
+
+ /* Skip timer event as redundant */
+ if (statePtr->timer != (Tcl_TimerToken) NULL) {
+ Tcl_DeleteTimerHandler(statePtr->timer);
+ statePtr->timer = (Tcl_TimerToken) NULL;
+ }
+ return interestMask;
+}
+
+/*
+ *
+ * Channel type structure definition for digest transformations.
+ *
+ */
+static const Tcl_ChannelType digestChannelType = {
+ "digest", /* Type name */
+ TCL_CHANNEL_VERSION_5, /* v5 channel */
+ DigestCloseProc, /* Close proc */
+ DigestInputProc, /* Input proc */
+ DigestOutputProc, /* Output proc */
+ NULL, /* Seek proc */
+ DigestSetOptionProc, /* Set option proc */
+ DigestGetOptionProc, /* Get option proc */
+ DigestWatchProc, /* Initialize notifier */
+ DigestGetHandleProc, /* Get OS handles out of channel */
+ DigestClose2Proc, /* close2proc */
+ DigestBlockModeProc, /* Set blocking/nonblocking mode*/
+ NULL, /* Flush proc */
+ DigestNotifyProc, /* Handling of events bubbling up */
+ NULL, /* Wide seek proc */
+ NULL, /* Thread action */
+ NULL /* Truncate */
+};
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DigestChannel --
+ *
+ * Create a stacked channel for a message digest transformation.
+ *
+ * Returns:
+ * TCL_OK or TCL_ERROR
+ *
+ * Side effects:
+ * Adds transform to channel and sets result to channel name or error message.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+DigestChannel(Tcl_Interp *interp, const char *channel, const EVP_MD *md, int format) {
+ int mode; /* OR-ed combination of TCL_READABLE and TCL_WRITABLE */
+ Tcl_Channel chan;
+ EVP_MD_CTX *ctx;
+ DigestState *statePtr;
+
+ /* Validate args */
+ if (channel == (const char *) NULL || md == (const EVP_MD *) NULL) {
+ return TCL_ERROR;
+ }
+
+ chan = Tcl_GetChannel(interp, channel, &mode);
+ if (chan == (Tcl_Channel) NULL) {
+ return TCL_ERROR;
+ }
+
+ /* Make sure to operate on the topmost channel */
+ chan = Tcl_GetTopChannel(chan);
+
+ /* Create internal storage structures */
+ ctx = EVP_MD_CTX_new();
+ statePtr = (DigestState *) ckalloc((unsigned) sizeof(DigestState));
+ if (ctx != NULL && statePtr != NULL) {
+ memset(statePtr, 0, sizeof(DigestState));
+ statePtr->self = chan; /* This socket channel */
+ statePtr->timer = (Tcl_TimerToken) NULL; /* Timer to flush data */
+ statePtr->flags = 0; /* Chan config flags */
+ statePtr->watchMask = 0; /* Current WatchProc mask */
+ statePtr->mode = mode; /* Current mode of parent channel */
+ statePtr->format = format; /* Output format */
+ statePtr->interp = interp; /* Current interpreter */
+ statePtr->ctx = ctx; /* SSL Context */
+ } else {
+ Tcl_AppendResult(interp, "Initialize digest error: memory allocation failure", (char *) NULL);
+ EVP_MD_CTX_free(ctx);
+ DigestFree(statePtr);
+ return TCL_ERROR;
+ }
+
+ /* Initialize digest */
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ if (!EVP_DigestInit_ex(ctx, md, NULL))
+#else
+ if (!EVP_DigestInit_ex2(ctx, md, NULL))
+#endif
+ {
+ Tcl_AppendResult(interp, "Initialize digest error: ", REASON(), (char *) NULL);
+ EVP_MD_CTX_free(ctx);
+ DigestFree(statePtr);
+ 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);
+ }
+
+ /* Stack channel */
+ statePtr->self = Tcl_StackChannel(interp, &digestChannelType, (ClientData) statePtr, mode, chan);
+ if (statePtr->self == (Tcl_Channel) NULL) {
+ EVP_MD_CTX_free(ctx);
+ DigestFree(statePtr);
+ return TCL_ERROR;
+ }
+
+ Tcl_SetResult(interp, (char *) Tcl_GetChannelName(chan), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*******************************************************************/
+
+/*
+ *-------------------------------------------------------------------
+ *
+ * DigestHashFunction --
+ *
+ * Calculate message digest using hash function.
+ *
+ * Returns:
+ * TCL_OK or TCL_ERROR
+ *
+ * Side effects:
+ * Sets result to message digest or error message
+ *
+ *-------------------------------------------------------------------
+ */
+int
+DigestHashFunction(Tcl_Interp *interp, int objc, Tcl_Obj *const objv[],
+ const EVP_MD *md, int format, Tcl_Obj *keyObj) {
+ char *data;
+ int len, res;
+ unsigned int md_len;
+ unsigned char md_buf[EVP_MAX_MD_SIZE];
+
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "data");
+ return TCL_ERROR;
+ }
+
+ /* Get data */
+ data = Tcl_GetByteArrayFromObj(objv[1], &len);
+ if (data == NULL || len == 0) {
+ Tcl_SetResult(interp, "No data", NULL);
+ return TCL_ERROR;
+ }
+
+ /* Calculate digest based on hash function */
+ if (keyObj == NULL) {
+ res = EVP_Digest(data, (size_t) len, md_buf, &md_len, md, NULL);
+ } else {
+ unsigned char *key, *hmac;
+ int key_len;
+
+ key = Tcl_GetByteArrayFromObj(keyObj, &key_len);
+ 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) {
+ 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;
+}
+
+/*
+ *-------------------------------------------------------------------
+ *
+ * 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
+ *
+ *-------------------------------------------------------------------
+ */
+static int
+DigestObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
+ int idx, len, format = HEX_FORMAT, key_len = 0, data_len = 0;
+ const char *digestname, *channel = NULL;
+ Tcl_Obj *dataObj = NULL, *fileObj = NULL, *keyObj = NULL;
+ unsigned char *key = NULL;
+ const EVP_MD *md;
+
+ Tcl_ResetResult(interp);
+
+ if (objc < 3 || objc > 7) {
+ Tcl_WrongNumArgs(interp, 1, objv, "type ?-bin|-hex? ?-key hmac_key? [-channel chan | -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);
+ return TCL_ERROR;
+ }
+
+ /* Optimal case for blob of data */
+ if (objc == 3) {
+ return DigestHashFunction(interp, --objc, ++objv, md, format, 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);
+ OPTOBJ("-data", dataObj);
+ OPTSTR("-chan", channel);
+ OPTSTR("-channel", channel);
+ OPTOBJ("-file", fileObj);
+ OPTOBJ("-filename", fileObj);
+ OPTOBJ("-key", keyObj);
+
+ OPTBAD("option", "-bin, -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];
+ }
+
+ /* Calc digest on file, stacked channel, or data blob */
+ if (fileObj != NULL) {
+ return DigestFile(interp, fileObj, md, format, keyObj);
+ } else if (channel != NULL) {
+ return DigestChannel(interp, channel, md, format);
+ } else if (dataObj != NULL) {
+ Tcl_Obj *objs[2];
+ objs[0] = NULL;
+ objs[1] = dataObj;
+ return DigestHashFunction(interp, 2, objs, md, format, keyObj);
+ }
+
+ Tcl_AppendResult(interp, "No data specified.", NULL);
+ return TCL_ERROR;
+}
+
+/*
+ *-------------------------------------------------------------------
+ *
+ * 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 DigestHashFunction(interp, objc, objv, EVP_md4(), HEX_FORMAT, NULL);
+}
+
+int DigestMD5Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
+ return DigestHashFunction(interp, objc, objv, EVP_md5(), HEX_FORMAT, NULL);
+}
+
+int DigestSHA1Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
+ return DigestHashFunction(interp, objc, objv, EVP_sha1(), HEX_FORMAT, NULL);
+}
+
+int DigestSHA256Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) {
+ return DigestHashFunction(interp, objc, objv, EVP_sha256(), 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::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);
+ return TCL_OK;
+}
+
Index: generic/tlsInt.h
==================================================================
--- generic/tlsInt.h
+++ generic/tlsInt.h
@@ -40,15 +40,11 @@
/*
* Backwards compatibility for size type change
*/
#if TCL_MAJOR_VERSION < 9 && TCL_MINOR_VERSION < 7
- #ifndef Tcl_Size
- typedef int Tcl_Size;
- #endif
-
- #define TCL_SIZE_MODIFIER ""
+# define Tcl_Size int
#endif
#include
#include
#include
@@ -196,11 +192,12 @@
Tcl_Obj *Tls_NewCAObj(Tcl_Interp *interp, const SSL *ssl, int peer);
void Tls_Error(State *statePtr, char *msg);
void Tls_Free(char *blockPtr);
void Tls_Clean(State *statePtr);
int Tls_WaitForConnect(State *statePtr, int *errorCodePtr, int handshakeFailureIsPermanent);
+int Tls_DigestCommands(Tcl_Interp *interp);
BIO *BIO_new_tcl(State* statePtr, int flags);
#define PTR2INT(x) ((int) ((intptr_t) (x)))
#endif /* _TLSINT_H */
Index: generic/tlsX509.c
==================================================================
--- generic/tlsX509.c
+++ generic/tlsX509.c
@@ -18,17 +18,21 @@
/*
* Binary string to hex string
*/
-int String_to_Hex(char* input, int ilen, char *output, int olen) {
+int String_to_Hex(unsigned char* input, int ilen, unsigned char *output, int olen) {
int count = 0;
+ unsigned char *iptr = input;
+ unsigned char *optr = &output[0];
+ const char *hex = "0123456789ABCDEF";
for (int i = 0; i < ilen && count < olen - 1; i++, count += 2) {
- sprintf(output + count, "%02X", input[i] & 0xff);
+ *optr++ = hex[(*iptr>>4)&0xF];
+ *optr++ = hex[(*iptr++)&0xF];
}
- output[count] = 0;
+ *optr = 0;
return count;
}
/*
* BIO to Buffer
@@ -77,14 +81,14 @@
Tcl_Obj *resultPtr = NULL;
int len = 0;
char buffer[1024];
if (astring != NULL) {
- len = String_to_Hex((char *)ASN1_STRING_get0_data(astring),
+ len = String_to_Hex(ASN1_STRING_get0_data(astring),
ASN1_STRING_length(astring), buffer, 1024);
}
- resultPtr = Tcl_NewStringObj(buffer, (Tcl_Size) len);
+ resultPtr = Tcl_NewStringObj(buffer, len);
return resultPtr;
}
/*
* Get Key Usage
@@ -202,11 +206,11 @@
if (names = X509_get_ext_d2i(cert, nid, NULL, NULL)) {
for (int i=0; i < sk_GENERAL_NAME_num(names); i++) {
const GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);
len = BIO_to_Buffer(name && GENERAL_NAME_print(bio, name), bio, buffer, 1024);
- LAPPEND_STR(interp, listPtr, NULL, buffer, (Tcl_Size) len);
+ LAPPEND_STR(interp, listPtr, NULL, buffer, len);
}
sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
}
return listPtr;
}
@@ -279,20 +283,20 @@
for (int j = 0; j < sk_GENERAL_NAME_num(distpoint->name.fullname); j++) {
GENERAL_NAME *gen = sk_GENERAL_NAME_value(distpoint->name.fullname, j);
int type;
ASN1_STRING *uri = GENERAL_NAME_get0_value(gen, &type);
if (type == GEN_URI) {
- LAPPEND_STR(interp, listPtr, NULL, ASN1_STRING_get0_data(uri), (Tcl_Size) ASN1_STRING_length(uri));
+ LAPPEND_STR(interp, listPtr, NULL, ASN1_STRING_get0_data(uri), ASN1_STRING_length(uri));
}
}
} else if (distpoint->type == 1) {
/* relative-name X509NAME */
STACK_OF(X509_NAME_ENTRY) *sk_relname = distpoint->name.relativename;
for (int j = 0; j < sk_X509_NAME_ENTRY_num(sk_relname); j++) {
X509_NAME_ENTRY *e = sk_X509_NAME_ENTRY_value(sk_relname, j);
ASN1_STRING *d = X509_NAME_ENTRY_get_data(e);
- LAPPEND_STR(interp, listPtr, NULL, ASN1_STRING_data(d), (Tcl_Size) ASN1_STRING_length(d));
+ LAPPEND_STR(interp, listPtr, NULL, ASN1_STRING_data(d), ASN1_STRING_length(d));
}
}
}
CRL_DIST_POINTS_free(crl);
}
@@ -333,11 +337,11 @@
for (int i = 0; i < sk_ACCESS_DESCRIPTION_num(ads); i++) {
ad = sk_ACCESS_DESCRIPTION_value(ads, i);
if (OBJ_obj2nid(ad->method) == NID_ad_ca_issuers && ad->location) {
if (ad->location->type == GEN_URI) {
len = ASN1_STRING_to_UTF8(&buf, ad->location->d.uniformResourceIdentifier);
- Tcl_ListObjAppendElement(interp, listPtr, Tcl_NewStringObj(buf, (Tcl_Size) len));
+ Tcl_ListObjAppendElement(interp, listPtr, Tcl_NewStringObj(buf, len));
OPENSSL_free(buf);
break;
}
}
}
@@ -393,53 +397,53 @@
X509_get0_signature(&sig, &sig_alg, cert);
/* sig_nid = X509_get_signature_nid(cert) */
sig_nid = OBJ_obj2nid(sig_alg->algorithm);
LAPPEND_STR(interp, certPtr, "signatureAlgorithm", OBJ_nid2ln(sig_nid), -1);
len = (sig_nid != NID_undef) ? String_to_Hex(sig->data, sig->length, buffer, BUFSIZ) : 0;
- LAPPEND_STR(interp, certPtr, "signatureValue", buffer, (Tcl_Size) len);
+ LAPPEND_STR(interp, certPtr, "signatureValue", buffer, len);
}
/* Version of the encoded certificate - RFC 5280 section 4.1.2.1 */
LAPPEND_LONG(interp, certPtr, "version", X509_get_version(cert)+1);
/* Unique number assigned by CA to certificate - RFC 5280 section 4.1.2.2 */
len = BIO_to_Buffer(i2a_ASN1_INTEGER(bio, X509_get0_serialNumber(cert)), bio, buffer, BUFSIZ);
- LAPPEND_STR(interp, certPtr, "serialNumber", buffer, (Tcl_Size) len);
+ LAPPEND_STR(interp, certPtr, "serialNumber", buffer, len);
/* Signature algorithm used by the CA to sign the certificate. Must match
signatureAlgorithm. RFC 5280 section 4.1.2.3 */
LAPPEND_STR(interp, certPtr, "signature", OBJ_nid2ln(X509_get_signature_nid(cert)), -1);
/* Issuer identifies the entity that signed and issued the cert. RFC 5280 section 4.1.2.4 */
len = BIO_to_Buffer(X509_NAME_print_ex(bio, X509_get_issuer_name(cert), 0, flags), bio, buffer, BUFSIZ);
- LAPPEND_STR(interp, certPtr, "issuer", buffer, (Tcl_Size) len);
+ LAPPEND_STR(interp, certPtr, "issuer", buffer, len);
/* Certificate validity period is the interval the CA warrants that it will
maintain info on the status of the certificate. RFC 5280 section 4.1.2.5 */
/* Get Validity - Not Before */
len = BIO_to_Buffer(ASN1_TIME_print(bio, X509_get0_notBefore(cert)), bio, buffer, BUFSIZ);
- LAPPEND_STR(interp, certPtr, "notBefore", buffer, (Tcl_Size) len);
+ LAPPEND_STR(interp, certPtr, "notBefore", buffer, len);
/* Get Validity - Not After */
len = BIO_to_Buffer(ASN1_TIME_print(bio, X509_get0_notAfter(cert)), bio, buffer, BUFSIZ);
- LAPPEND_STR(interp, certPtr, "notAfter", buffer, (Tcl_Size) len);
+ LAPPEND_STR(interp, certPtr, "notAfter", buffer, len);
/* Subject identifies the entity associated with the public key stored in
the subject public key field. RFC 5280 section 4.1.2.6 */
len = BIO_to_Buffer(X509_NAME_print_ex(bio, X509_get_subject_name(cert), 0, flags), bio, buffer, BUFSIZ);
- LAPPEND_STR(interp, certPtr, "subject", buffer, (Tcl_Size) len);
+ LAPPEND_STR(interp, certPtr, "subject", buffer, len);
/* SHA1 Digest (Fingerprint) of cert - DER representation */
if (X509_digest(cert, EVP_sha1(), md, &len)) {
len = String_to_Hex(md, len, buffer, BUFSIZ);
- LAPPEND_STR(interp, certPtr, "sha1_hash", buffer, (Tcl_Size) len);
+ LAPPEND_STR(interp, certPtr, "sha1_hash", buffer, len);
}
/* SHA256 Digest (Fingerprint) of cert - DER representation */
if (X509_digest(cert, EVP_sha256(), md, &len)) {
len = String_to_Hex(md, len, buffer, BUFSIZ);
- LAPPEND_STR(interp, certPtr, "sha256_hash", buffer, (Tcl_Size) len);
+ LAPPEND_STR(interp, certPtr, "sha256_hash", buffer, len);
}
/* Subject Public Key Info specifies the public key and identifies the
algorithm with which the key is used. RFC 5280 section 4.1.2.7 */
if (X509_get_signature_info(cert, &mdnid, &pknid, &bits, &xflags)) {
@@ -450,24 +454,24 @@
LAPPEND_STR(interp, certPtr, "publicKeyAlgorithm", OBJ_nid2ln(pknid), -1);
LAPPEND_INT(interp, certPtr, "bits", bits); /* Effective security bits */
key = X509_get0_pubkey_bitstr(cert);
len = String_to_Hex(key->data, key->length, buffer, BUFSIZ);
- LAPPEND_STR(interp, certPtr, "publicKey", buffer, (Tcl_Size) len);
+ LAPPEND_STR(interp, certPtr, "publicKey", buffer, len);
len = 0;
if (X509_pubkey_digest(cert, EVP_get_digestbynid(pknid), md, &n)) {
len = String_to_Hex(md, (int)n, buffer, BUFSIZ);
}
- LAPPEND_STR(interp, certPtr, "publicKeyHash", buffer, (Tcl_Size) len);
+ LAPPEND_STR(interp, certPtr, "publicKeyHash", buffer, len);
/* digest of the DER representation of the certificate */
len = 0;
if (X509_digest(cert, EVP_get_digestbynid(mdnid), md, &n)) {
len = String_to_Hex(md, (int)n, buffer, BUFSIZ);
}
- LAPPEND_STR(interp, certPtr, "signatureHash", buffer, (Tcl_Size) len);
+ LAPPEND_STR(interp, certPtr, "signatureHash", buffer, len);
}
/* Certificate Purpose. Call before checking for extensions. */
LAPPEND_STR(interp, certPtr, "purpose", Tls_x509Purpose(cert), -1);
LAPPEND_OBJ(interp, certPtr, "certificatePurpose", Tls_x509Purposes(interp, cert));
@@ -489,18 +493,18 @@
const ASN1_BIT_STRING *iuid, *suid;
X509_get0_uids(cert, &iuid, &suid);
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj("issuerUniqueId", -1));
if (iuid != NULL) {
- Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewByteArrayObj((char *)iuid->data, (Tcl_Size) iuid->length));
+ Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewByteArrayObj((char *)iuid->data, iuid->length));
} else {
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj("", -1));
}
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj("subjectUniqueId", -1));
if (suid != NULL) {
- Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewByteArrayObj((char *)suid->data, (Tcl_Size) suid->length));
+ Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewByteArrayObj((char *)suid->data, suid->length));
} else {
Tcl_ListObjAppendElement(interp, certPtr, Tcl_NewStringObj("", -1));
}
}
@@ -583,24 +587,24 @@
/* Certificate Alias. If uses a PKCS#12 structure, alias will reflect the
friendlyName attribute (RFC 2985). */
{
len = 0;
char *string = X509_alias_get0(cert, &len);
- LAPPEND_STR(interp, certPtr, "alias", string, (Tcl_Size) len);
+ LAPPEND_STR(interp, certPtr, "alias", string, len);
}
/* Certificate and dump all data */
{
char certStr[CERT_STR_SIZE];
/* Get certificate */
len = BIO_to_Buffer(PEM_write_bio_X509(bio, cert), bio, certStr, CERT_STR_SIZE);
- LAPPEND_STR(interp, certPtr, "certificate", certStr, (Tcl_Size) len);
+ LAPPEND_STR(interp, certPtr, "certificate", certStr, len);
/* Get all cert info */
len = BIO_to_Buffer(X509_print_ex(bio, cert, flags, 0), bio, certStr, CERT_STR_SIZE);
- LAPPEND_STR(interp, certPtr, "all", certStr, (Tcl_Size) len);
+ LAPPEND_STR(interp, certPtr, "all", certStr, len);
}
BIO_free(bio);
return certPtr;
}
Index: tests/ciphers.csv
==================================================================
--- tests/ciphers.csv
+++ tests/ciphers.csv
@@ -1,10 +1,10 @@
# Group,Name,Constraints,Setup,Body,Cleanup,Match,Result,Output,Error Output,Return Codes
command,package require tls,,,,,,,,,
command,,,,,,,,,,
command,# Make sure path includes location of OpenSSL executable,,,,,,,,,
-command,"if {[info exists ::env(OPENSSL)]} {set ::env(path) [string cat [file join $::env(OPENSSL) bin] "";"" $::env(path)}",,,,,,,,,
+command,"if {[info exists ::env(OPENSSL)]} {set ::env(path) [string cat [file join $::env(OPENSSL) bin] "";"" $::env(path)]}",,,,,,,,,
command,,,,,,,,,,
command,# Constraints,,,,,,,,,
command,set protocols [list ssl2 ssl3 tls1 tls1.1 tls1.2 tls1.3],,,,,,,,,
command,foreach protocol $protocols {::tcltest::testConstraint $protocol 0},,,,,,,,,
command,foreach protocol [::tls::protocols] {::tcltest::testConstraint $protocol 1},,,,,,,,,
@@ -11,36 +11,77 @@
command,"::tcltest::testConstraint OpenSSL [string match ""OpenSSL*"" [::tls::version]]",,,,,,,,,
,,,,,,,,,,
command,# Helper functions,,,,,,,,,
command,"proc lcompare {list1 list2} {set m """";set u """";foreach i $list1 {if {$i ni $list2} {lappend m $i}};foreach i $list2 {if {$i ni $list1} {lappend u $i}};return [list ""missing"" $m ""unexpected"" $u]}",,,,,,,,,
command,proc exec_get {delim args} {return [split [exec openssl {*}$args] $delim]},,,,,,,,,
-,,,,,,,,,,
-command,# Test protocols,,,,,,,,,
-Protocols,All,,,lcompare $protocols [::tls::protocols],,,missing {ssl2 ssl3} unexpected {},,,
-,,,,,,,,,,
-command,# Test ciphers,,,,,,,,,
-CiphersAll,SSL2,ssl2,,"lcompare [exec_get "":"" ciphers -ssl2] [::tls::ciphers ssl2]",,,missing {} unexpected {},,,
-CiphersAll,SSL3,ssl3,,"lcompare [exec_get "":"" ciphers -ssl3] [::tls::ciphers ssl3]",,,missing {} unexpected {},,,
-CiphersAll,TLS1,tls1,,"lcompare [exec_get "":"" ciphers -tls1] [::tls::ciphers tls1]",,,missing {} unexpected {},,,
-CiphersAll,TLS1.1,tls1.1,,"lcompare [exec_get "":"" ciphers -tls1_1] [::tls::ciphers tls1.1]",,,missing {} unexpected {},,,
-CiphersAll,TLS1.2,tls1.2,,"lcompare [exec_get "":"" ciphers -tls1_2] [::tls::ciphers tls1.2]",,,missing {} unexpected {},,,
-CiphersAll,TLS1.3,tls1.3,,"lcompare [exec_get "":"" ciphers -tls1_3] [::tls::ciphers tls1.3]",,,missing {} unexpected {},,,
+command,"proc exec_get_ciphers {} {set list [list];set data [exec openssl list -cipher-algorithms];foreach line [split $data ""\n""] {foreach {cipher null alias} [split [string trim $line]] {lappend list [string tolower $cipher]}};return [lsort -unique $list]}",,,,,,,,,
+command,"proc exec_get_digests {} {set list [list];set data [exec openssl dgst -list];foreach line [split $data ""\n""] {foreach digest $line {if {[string match ""-*"" $digest]} {lappend list [string trimleft $digest ""-""]}}};return [lsort $list]}",,,,,,,,,
+command,proc list_tolower {list} {set result [list];foreach element $list {lappend result [string tolower $element]};return $result},,,,,,,,,
+command,proc read_chan {md filename args} {set ch [open $filename rb];fconfigure $ch -translation binary;set new [tls::digest $md {*}$args -chan $ch];while {![eof $new]} {set result [read $new]};close $new;return $result},,,,,,,,,
+,,,,,,,,,,
+command,# Test list ciphers,,,,,,,,,
+Ciphers List,All,,,lcompare [lsort [exec_get_ciphers]] [list_tolower [lsort [::tls::ciphers]]],,,missing {rc5 rc5-cbc rc5-cfb rc5-ecb rc5-ofb} unexpected {aes-128-ccm aes-128-gcm aes-192-ccm aes-192-gcm aes-256-ccm aes-256-gcm},,,
+,,,,,,,,,,
+command,# Test list ciphers for protocols,,,,,,,,,
+Ciphers By Protocol,SSL2,ssl2,,"lcompare [exec_get "":"" ciphers -ssl2] [::tls::ciphers ssl2]",,,missing {} unexpected {},,,
+Ciphers By Protocol,SSL3,ssl3,,"lcompare [exec_get "":"" ciphers -ssl3] [::tls::ciphers ssl3]",,,missing {} unexpected {},,,
+Ciphers By Protocol,TLS1,tls1,,"lcompare [exec_get "":"" ciphers -tls1] [::tls::ciphers tls1]",,,missing {} unexpected {},,,
+Ciphers By Protocol,TLS1.1,tls1.1,,"lcompare [exec_get "":"" ciphers -tls1_1] [::tls::ciphers tls1.1]",,,missing {} unexpected {},,,
+Ciphers By Protocol,TLS1.2,tls1.2,,"lcompare [exec_get "":"" ciphers -tls1_2] [::tls::ciphers tls1.2]",,,missing {} unexpected {},,,
+Ciphers By Protocol,TLS1.3,tls1.3,,"lcompare [exec_get "":"" ciphers -tls1_3] [::tls::ciphers tls1.3]",,,missing {} unexpected {},,,
,,,,,,,,,,
command,# Test cipher descriptions,,,,,,,,,
-CiphersDesc,SSL2,ssl2,,"lcompare [exec_get ""\r\n"" ciphers -ssl2 -v] [split [string trim [::tls::ciphers ssl2 1]] \n]",,,missing {} unexpected {},,,
-CiphersDesc,SSL3,ssl3,,"lcompare [exec_get ""\r\n"" ciphers -ssl3 -v] [split [string trim [::tls::ciphers ssl3 1]] \n]",,,missing {} unexpected {},,,
-CiphersDesc,TLS1,tls1,,"lcompare [exec_get ""\r\n"" ciphers -tls1 -v] [split [string trim [::tls::ciphers tls1 1]] \n]",,,missing {} unexpected {},,,
-CiphersDesc,TLS1.1,tls1.1,,"lcompare [exec_get ""\r\n"" ciphers -tls1_1 -v] [split [string trim [::tls::ciphers tls1.1 1]] \n]",,,missing {} unexpected {},,,
-CiphersDesc,TLS1.2,tls1.2,,"lcompare [exec_get ""\r\n"" ciphers -tls1_2 -v] [split [string trim [::tls::ciphers tls1.2 1]] \n]",,,missing {} unexpected {},,,
-CiphersDesc,TLS1.3,tls1.3,,"lcompare [exec_get ""\r\n"" ciphers -tls1_3 -v] [split [string trim [::tls::ciphers tls1.3 1]] \n]",,,missing {} unexpected {},,,
+Ciphers With Descriptions,SSL2,ssl2,,"lcompare [exec_get ""\r\n"" ciphers -ssl2 -v] [split [string trim [::tls::ciphers ssl2 1]] \n]",,,missing {} unexpected {},,,
+Ciphers With Descriptions,SSL3,ssl3,,"lcompare [exec_get ""\r\n"" ciphers -ssl3 -v] [split [string trim [::tls::ciphers ssl3 1]] \n]",,,missing {} unexpected {},,,
+Ciphers With Descriptions,TLS1,tls1,,"lcompare [exec_get ""\r\n"" ciphers -tls1 -v] [split [string trim [::tls::ciphers tls1 1]] \n]",,,missing {} unexpected {},,,
+Ciphers With Descriptions,TLS1.1,tls1.1,,"lcompare [exec_get ""\r\n"" ciphers -tls1_1 -v] [split [string trim [::tls::ciphers tls1.1 1]] \n]",,,missing {} unexpected {},,,
+Ciphers With Descriptions,TLS1.2,tls1.2,,"lcompare [exec_get ""\r\n"" ciphers -tls1_2 -v] [split [string trim [::tls::ciphers tls1.2 1]] \n]",,,missing {} unexpected {},,,
+Ciphers With Descriptions,TLS1.3,tls1.3,,"lcompare [exec_get ""\r\n"" ciphers -tls1_3 -v] [split [string trim [::tls::ciphers tls1.3 1]] \n]",,,missing {} unexpected {},,,
,,,,,,,,,,
command,# Test protocol specific ciphers,,,,,,,,,
-CiphersSpecific,SSL2,ssl2,,"lcompare [exec_get "":"" ciphers -ssl2 -s] [::tls::ciphers ssl2 0 1]",,,missing {} unexpected {},,,
-CiphersSpecific,SSL3,ssl3,,"lcompare [exec_get "":"" ciphers -ssl3 -s] [::tls::ciphers ssl3 0 1]",,,missing {} unexpected {},,,
-CiphersSpecific,TLS1,tls1,,"lcompare [exec_get "":"" ciphers -tls1 -s] [::tls::ciphers tls1 0 1]",,,missing {} unexpected {},,,
-CiphersSpecific,TLS1.1,tls1.1,,"lcompare [exec_get "":"" ciphers -tls1_1 -s] [::tls::ciphers tls1.1 0 1]",,,missing {} unexpected {},,,
-CiphersSpecific,TLS1.2,tls1.2,,"lcompare [exec_get "":"" ciphers -tls1_2 -s] [::tls::ciphers tls1.2 0 1]",,,missing {} unexpected {},,,
-CiphersSpecific,TLS1.3,tls1.3,,"lcompare [exec_get "":"" ciphers -tls1_3 -s] [::tls::ciphers tls1.3 0 1]",,,missing {} unexpected {},,,
+Ciphers Protocol Specific,SSL2,ssl2,,"lcompare [exec_get "":"" ciphers -ssl2 -s] [::tls::ciphers ssl2 0 1]",,,missing {} unexpected {},,,
+Ciphers Protocol Specific,SSL3,ssl3,,"lcompare [exec_get "":"" ciphers -ssl3 -s] [::tls::ciphers ssl3 0 1]",,,missing {} unexpected {},,,
+Ciphers Protocol Specific,TLS1,tls1,,"lcompare [exec_get "":"" ciphers -tls1 -s] [::tls::ciphers tls1 0 1]",,,missing {} unexpected {},,,
+Ciphers Protocol Specific,TLS1.1,tls1.1,,"lcompare [exec_get "":"" ciphers -tls1_1 -s] [::tls::ciphers tls1.1 0 1]",,,missing {} unexpected {},,,
+Ciphers Protocol Specific,TLS1.2,tls1.2,,"lcompare [exec_get "":"" ciphers -tls1_2 -s] [::tls::ciphers tls1.2 0 1]",,,missing {} unexpected {},,,
+Ciphers Protocol Specific,TLS1.3,tls1.3,,"lcompare [concat [exec_get "":"" ciphers -tls1_3 -s] [exec_get "":"" ciphers -tls1_2 -s]] [::tls::ciphers tls1.3 0 1]",,,missing {} unexpected {},,,
+,,,,,,,,,,
+command,# Test list digests,,,,,,,,,
+Digest List,All,,,lcompare [lsort [exec_get_digests]] [lsort [tls::digests]],,,missing {} unexpected {},,,
+,,,,,,,,,,
+command,# Test digest commands,,,,,,,,,
+Digest Cmds,md4 cmd,,,"tls::md4 ""Example string for message digest tests.""",,,181CDCF9DB9B6FA8FC0A3BF9C34E29D9,,,
+Digest Cmds,md5 cmd,,,"tls::md5 ""Example string for message digest tests.""",,,CCB1BE2E11D8183E843FF73DA8C6D206,,,
+Digest Cmds,sha1 cmd,,,"tls::sha1 ""Example string for message digest tests.""",,,3AEFE840CA492C387E903F15ED6019E7AD833B47,,,
+Digest Cmds,sha256 cmd,,,"tls::sha256 ""Example string for message digest tests.""",,,B7DFDDEB0314A74FF56A8AC1E3DC57DF09BB52A96DA50F6549EB62CA61A0A491,,,
+,,,,,,,,,,
+command,# Test digest command for data,,,,,,,,,
+Digest Data,md4,,,"tls::digest md4 ""Example string for message digest tests.""",,,181CDCF9DB9B6FA8FC0A3BF9C34E29D9,,,
+Digest Data,md5,,,"tls::digest md5 ""Example string for message digest tests.""",,,CCB1BE2E11D8183E843FF73DA8C6D206,,,
+Digest Data,sha1,,,"tls::digest sha1 ""Example string for message digest tests.""",,,3AEFE840CA492C387E903F15ED6019E7AD833B47,,,
+Digest Data,sha256,,,"tls::digest sha256 ""Example string for message digest tests.""",,,B7DFDDEB0314A74FF56A8AC1E3DC57DF09BB52A96DA50F6549EB62CA61A0A491,,,
+Digest Data,md5 bin,,,"string toupper [binary encode hex [tls::digest md5 -bin ""Example string for message digest tests.""]]",,,CCB1BE2E11D8183E843FF73DA8C6D206,,,
+Digest Data,md5 hex,,,"tls::digest md5 -hex ""Example string for message digest tests.""",,,CCB1BE2E11D8183E843FF73DA8C6D206,,,
+Digest Data,md5 with arg,,,"tls::digest md5 -data ""Example string for message digest tests.""",,,CCB1BE2E11D8183E843FF73DA8C6D206,,,
+,,,,,,,,,,
+command,# Test digest command for files,,,,,,,,,
+Digest File,md4,,,tls::digest md4 -file md_data.dat,,,181CDCF9DB9B6FA8FC0A3BF9C34E29D9,,,
+Digest File,md5,,,tls::digest md5 -file md_data.dat,,,CCB1BE2E11D8183E843FF73DA8C6D206,,,
+Digest File,sha1,,,tls::digest sha1 -file md_data.dat,,,3AEFE840CA492C387E903F15ED6019E7AD833B47,,,
+Digest File,sha256,,,tls::digest sha256 -file md_data.dat,,,B7DFDDEB0314A74FF56A8AC1E3DC57DF09BB52A96DA50F6549EB62CA61A0A491,,,
+Digest File,md5 bin,,,string toupper [binary encode hex [tls::digest md5 -bin -file md_data.dat]],,,CCB1BE2E11D8183E843FF73DA8C6D206,,,
+Digest File,md5 hex,,,tls::digest md5 -hex -file md_data.dat,,,CCB1BE2E11D8183E843FF73DA8C6D206,,,
+,,,,,,,,,,
+command,# Test digest command for channel,,,,,,,,,
+Digest Chan,md4,,,read_chan md4 md_data.dat,,,181CDCF9DB9B6FA8FC0A3BF9C34E29D9,,,
+Digest Chan,md5,,,read_chan md5 md_data.dat,,,CCB1BE2E11D8183E843FF73DA8C6D206,,,
+Digest Chan,sha1,,,read_chan sha1 md_data.dat,,,3AEFE840CA492C387E903F15ED6019E7AD833B47,,,
+Digest Chan,sha256,,,read_chan sha256 md_data.dat,,,B7DFDDEB0314A74FF56A8AC1E3DC57DF09BB52A96DA50F6549EB62CA61A0A491,,,
+Digest Chan,md5 bin,,,string toupper [binary encode hex [read_chan md5 md_data.dat -bin]],,,CCB1BE2E11D8183E843FF73DA8C6D206,,,
+Digest Chan,md5 hex,,,read_chan md5 md_data.dat -hex,,,CCB1BE2E11D8183E843FF73DA8C6D206,,,
+,,,,,,,,,,
+command,# Test list protocols,,,,,,,,,
+Protocols,All,,,lcompare $protocols [::tls::protocols],,,missing {ssl2 ssl3} unexpected {},,,
,,,,,,,,,,
-command,# Test version,,,,,,,,,
+command,# Test show version,,,,,,,,,
Version,All,,,::tls::version,,glob,*,,,
Version,OpenSSL,OpenSSL,,::tls::version,,glob,OpenSSL*,,,
Index: tests/ciphers.test
==================================================================
--- tests/ciphers.test
+++ tests/ciphers.test
@@ -1,6 +1,6 @@
-# Auto generated test cases for ciphers_and_protocols.csv
+# Auto generated test cases for ciphers.csv
# Load Tcl Test package
if {[lsearch [namespace children] ::tcltest] == -1} {
package require tcltest
namespace import ::tcltest::*
@@ -9,113 +9,229 @@
set auto_path [concat [list [file dirname [file dirname [info script]]]] $auto_path]
package require tls
# Make sure path includes location of OpenSSL executable
-if {[info exists ::env(OPENSSL)]} {set ::env(path) [string cat [file join $::env(OPENSSL) bin] ";" $::env(path)}
+if {[info exists ::env(OPENSSL)]} {set ::env(path) [string cat [file join $::env(OPENSSL) bin] ";" $::env(path)]}
# Constraints
set protocols [list ssl2 ssl3 tls1 tls1.1 tls1.2 tls1.3]
foreach protocol $protocols {::tcltest::testConstraint $protocol 0}
foreach protocol [::tls::protocols] {::tcltest::testConstraint $protocol 1}
::tcltest::testConstraint OpenSSL [string match "OpenSSL*" [::tls::version]]
# Helper functions
proc lcompare {list1 list2} {set m "";set u "";foreach i $list1 {if {$i ni $list2} {lappend m $i}};foreach i $list2 {if {$i ni $list1} {lappend u $i}};return [list "missing" $m "unexpected" $u]}
proc exec_get {delim args} {return [split [exec openssl {*}$args] $delim]}
-# Test protocols
+proc exec_get_ciphers {} {set list [list];set data [exec openssl list -cipher-algorithms];foreach line [split $data "\n"] {foreach {cipher null alias} [split [string trim $line]] {lappend list [string tolower $cipher]}};return [lsort -unique $list]}
+proc exec_get_digests {} {set list [list];set data [exec openssl dgst -list];foreach line [split $data "\n"] {foreach digest $line {if {[string match "-*" $digest]} {lappend list [string trimleft $digest "-"]}}};return [lsort $list]}
+proc list_tolower {list} {set result [list];foreach element $list {lappend result [string tolower $element]};return $result}
+proc read_chan {md filename args} {set ch [open $filename rb];fconfigure $ch -translation binary;set new [tls::digest $md {*}$args -chan $ch];while {![eof $new]} {set result [read $new]};close $new;return $result}
+# Test list ciphers
-test Protocols-1.1 {All} -body {
- lcompare $protocols [::tls::protocols]
- } -result {missing {ssl2 ssl3} unexpected {}}
-# Test ciphers
+test Ciphers_List-1.1 {All} -body {
+ lcompare [lsort [exec_get_ciphers]] [list_tolower [lsort [::tls::ciphers]]]
+ } -result {missing {rc5 rc5-cbc rc5-cfb rc5-ecb rc5-ofb} unexpected {aes-128-ccm aes-128-gcm aes-192-ccm aes-192-gcm aes-256-ccm aes-256-gcm}}
+# Test list ciphers for protocols
-test CiphersAll-2.1 {SSL2} -constraints {ssl2} -body {
+test Ciphers_By_Protocol-2.1 {SSL2} -constraints {ssl2} -body {
lcompare [exec_get ":" ciphers -ssl2] [::tls::ciphers ssl2]
} -result {missing {} unexpected {}}
-test CiphersAll-2.2 {SSL3} -constraints {ssl3} -body {
+test Ciphers_By_Protocol-2.2 {SSL3} -constraints {ssl3} -body {
lcompare [exec_get ":" ciphers -ssl3] [::tls::ciphers ssl3]
} -result {missing {} unexpected {}}
-test CiphersAll-2.3 {TLS1} -constraints {tls1} -body {
+test Ciphers_By_Protocol-2.3 {TLS1} -constraints {tls1} -body {
lcompare [exec_get ":" ciphers -tls1] [::tls::ciphers tls1]
} -result {missing {} unexpected {}}
-test CiphersAll-2.4 {TLS1.1} -constraints {tls1.1} -body {
+test Ciphers_By_Protocol-2.4 {TLS1.1} -constraints {tls1.1} -body {
lcompare [exec_get ":" ciphers -tls1_1] [::tls::ciphers tls1.1]
} -result {missing {} unexpected {}}
-test CiphersAll-2.5 {TLS1.2} -constraints {tls1.2} -body {
+test Ciphers_By_Protocol-2.5 {TLS1.2} -constraints {tls1.2} -body {
lcompare [exec_get ":" ciphers -tls1_2] [::tls::ciphers tls1.2]
} -result {missing {} unexpected {}}
-test CiphersAll-2.6 {TLS1.3} -constraints {tls1.3} -body {
+test Ciphers_By_Protocol-2.6 {TLS1.3} -constraints {tls1.3} -body {
lcompare [exec_get ":" ciphers -tls1_3] [::tls::ciphers tls1.3]
} -result {missing {} unexpected {}}
# Test cipher descriptions
-test CiphersDesc-3.1 {SSL2} -constraints {ssl2} -body {
+test Ciphers_With_Descriptions-3.1 {SSL2} -constraints {ssl2} -body {
lcompare [exec_get "\r\n" ciphers -ssl2 -v] [split [string trim [::tls::ciphers ssl2 1]] \n]
} -result {missing {} unexpected {}}
-test CiphersDesc-3.2 {SSL3} -constraints {ssl3} -body {
+test Ciphers_With_Descriptions-3.2 {SSL3} -constraints {ssl3} -body {
lcompare [exec_get "\r\n" ciphers -ssl3 -v] [split [string trim [::tls::ciphers ssl3 1]] \n]
} -result {missing {} unexpected {}}
-test CiphersDesc-3.3 {TLS1} -constraints {tls1} -body {
+test Ciphers_With_Descriptions-3.3 {TLS1} -constraints {tls1} -body {
lcompare [exec_get "\r\n" ciphers -tls1 -v] [split [string trim [::tls::ciphers tls1 1]] \n]
} -result {missing {} unexpected {}}
-test CiphersDesc-3.4 {TLS1.1} -constraints {tls1.1} -body {
+test Ciphers_With_Descriptions-3.4 {TLS1.1} -constraints {tls1.1} -body {
lcompare [exec_get "\r\n" ciphers -tls1_1 -v] [split [string trim [::tls::ciphers tls1.1 1]] \n]
} -result {missing {} unexpected {}}
-test CiphersDesc-3.5 {TLS1.2} -constraints {tls1.2} -body {
+test Ciphers_With_Descriptions-3.5 {TLS1.2} -constraints {tls1.2} -body {
lcompare [exec_get "\r\n" ciphers -tls1_2 -v] [split [string trim [::tls::ciphers tls1.2 1]] \n]
} -result {missing {} unexpected {}}
-test CiphersDesc-3.6 {TLS1.3} -constraints {tls1.3} -body {
+test Ciphers_With_Descriptions-3.6 {TLS1.3} -constraints {tls1.3} -body {
lcompare [exec_get "\r\n" ciphers -tls1_3 -v] [split [string trim [::tls::ciphers tls1.3 1]] \n]
} -result {missing {} unexpected {}}
# Test protocol specific ciphers
-test CiphersSpecific-4.1 {SSL2} -constraints {ssl2} -body {
+test Ciphers_Protocol_Specific-4.1 {SSL2} -constraints {ssl2} -body {
lcompare [exec_get ":" ciphers -ssl2 -s] [::tls::ciphers ssl2 0 1]
} -result {missing {} unexpected {}}
-test CiphersSpecific-4.2 {SSL3} -constraints {ssl3} -body {
+test Ciphers_Protocol_Specific-4.2 {SSL3} -constraints {ssl3} -body {
lcompare [exec_get ":" ciphers -ssl3 -s] [::tls::ciphers ssl3 0 1]
} -result {missing {} unexpected {}}
-test CiphersSpecific-4.3 {TLS1} -constraints {tls1} -body {
+test Ciphers_Protocol_Specific-4.3 {TLS1} -constraints {tls1} -body {
lcompare [exec_get ":" ciphers -tls1 -s] [::tls::ciphers tls1 0 1]
} -result {missing {} unexpected {}}
-test CiphersSpecific-4.4 {TLS1.1} -constraints {tls1.1} -body {
+test Ciphers_Protocol_Specific-4.4 {TLS1.1} -constraints {tls1.1} -body {
lcompare [exec_get ":" ciphers -tls1_1 -s] [::tls::ciphers tls1.1 0 1]
} -result {missing {} unexpected {}}
-test CiphersSpecific-4.5 {TLS1.2} -constraints {tls1.2} -body {
+test Ciphers_Protocol_Specific-4.5 {TLS1.2} -constraints {tls1.2} -body {
lcompare [exec_get ":" ciphers -tls1_2 -s] [::tls::ciphers tls1.2 0 1]
} -result {missing {} unexpected {}}
-test CiphersSpecific-4.6 {TLS1.3} -constraints {tls1.3} -body {
- lcompare [exec_get ":" ciphers -tls1_3 -s] [::tls::ciphers tls1.3 0 1]
+test Ciphers_Protocol_Specific-4.6 {TLS1.3} -constraints {tls1.3} -body {
+ lcompare [concat [exec_get ":" ciphers -tls1_3 -s] [exec_get ":" ciphers -tls1_2 -s]] [::tls::ciphers tls1.3 0 1]
+ } -result {missing {} unexpected {}}
+# Test list digests
+
+
+test Digest_List-5.1 {All} -body {
+ lcompare [lsort [exec_get_digests]] [lsort [tls::digests]]
} -result {missing {} unexpected {}}
-# Test version
+# Test digest commands
+
+
+test Digest_Cmds-6.1 {md4 cmd} -body {
+ tls::md4 "Example string for message digest tests."
+ } -result {181CDCF9DB9B6FA8FC0A3BF9C34E29D9}
+
+test Digest_Cmds-6.2 {md5 cmd} -body {
+ tls::md5 "Example string for message digest tests."
+ } -result {CCB1BE2E11D8183E843FF73DA8C6D206}
+
+test Digest_Cmds-6.3 {sha1 cmd} -body {
+ tls::sha1 "Example string for message digest tests."
+ } -result {3AEFE840CA492C387E903F15ED6019E7AD833B47}
+
+test Digest_Cmds-6.4 {sha256 cmd} -body {
+ tls::sha256 "Example string for message digest tests."
+ } -result {B7DFDDEB0314A74FF56A8AC1E3DC57DF09BB52A96DA50F6549EB62CA61A0A491}
+# Test digest command for data
+
+
+test Digest_Data-7.1 {md4} -body {
+ tls::digest md4 "Example string for message digest tests."
+ } -result {181CDCF9DB9B6FA8FC0A3BF9C34E29D9}
+
+test Digest_Data-7.2 {md5} -body {
+ tls::digest md5 "Example string for message digest tests."
+ } -result {CCB1BE2E11D8183E843FF73DA8C6D206}
+
+test Digest_Data-7.3 {sha1} -body {
+ tls::digest sha1 "Example string for message digest tests."
+ } -result {3AEFE840CA492C387E903F15ED6019E7AD833B47}
+
+test Digest_Data-7.4 {sha256} -body {
+ tls::digest sha256 "Example string for message digest tests."
+ } -result {B7DFDDEB0314A74FF56A8AC1E3DC57DF09BB52A96DA50F6549EB62CA61A0A491}
+
+test Digest_Data-7.5 {md5 bin} -body {
+ string toupper [binary encode hex [tls::digest md5 -bin "Example string for message digest tests."]]
+ } -result {CCB1BE2E11D8183E843FF73DA8C6D206}
+
+test Digest_Data-7.6 {md5 hex} -body {
+ tls::digest md5 -hex "Example string for message digest tests."
+ } -result {CCB1BE2E11D8183E843FF73DA8C6D206}
+
+test Digest_Data-7.7 {md5 with arg} -body {
+ tls::digest md5 -data "Example string for message digest tests."
+ } -result {CCB1BE2E11D8183E843FF73DA8C6D206}
+# Test digest command for files
+
+
+test Digest_File-8.1 {md4} -body {
+ tls::digest md4 -file md_data.dat
+ } -result {181CDCF9DB9B6FA8FC0A3BF9C34E29D9}
+
+test Digest_File-8.2 {md5} -body {
+ tls::digest md5 -file md_data.dat
+ } -result {CCB1BE2E11D8183E843FF73DA8C6D206}
+
+test Digest_File-8.3 {sha1} -body {
+ tls::digest sha1 -file md_data.dat
+ } -result {3AEFE840CA492C387E903F15ED6019E7AD833B47}
+
+test Digest_File-8.4 {sha256} -body {
+ tls::digest sha256 -file md_data.dat
+ } -result {B7DFDDEB0314A74FF56A8AC1E3DC57DF09BB52A96DA50F6549EB62CA61A0A491}
+
+test Digest_File-8.5 {md5 bin} -body {
+ string toupper [binary encode hex [tls::digest md5 -bin -file md_data.dat]]
+ } -result {CCB1BE2E11D8183E843FF73DA8C6D206}
+
+test Digest_File-8.6 {md5 hex} -body {
+ tls::digest md5 -hex -file md_data.dat
+ } -result {CCB1BE2E11D8183E843FF73DA8C6D206}
+# Test digest command for channel
+
+
+test Digest_Chan-9.1 {md4} -body {
+ read_chan md4 md_data.dat
+ } -result {181CDCF9DB9B6FA8FC0A3BF9C34E29D9}
+
+test Digest_Chan-9.2 {md5} -body {
+ read_chan md5 md_data.dat
+ } -result {CCB1BE2E11D8183E843FF73DA8C6D206}
+
+test Digest_Chan-9.3 {sha1} -body {
+ read_chan sha1 md_data.dat
+ } -result {3AEFE840CA492C387E903F15ED6019E7AD833B47}
+
+test Digest_Chan-9.4 {sha256} -body {
+ read_chan sha256 md_data.dat
+ } -result {B7DFDDEB0314A74FF56A8AC1E3DC57DF09BB52A96DA50F6549EB62CA61A0A491}
+
+test Digest_Chan-9.5 {md5 bin} -body {
+ string toupper [binary encode hex [read_chan md5 md_data.dat -bin]]
+ } -result {CCB1BE2E11D8183E843FF73DA8C6D206}
+
+test Digest_Chan-9.6 {md5 hex} -body {
+ read_chan md5 md_data.dat -hex
+ } -result {CCB1BE2E11D8183E843FF73DA8C6D206}
+# Test list protocols
+
+
+test Protocols-10.1 {All} -body {
+ lcompare $protocols [::tls::protocols]
+ } -result {missing {ssl2 ssl3} unexpected {}}
+# Test show version
-test Version-5.1 {All} -body {
+test Version-11.1 {All} -body {
::tls::version
} -match {glob} -result {*}
-test Version-5.2 {OpenSSL} -constraints {OpenSSL} -body {
+test Version-11.2 {OpenSSL} -constraints {OpenSSL} -body {
::tls::version
} -match {glob} -result {OpenSSL*}
# Cleanup
::tcltest::cleanupTests
return
ADDED tests/md_data.dat
Index: tests/md_data.dat
==================================================================
--- /dev/null
+++ tests/md_data.dat
@@ -0,0 +1,1 @@
+Example string for message digest tests.
Index: win/makefile.vc
==================================================================
--- win/makefile.vc
+++ win/makefile.vc
@@ -25,20 +25,22 @@
# Note the resource file does not makes sense if doing a static library build
# hence it is under that condition. TMP_DIR is the output directory
# defined by rules for object files.
PRJ_OBJS = $(TMP_DIR)\tls.obj \
$(TMP_DIR)\tlsBIO.obj \
+ $(TMP_DIR)\tlsDigest.obj \
$(TMP_DIR)\tlsIO.obj \
$(TMP_DIR)\tlsX509.obj
# Define any additional project include flags
# SSL_INSTALL_FOLDER = with the OpenSSL installation folder following.
PRJ_INCLUDES = -I"$(SSL_INSTALL_FOLDER)\include" -I"$(OPENSSL_INSTALL_DIR)\include"
# Define any additional compiler flags that might be required for the project
PRJ_DEFINES = -D NO_SSL2 -D NO_SSL3 -D _CRT_SECURE_NO_WARNINGS
-
+
+#
# SSL Libs:
# 1. ${LIBCRYPTO}.dll
# 2. ${LIBSSL}.dll
# Where LIBCRYPTO (#1.) and LIBSSL (#2.) are defined as follows:
# v1.1: libcrypto-1.1-x64.dll and libssl-1.1-x64.dll
@@ -53,10 +55,14 @@
# Define the standard targets
!include "targets.vc"
# Project specific targets
+all:
+
+clean: default-clean
+
# We must define a pkgindex target that will create a pkgIndex.tcl
# file in the $(OUT_DIR) directory. We can just redirect to the
# default-pkgindex target for our sample extension.
pkgindex: default-pkgindex