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 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.

SYNOPSIS

-

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 data
+tls::digests
+tls::md4 data
+tls::md5 data
+tls::sha1 data
+tls::sha256 data

DESCRIPTION

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 supportted 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,33 @@ and tls1.3. Exact list depends on OpenSSL version and compile time flags.
tls::version
Returns the OpenSSL version string.
+ +
+
tls::digest type data
+
Calculate the message digest for data using type + hash algorithm. Returns value as a hex string. Type can be any + OpenSSL supported hash algorithm including: md4, md5, + sha1, sha256, sha512, sha3-256, etc. + See digests command for a full list.
+ +
tls::digests
+
Returns a list of the valid hash algorithms used to create message digests.
+ +
tls::md4 data
+
Return the MD4 message-digest for data.
+ +
tls::md5 data
+
Return the MD5 message-digest for data.
+ +
tls::sha1 data
+
Return the SHA1 secure hash algorithm digest for data.
+ +
tls::sha256 data
+
Return the SHA-2 SHA256 secure hash algorithm digest for data.

CALLBACK OPTIONS

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) \ + if (strcmp(opt, (option)) == 0) { \ + var = 1; \ + 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() 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,163 @@ +/* + * Digest Commands + * + * Copyright (C) 2023 Brian O'Hagan + * + */ + +#include "tlsInt.h" +#include +#include +#include +#include + +/* Constants */ +const char *hex = "0123456789ABCDEF"; + + +/*******************************************************************/ + +/* + *------------------------------------------------------------------- + * + * Hash Calc -- + * + * Calculate message digest of data using type hash algorithm. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * None. + * + *------------------------------------------------------------------- + */ +int +HashCalc(Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], const EVP_MD *type) { + char *data; + int len; + unsigned int mdlen; + unsigned char mdbuf[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 hash value, create hex representation, and write to result */ + if (EVP_Digest(data, (size_t) len, mdbuf, &mdlen, type, NULL)) { + Tcl_Obj *resultObj; + unsigned char *ptr; + resultObj = Tcl_NewObj(); + ptr = Tcl_SetByteArrayLength(resultObj, mdlen*2); + + for (unsigned int i = 0; i < mdlen; i++) { + *ptr++ = hex[(mdbuf[i] >> 4) & 0x0F]; + *ptr++ = hex[mdbuf[i] & 0x0F]; + } + Tcl_SetObjResult(interp, resultObj); + } else { + Tcl_SetResult(interp, "Hash calculation error", NULL); + return TCL_ERROR; + } + return TCL_OK; +} + +/* + *------------------------------------------------------------------- + * + * Hash Commands -- + * + * Return the digest as a hex string for data using type message digest. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * None. + * + *------------------------------------------------------------------- + */ +DigestObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { + int len; + const char *name; + const EVP_MD *type; + + if (objc != 3) { + Tcl_WrongNumArgs(interp, 1, objv, "type data"); + return TCL_ERROR; + } + + name = Tcl_GetStringFromObj(objv[1],&len); + if (name == NULL || (type = EVP_get_digestbyname(name)) == NULL) { + Tcl_AppendResult(interp, "Invalid digest type \"", name, "\"", NULL); + return TCL_ERROR; + } + objc--; + objv++; + return HashCalc(interp, objc, objv, type); +} + +/* + * Command to Calculate MD4 Message Digest + */ +int +DigestMD4Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { + return HashCalc(interp, objc, objv, EVP_md4()); +} + +/* + * Command to Calculate MD5 Message Digest + */ +int +DigestMD5Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { + return HashCalc(interp, objc, objv, EVP_md5()); +} + +/* + * Command to Calculate SHA-1 Hash + */ +int +DigestSHA1Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { + return HashCalc(interp, objc, objv, EVP_sha1()); +} + +/* + * Command to Calculate SHA2 SHA-256 Hash + */ +int +DigestSHA256Cmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { + return HashCalc(interp, objc, objv, EVP_sha256()); +} + +/* + *------------------------------------------------------------------- + * + * 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,55 @@ 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_toupper {list} {set result [list];foreach element $list {lappend result [string toupper $element]};return $result},,,,,,,,, +,,,,,,,,,, +command,# Test list ciphers,,,,,,,,, +CiphersAll,,,,lcompare [lsort [exec_get_ciphers]] [lsort [list_toupper [::tls::ciphers]]],,,missing {} unexpected {},,, +,,,,,,,,,, +command,# Test list ciphers for protocols,,,,,,,,, +CiphersProtocols,SSL2,ssl2,,"lcompare [exec_get "":"" ciphers -ssl2] [::tls::ciphers ssl2]",,,missing {} unexpected {},,, +CiphersProtocols,SSL3,ssl3,,"lcompare [exec_get "":"" ciphers -ssl3] [::tls::ciphers ssl3]",,,missing {} unexpected {},,, +CiphersProtocols,TLS1,tls1,,"lcompare [exec_get "":"" ciphers -tls1] [::tls::ciphers tls1]",,,missing {} unexpected {},,, +CiphersProtocols,TLS1.1,tls1.1,,"lcompare [exec_get "":"" ciphers -tls1_1] [::tls::ciphers tls1.1]",,,missing {} unexpected {},,, +CiphersProtocols,TLS1.2,tls1.2,,"lcompare [exec_get "":"" ciphers -tls1_2] [::tls::ciphers tls1.2]",,,missing {} unexpected {},,, +CiphersProtocols,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 {},,, +CiphersDescriptions,SSL2,ssl2,,"lcompare [exec_get ""\r\n"" ciphers -ssl2 -v] [split [string trim [::tls::ciphers ssl2 1]] \n]",,,missing {} unexpected {},,, +CiphersDescriptions,SSL3,ssl3,,"lcompare [exec_get ""\r\n"" ciphers -ssl3 -v] [split [string trim [::tls::ciphers ssl3 1]] \n]",,,missing {} unexpected {},,, +CiphersDescriptions,TLS1,tls1,,"lcompare [exec_get ""\r\n"" ciphers -tls1 -v] [split [string trim [::tls::ciphers tls1 1]] \n]",,,missing {} unexpected {},,, +CiphersDescriptions,TLS1.1,tls1.1,,"lcompare [exec_get ""\r\n"" ciphers -tls1_1 -v] [split [string trim [::tls::ciphers tls1.1 1]] \n]",,,missing {} unexpected {},,, +CiphersDescriptions,TLS1.2,tls1.2,,"lcompare [exec_get ""\r\n"" ciphers -tls1_2 -v] [split [string trim [::tls::ciphers tls1.2 1]] \n]",,,missing {} unexpected {},,, +CiphersDescriptions,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 {},,, ,,,,,,,,,, +command,# Test list digests,,,,,,,,, +Digest List,Digest List,,,lcompare [lsort [exec_get_digests]] [lsort [tls::digests]],,,missing {} unexpected {},,, +,,,,,,,,,, +command,# Test Digests,,,,,,,,, +Digest,md4 opt,,,"tls::md4 ""Example string for message digest tests.""",,,181CDCF9DB9B6FA8FC0A3BF9C34E29D9,,, +Digest,md5 opt,,,"tls::md5 ""Example string for message digest tests.""",,,CCB1BE2E11D8183E843FF73DA8C6D206,,, +Digest,sha1 opt,,,"tls::sha1 ""Example string for message digest tests.""",,,3AEFE840CA492C387E903F15ED6019E7AD833B47,,, +Digest,sha256 opt,,,"tls::sha256 ""Example string for message digest tests.""",,,B7DFDDEB0314A74FF56A8AC1E3DC57DF09BB52A96DA50F6549EB62CA61A0A491,,, +Digest,md4,,,"tls::digest md4 ""Example string for message digest tests.""",,,181CDCF9DB9B6FA8FC0A3BF9C34E29D9,,, +Digest,md5,,,"tls::digest md5 ""Example string for message digest tests.""",,,CCB1BE2E11D8183E843FF73DA8C6D206,,, +Digest,sha1,,,"tls::digest sha1 ""Example string for message digest tests.""",,,3AEFE840CA492C387E903F15ED6019E7AD833B47,,, +Digest,sha256,,,"tls::digest sha256 ""Example string for message digest tests.""",,,B7DFDDEB0314A74FF56A8AC1E3DC57DF09BB52A96DA50F6549EB62CA61A0A491,,, +,,,,,,,,,, +command,# Test protocols,,,,,,,,, +Protocols,All,,,lcompare $protocols [::tls::protocols],,,missing {ssl2 ssl3} unexpected {},,, +,,,,,,,,,, command,# Test 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,76 +9,79 @@ 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_toupper {list} {set result [list];foreach element $list {lappend result [string toupper $element]};return $result} +# Test list ciphers -test Protocols-1.1 {All} -body { - lcompare $protocols [::tls::protocols] - } -result {missing {ssl2 ssl3} unexpected {}} -# Test ciphers +test CiphersAll-1.1 {} -body { + lcompare [lsort [exec_get_ciphers]] [lsort [list_toupper [::tls::ciphers]]] + } -result {missing {} unexpected {}} +# Test list ciphers for protocols -test CiphersAll-2.1 {SSL2} -constraints {ssl2} -body { +test CiphersProtocols-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 CiphersProtocols-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 CiphersProtocols-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 CiphersProtocols-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 CiphersProtocols-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 CiphersProtocols-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 CiphersDescriptions-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 CiphersDescriptions-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 CiphersDescriptions-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 CiphersDescriptions-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 CiphersDescriptions-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 CiphersDescriptions-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 @@ -103,19 +106,65 @@ } -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] } -result {missing {} unexpected {}} +# Test list digests + + +test Digest_List-5.1 {Digest List} -body { + lcompare [lsort [exec_get_digests]] [lsort [tls::digests]] + } -result {missing {} unexpected {}} +# Test Digests + + +test Digest-6.1 {md4 opt} -body { + tls::md4 "Example string for message digest tests." + } -result {181CDCF9DB9B6FA8FC0A3BF9C34E29D9} + +test Digest-6.2 {md5 opt} -body { + tls::md5 "Example string for message digest tests." + } -result {CCB1BE2E11D8183E843FF73DA8C6D206} + +test Digest-6.3 {sha1 opt} -body { + tls::sha1 "Example string for message digest tests." + } -result {3AEFE840CA492C387E903F15ED6019E7AD833B47} + +test Digest-6.4 {sha256 opt} -body { + tls::sha256 "Example string for message digest tests." + } -result {B7DFDDEB0314A74FF56A8AC1E3DC57DF09BB52A96DA50F6549EB62CA61A0A491} + +test Digest-6.5 {md4} -body { + tls::digest md4 "Example string for message digest tests." + } -result {181CDCF9DB9B6FA8FC0A3BF9C34E29D9} + +test Digest-6.6 {md5} -body { + tls::digest md5 "Example string for message digest tests." + } -result {CCB1BE2E11D8183E843FF73DA8C6D206} + +test Digest-6.7 {sha1} -body { + tls::digest sha1 "Example string for message digest tests." + } -result {3AEFE840CA492C387E903F15ED6019E7AD833B47} + +test Digest-6.8 {sha256} -body { + tls::digest sha256 "Example string for message digest tests." + } -result {B7DFDDEB0314A74FF56A8AC1E3DC57DF09BB52A96DA50F6549EB62CA61A0A491} +# Test protocols + + +test Protocols-7.1 {All} -body { + lcompare $protocols [::tls::protocols] + } -result {missing {ssl2 ssl3} unexpected {}} # Test version -test Version-5.1 {All} -body { +test Version-8.1 {All} -body { ::tls::version } -match {glob} -result {*} -test Version-5.2 {OpenSSL} -constraints {OpenSSL} -body { +test Version-8.2 {OpenSSL} -constraints {OpenSSL} -body { ::tls::version } -match {glob} -result {OpenSSL*} # Cleanup ::tcltest::cleanupTests return 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