Bug Summary

File:d/tclcrypto.c
Warning:line 645, column 5
Duplicate code detected
Note:line 986, column 5
Similar code here

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name tclcrypto.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/home/isvv/naviserver/nsd -resource-dir /usr/local/lib/clang/15.0.0 -D _FORTIFY_SOURCE=2 -D NDEBUG -D SYSTEM_MALLOC -I ../include -I /usr/include/tcl8.6 -D HAVE_CONFIG_H -internal-isystem /usr/local/lib/clang/15.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/11/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -std=c99 -fdebug-compilation-dir=/home/isvv/naviserver/nsd -ferror-limit 19 -stack-protector 2 -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-checker alpha -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-07-23-130959-11103-1 -x c tclcrypto.c
1/*
2 * The contents of this file are subject to the Mozilla Public License
3 * Version 1.1 (the "License"); you may not use this file except in
4 * compliance with the License. You may obtain a copy of the License at
5 * http://mozilla.org/.
6 *
7 * Software distributed under the License is distributed on an "AS IS"
8 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
9 * the License for the specific language governing rights and limitations
10 * under the License.
11 *
12 * The Original Code is AOLserver Code and related documentation
13 * distributed by AOL.
14 *
15 * The Initial Developer of the Original Code is America Online,
16 * Inc. Portions created by AOL are Copyright (C) 1999 America Online,
17 * Inc. All Rights Reserved.
18 *
19 * Alternatively, the contents of this file may be used under the terms
20 * of the GNU General Public License (the "GPL"), in which case the
21 * provisions of GPL are applicable instead of those above. If you wish
22 * to allow use of your version of this file only under the terms of the
23 * GPL and not to allow others to use your version of this file under the
24 * License, indicate your decision by deleting the provisions above and
25 * replace them with the notice and other provisions required by the GPL.
26 * If you do not delete the provisions above, a recipient may use your
27 * version of this file under either the License or the GPL.
28 */
29
30/*
31 * tclcrypto.c --
32 *
33 * Function callable from Tcl to use OpenSSL crypto support
34 */
35
36/*
37 * We define for the time being that we want to use an API compatible
38 * with OpenSSL 1.1.0. OpenSSL defines two versions, a hex version
39 *
40 * #define OPENSSL_API_COMPAT 0x10100000L
41 *
42 * and a decimal variant, which should be apparently used in versions
43 * beyond OpenSSL 1.1.x.
44 */
45# define OPENSSL_API_COMPAT10000 10000
46
47#include "nsd.h"
48
49#ifdef HAVE_OPENSSL_EVP_H1
50
51# include "nsopenssl.h"
52#endif
53
54/*
55 * We need OpenSSL least in version 1.0 or newer for the crypto
56 * functions.
57 */
58#if defined(HAVE_OPENSSL_EVP_H1) && !defined(HAVE_OPENSSL_PRE_1_0)
59
60#include <openssl/err.h>
61#include <openssl/evp.h>
62#include <openssl/rand.h>
63
64# ifdef HAVE_OPENSSL_HKDF
65# include <openssl/kdf.h>
66# endif
67
68/*
69 * The following result encodings can be used
70 */
71typedef enum {
72 RESULT_ENCODING_HEX = 1,
73 RESULT_ENCODING_BASE64URL = 2,
74 RESULT_ENCODING_BASE64 = 3,
75 RESULT_ENCODING_BINARY = 4
76} Ns_BinaryEncoding;
77
78static Ns_ObjvTable binaryencodings[] = {
79 {"hex", RESULT_ENCODING_HEX},
80 {"base64url",RESULT_ENCODING_BASE64URL},
81 {"base64", RESULT_ENCODING_BASE64},
82 {"binary", RESULT_ENCODING_BINARY},
83 {NULL((void*)0), 0u}
84};
85
86
87/*
88 * Static functions defined in this file.
89 */
90static Tcl_Obj *EncodedObj(
91 unsigned char *octects,
92 size_t octectLength,
93 char *outputBuffer,
94 Ns_BinaryEncoding encoding
95) NS_GNUC_RETURNS_NONNULL NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
96
97static int GetDigest(Tcl_Interp *interp, const char *digestName, const EVP_MD **mdPtr)
98 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3)));
99
100# ifndef OPENSSL_NO_EC
101static int GetCurve(Tcl_Interp *interp, const char *curveName, int *nidPtr)
102 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3)));
103
104static void
105SetResultFromEC_POINT(
106 Tcl_Interp *interp,
107 Tcl_DString *dsPtr,
108 EC_KEY *eckey,
109 const EC_POINT *ecpoint,
110 BN_CTX *bn_ctx,
111 Ns_BinaryEncoding encoding)
112 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3))) NS_GNUC_NONNULL(4)__attribute__((__nonnull__(4))) NS_GNUC_NONNULL(5)__attribute__((__nonnull__(5)));
113# endif /* OPENSSL_NO_EC */
114
115static int GetCipher(
116 Tcl_Interp *interp, const char *cipherName, unsigned long flags,
117 const char *modeMsg, const EVP_CIPHER **cipherPtr
118) NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))) NS_GNUC_NONNULL(4)__attribute__((__nonnull__(4))) NS_GNUC_NONNULL(5)__attribute__((__nonnull__(5)));
119
120# ifndef HAVE_OPENSSL_PRE_1_0
121static void ListMDfunc(const EVP_MD *m, const char *from, const char *to, void *arg);
122# endif
123
124static Tcl_ObjCmdProc CryptoHmacAddObjCmd;
125static Tcl_ObjCmdProc CryptoHmacFreeObjCmd;
126static Tcl_ObjCmdProc CryptoHmacGetObjCmd;
127static Tcl_ObjCmdProc CryptoHmacNewObjCmd;
128static Tcl_ObjCmdProc CryptoHmacStringObjCmd;
129
130static Tcl_ObjCmdProc CryptoMdAddObjCmd;
131static Tcl_ObjCmdProc CryptoMdFreeObjCmd;
132static Tcl_ObjCmdProc CryptoMdGetObjCmd;
133static Tcl_ObjCmdProc CryptoMdNewObjCmd;
134static Tcl_ObjCmdProc CryptoMdStringObjCmd;
135
136# ifndef OPENSSL_NO_EC
137# ifdef HAVE_OPENSSL_EC_PRIV2OCT
138static Tcl_ObjCmdProc CryptoEckeyPrivObjCmd;
139static Tcl_ObjCmdProc CryptoEckeyImportObjCmd;
140# endif
141# endif
142
143/*
144 * Local variables defined in this file.
145 */
146
147static const char * const mdCtxType = "ns:mdctx";
148static const char * const hmacCtxType = "ns:hmacctx";
149
150# ifdef HAVE_OPENSSL_HKDF
151static Ns_ObjvValueRange posIntRange0 = {0, INT_MAX2147483647};
152# endif
153# ifdef HAVE_OPENSSL_31
154#include <openssl/core_names.h>
155# endif
156static Ns_ObjvValueRange posIntRange1 = {1, INT_MAX2147483647};
157
158/*
159 *----------------------------------------------------------------------
160 *
161 * Debug function to ease work with binary data.
162 *
163 *----------------------------------------------------------------------
164 */
165static void hexPrint(const char *msg, const unsigned char *octects, size_t octectLength)
166{
167 if (Ns_LogSeverityEnabled(Debug)) {
168 size_t i;
169 Tcl_DString ds;
170
171 Tcl_DStringInit(&ds);
172 Ns_DStringPrintf(&ds, "%s (len %" PRIuz"zu" "): ", msg, octectLength);
173 for (i = 0; i < octectLength; i++) {
174 Ns_DStringPrintf(&ds, "%.2x ", octects[i] & 0xff);
175 }
176 Ns_Log(Debug, "%s", ds.string);
177 Tcl_DStringFree(&ds);
178 }
179}
180
181
182/*
183 *----------------------------------------------------------------------
184 *
185 * EncodedObj --
186 *
187 * Helper function result encodings.
188 *
189 * Results:
190 * Tcl result code
191 *
192 * Side effects:
193 * Interp result Obj is updated in case of error.
194 *
195 *----------------------------------------------------------------------
196 */
197
198static Tcl_Obj*
199EncodedObj(unsigned char *octects, size_t octectLength,
200 char *outputBuffer, Ns_BinaryEncoding encoding) {
201 char *origOutputBuffer = outputBuffer;
202 Tcl_Obj *resultObj = NULL((void*)0); /* enumeration is complete, quiet some older compilers */
203
204 NS_NONNULL_ASSERT(octects != NULL)((void) (0));
205
206 if (outputBuffer == NULL((void*)0) && encoding != RESULT_ENCODING_BINARY) {
207 /*
208 * It is a safe assumption to double the size, since the hex
209 * encoding needs the most space.
210 */
211 outputBuffer = ns_malloc(octectLength * 2u + 1u);
212 }
213
214 switch (encoding) {
215 case RESULT_ENCODING_BINARY:
216 resultObj = Tcl_NewByteArrayObj(octects, (int)octectLength);
217 break;
218
219 case RESULT_ENCODING_BASE64URL:
220 hexPrint("result", octects, octectLength);
221 (void)Ns_HtuuEncode2(octects, octectLength, outputBuffer, 1);
222 resultObj = Tcl_NewStringObj(outputBuffer, (int)strlen(outputBuffer));
223 break;
224
225 case RESULT_ENCODING_BASE64:
226 (void)Ns_HtuuEncode2(octects, octectLength, outputBuffer, 0);
227 resultObj = Tcl_NewStringObj(outputBuffer, (int)strlen(outputBuffer));
228 break;
229
230 case RESULT_ENCODING_HEX:
231 Ns_HexString(octects, outputBuffer, (int)octectLength, NS_FALSE0);
232 resultObj = Tcl_NewStringObj(outputBuffer, (int)octectLength*2);
233 break;
234 }
235
236 if (outputBuffer != origOutputBuffer) {
237 ns_free(outputBuffer);
238 }
239
240 return resultObj;
241}
242
243/*
244 *----------------------------------------------------------------------
245 *
246 * Compatibility functions for older versions of OpenSSL.
247 *
248 *----------------------------------------------------------------------
249 */
250# ifdef HAVE_OPENSSL_PRE_1_1
251# define NS_EVP_MD_CTX_newEVP_MD_CTX_new EVP_MD_CTX_create
252# define NS_EVP_MD_CTX_freeEVP_MD_CTX_free EVP_MD_CTX_destroy
253
254static HMAC_CTX *HMAC_CTX_new(void);
255static void HMAC_CTX_free(HMAC_CTX *ctx) NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
256
257# else
258# define NS_EVP_MD_CTX_newEVP_MD_CTX_new EVP_MD_CTX_new
259# define NS_EVP_MD_CTX_freeEVP_MD_CTX_free EVP_MD_CTX_free
260# endif
261
262# ifdef HAVE_OPENSSL_PRE_1_1
263/*
264 *----------------------------------------------------------------------
265 *
266 * HMAC_CTX_new, HMAC_CTX_free --
267 *
268 * The NEW/FREE interface for HMAC_CTX is new in OpenSSL 1.1.0.
269 * Before, HMAC_CTX_init and HMAC_CTX_cleanup were used. We
270 * provide here a forward compatible version.
271 *
272 *----------------------------------------------------------------------
273 */
274static HMAC_CTX *HMAC_CTX_new(void)
275{
276 HMAC_CTX *ctx = ns_malloc(sizeof(HMAC_CTX));
277 HMAC_CTX_init(ctx);
278 return ctx;
279}
280
281static void HMAC_CTX_free(HMAC_CTX *ctx)
282{
283 NS_NONNULL_ASSERT(ctx != NULL)((void) (0));
284
285 HMAC_CTX_cleanup(ctx);
286 ns_free(ctx);
287}
288# endif
289
290
291# ifdef HAVE_OPENSSL_PRE_1_1
292# ifndef OPENSSL_NO_EC
293/*
294 *----------------------------------------------------------------------
295 *
296 * ECDSA_SIG_get0 --
297 *
298 * The function ECDSA_SIG_get0 is new in OpenSSL 1.1.0 (and not
299 * available in LIBRESSL). We provide here a forward compatible
300 * version.
301 *
302 *----------------------------------------------------------------------
303 */
304static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
305{
306 if (pr != NULL((void*)0)) {
307 *pr = sig->r;
308 }
309 if (ps != NULL((void*)0)) {
310 *ps = sig->s;
311 }
312}
313# endif
314# endif
315
316
317/*
318 *----------------------------------------------------------------------
319 *
320 * GetDigest, ListMDfunc --
321 *
322 * Converter from a digest string to internal OpenSSL
323 * representation. ListMDfunc is an iterator usable in OpenSSL
324 * 1.0.0 or newer to obtain the names of all available digest
325 * functions to provide nicer error messages.
326 *
327 * Results:
328 * Tcl result code, value in third argument.
329 *
330 * Side effects:
331 * Interp result Obj is updated.
332 *
333 *----------------------------------------------------------------------
334 */
335
336# ifndef HAVE_OPENSSL_PRE_1_0
337static void
338ListMDfunc(const EVP_MD *m, const char *from, const char *UNUSED(to)UNUSED_to __attribute__((__unused__)), void *arg)
339{
340 Tcl_Obj *listPtr = (Tcl_Obj *)arg;
341
342 if ((m != NULL((void*)0)) && (from != NULL((void*)0))) {
343 const char *mdName = EVP_MD_nameEVP_MD_get0_name(m);
344
345 /* fprintf(stderr, "from %s to %to name <%s> type (nid) %d\n", from, to, mdName, EVP_MD_type(m)); */
346 /*
347 * Apprarently, the list contains upper and lowercase variants. Avoid
348 * duplication.
349 */
350 if ((*from >= 'a') && (*from <= 'z')) {
351 (void)Tcl_ListObjAppendElement(NULL((void*)0), listPtr, Tcl_NewStringObj(mdName, -1));
352 }
353 }
354}
355# endif
356
357static int
358GetDigest(Tcl_Interp *interp, const char *digestName, const EVP_MD **mdPtr)
359{
360 int result;
361
362 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
363 NS_NONNULL_ASSERT(digestName != NULL)((void) (0));
364 NS_NONNULL_ASSERT(mdPtr != NULL)((void) (0));
365
366 *mdPtr = EVP_get_digestbyname(digestName);
367 if (*mdPtr == NULL((void*)0)) {
368# ifndef HAVE_OPENSSL_PRE_1_0
369 /*
370 * EVP_MD_do_all_sorted was added in OpenSSL 1.0.0. The
371 * function is an iterator, which we provide with a tailored
372 * callback.
373 */
374 Tcl_Obj *listObj = Tcl_NewListObj(0, NULL((void*)0));
375
376 Tcl_IncrRefCount(listObj)++(listObj)->refCount;
377 EVP_MD_do_all_sorted(ListMDfunc, listObj);
378 Ns_TclPrintfResult(interp, "Unknown value for digest \"%s\", valid: %s",
379 digestName, Tcl_GetString(listObj));
380 Tcl_DecrRefCount(listObj)do { Tcl_Obj *_objPtr = (listObj); if (_objPtr->refCount--
<= 1) { TclFreeObj(_objPtr); } } while(0)
;
381# else
382 Ns_TclPrintfResult(interp, "Unknown message digest \"%s\"", digestName);
383# endif
384 result = TCL_ERROR1;
385 } else {
386 result = TCL_OK0;
387 }
388 return result;
389}
390
391/*
392 *----------------------------------------------------------------------
393 *
394 * GetCipher --
395 *
396 * Helper function to lookup cipher from a string.
397 *
398 * Results:
399 * Tcl result code, value in third argument.
400 *
401 * Side effects:
402 * Interp result Obj is updated.
403 *
404 *----------------------------------------------------------------------
405 */
406static int
407GetCipher(Tcl_Interp *interp, const char *cipherName, unsigned long flags, const char *modeMsg, const EVP_CIPHER **cipherPtr)
408{
409 int result = TCL_OK0;
410
411 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
412 NS_NONNULL_ASSERT(cipherName != NULL)((void) (0));
413 NS_NONNULL_ASSERT(modeMsg != NULL)((void) (0));
414 NS_NONNULL_ASSERT(cipherPtr != NULL)((void) (0));
415
416 *cipherPtr = EVP_get_cipherbyname(cipherName);
417 if (*cipherPtr == NULL((void*)0)) {
418 Ns_TclPrintfResult(interp, "Unknown cipher \"%s\"", cipherName);
419 result = TCL_ERROR1;
420 } else if (flags != 0u) {
421 int mode = EVP_CIPHER_modeEVP_CIPHER_get_mode(*cipherPtr);
422 if (((unsigned)mode && flags) == 0u) {
423 Ns_TclPrintfResult(interp, "cipher \"%s\" does not support require mode: %s", cipherName, modeMsg);
424 result = TCL_ERROR1;
425 }
426 }
427 return result;
428}
429
430# ifndef OPENSSL_NO_EC
431/*
432 *----------------------------------------------------------------------
433 *
434 * GetCurve --
435 *
436 * Helper function to lookup a nid from a curve name.
437 * The logic is from apps/ecparam.c
438 *
439 * Results:
440 * Tcl result code, value in third argument.
441 *
442 * Side effects:
443 * Interp result Obj is updated in case of error.
444 *
445 *----------------------------------------------------------------------
446 */
447static int
448GetCurve(Tcl_Interp *interp, const char *curveName, int *nidPtr)
449{
450 int result, nid;
451
452 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
453 NS_NONNULL_ASSERT(curveName != NULL)((void) (0));
454 NS_NONNULL_ASSERT(nidPtr != NULL)((void) (0));
455
456 /*
457 * Workaround for the SECG curve names secp192r1 and secp256r1 (which
458 * are the same as the curves prime192v1 and prime256v1 defined in
459 * X9.62).
460 */
461 if (strcmp(curveName, "secp192r1") == 0) {
462 Ns_Log(Warning, "using curve name prime192v1 instead of secp192r1");
463 nid = NID_X9_62_prime192v1409;
464 } else if (strcmp(curveName, "secp256r1") == 0) {
465 Ns_Log(Warning, "using curve name prime256v1 instead of secp256r1");
466 nid = NID_X9_62_prime256v1415;
467 } else {
468 nid = OBJ_sn2nid(curveName);
469 }
470# ifndef HAVE_OPENSSL_PRE_1_0_2
471 if (nid == 0) {
472 nid = EC_curve_nist2nid(curveName);
473 }
474# endif
475 if (nid == 0) {
476 Ns_TclPrintfResult(interp, "Unknown curve name \"%s\"", curveName);
477 result = TCL_ERROR1;
478 } else {
479 *nidPtr = nid;
480 result = TCL_OK0;
481 }
482 return result;
483}
484# endif /* OPENSSL_NO_EC */
485
486/*
487 *----------------------------------------------------------------------
488 *
489 * GetPkeyFromPem, GetEckeyFromPem --
490 *
491 * Helper function for reading .pem-files
492 *
493 * Results:
494 * Tcl result code
495 *
496 * Side effects:
497 * Interp result Obj is updated in case of error.
498 *
499 *----------------------------------------------------------------------
500 */
501
502static EVP_PKEY *
503GetPkeyFromPem(Tcl_Interp *interp, char *pemFileName, const char *passPhrase, bool_Bool private)
504{
505 BIO *bio;
506 EVP_PKEY *result;
507
508 bio = BIO_new_file(pemFileName, "r");
509 if (bio == NULL((void*)0)) {
510 Ns_TclPrintfResult(interp, "could not open pem file '%s' for reading", pemFileName);
511 result = NULL((void*)0);
512 } else {
513 if (private) {
514 result = PEM_read_bio_PrivateKey(bio, NULL((void*)0), NULL((void*)0), (char*)passPhrase);
515 } else {
516 result = PEM_read_bio_PUBKEY(bio, NULL((void*)0), NULL((void*)0), (char*)passPhrase);
517 }
518 BIO_free(bio);
519 if (result == NULL((void*)0)) {
520 Ns_TclPrintfResult(interp, "pem file contains no %s key", (private ? "private" : "public"));
521 }
522 }
523 return result;
524}
525
526# ifndef OPENSSL_NO_EC
527static EC_KEY *
528GetEckeyFromPem(Tcl_Interp *interp, char *pemFileName, const char *passPhrase, bool_Bool private)
529{
530 BIO *bio;
531 EC_KEY *result;
532
533 bio = BIO_new_file(pemFileName, "r");
534 if (bio == NULL((void*)0)) {
535 Ns_TclPrintfResult(interp, "could not open pem file '%s' for reading", pemFileName);
536 result = NULL((void*)0);
537 } else {
538 if (private) {
539 result = PEM_read_bio_ECPrivateKey(bio, NULL((void*)0), NULL((void*)0), (char*)passPhrase);
540 } else {
541 result = PEM_read_bio_EC_PUBKEY(bio, NULL((void*)0), NULL((void*)0), (char*)passPhrase);
542 }
543 BIO_free(bio);
544 if (result == NULL((void*)0)) {
545 Ns_TclPrintfResult(interp, "eckey_from_pem: pem file contains no %s EC key",
546 (private ? "private" : "public"));
547 }
548 }
549 return result;
550}
551# endif /* OPENSSL_NO_EC */
552
553
554
555
556
557/*
558 *----------------------------------------------------------------------
559 *
560 * CryptoHmacNewObjCmd -- Subcommand of NsTclCryptoHmacObjCmd
561 *
562 * Incremental command to initialize a HMAC context. This
563 * command is typically followed by a sequence of "add"
564 * subcommands until the content is read with the "get"
565 * subcommand and then freed.
566 *
567 * Results:
568 * Tcl Result Code.
569 *
570 * Side effects:
571 * Creating HMAC context
572 *
573 *----------------------------------------------------------------------
574 */
575static int
576CryptoHmacNewObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
577{
578 int result, isBinary = 0;
579 char *digestName = (char *)"sha256";
580 Tcl_Obj *keyObj;
581 Ns_ObjvSpec opts[] = {
582 {"-binary", Ns_ObjvBool, &isBinary, INT2PTR(NS_TRUE)((void *)(intptr_t)(1))},
583 {"--", Ns_ObjvBreak, NULL((void*)0), NULL((void*)0)},
584 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
585 };
586 Ns_ObjvSpec args[] = {
587 {"digest", Ns_ObjvString, &digestName, NULL((void*)0)},
588 {"key", Ns_ObjvObj, &keyObj, NULL((void*)0)},
589 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
590 };
591
592 if (Ns_ParseObjv(opts, args, interp, 2, objc, objv) != NS_OK) {
593 result = TCL_ERROR1;
594
595 } else {
596 const EVP_MD *md;
597
598 /*
599 * Look up the Message Digest from OpenSSL
600 */
601 result = GetDigest(interp, digestName, &md);
602 if (result != TCL_ERROR1) {
603 HMAC_CTX *ctx;
604 const unsigned char *keyString;
605 int keyLength;
606 Tcl_DString keyDs;
607
608 Tcl_DStringInit(&keyDs);
609 keyString = Ns_GetBinaryString(keyObj, isBinary == 1, &keyLength, &keyDs);
610 ctx = HMAC_CTX_new();
611 HMAC_Init_ex(ctx, keyString, keyLength, md, NULL((void*)0));
612 Ns_TclSetAddrObj(Tcl_GetObjResult(interp), hmacCtxType, ctx);
613 Tcl_DStringFree(&keyDs);
614 }
615 }
616 return result;
617}
618
619
620/*
621 *----------------------------------------------------------------------
622 *
623 * CryptoHmacAddObjCmd -- Subcommand of NsTclCryptoHmacObjCmd
624 *
625 * Implements "ns_crypto::hmac add", an incremental command to
626 * add a message chunk to a predefined HMAC context, which was
627 * previously created via the "new" subcommand.
628 *
629 * Results:
630 * Tcl Result Code.
631 *
632 * Side effects:
633 * Updating HMAC context
634 *
635 *----------------------------------------------------------------------
636 */
637static int
638CryptoHmacAddObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
639{
640 int result = TCL_OK0, isBinary = 0;
641 HMAC_CTX *ctx;
642 Tcl_Obj *ctxObj;
643 Tcl_Obj *messageObj;
644 int messageLength;
645 Ns_ObjvSpec opts[] = {
Duplicate code detected
646 {"-binary", Ns_ObjvBool, &isBinary, INT2PTR(NS_TRUE)((void *)(intptr_t)(1))},
647 {"--", Ns_ObjvBreak, NULL((void*)0), NULL((void*)0)},
648 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
649 };
650 Ns_ObjvSpec args[] = {
651 {"ctx", Ns_ObjvObj, &ctxObj, NULL((void*)0)},
652 {"message", Ns_ObjvObj, &messageObj, NULL((void*)0)},
653 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
654 };
655
656 if (Ns_ParseObjv(opts, args, interp, 2, objc, objv) != NS_OK) {
657 result = TCL_ERROR1;
658
659 } else if (Ns_TclGetOpaqueFromObj(ctxObj, hmacCtxType, (void **)&ctx) != TCL_OK0) {
660 Ns_TclPrintfResult(interp, "argument is not of type \"%s\"", hmacCtxType);
661 result = TCL_ERROR1;
662
663 } else {
664 const unsigned char *message;
665 Tcl_DString messageDs;
666
667 Tcl_DStringInit(&messageDs);
668 message = Ns_GetBinaryString(messageObj, isBinary == 1, &messageLength, &messageDs);
669 HMAC_Update(ctx, message, (size_t)messageLength);
670 Tcl_DStringFree(&messageDs);
671 }
672 return result;
673}
674
675
676/*
677 *----------------------------------------------------------------------
678 *
679 * CryptoHmacGetObjCmd -- Subcommand of NsTclCryptoHmacObjCmd
680 *
681 * Implements "ns_crypto::hmac get", an incremental command to
682 * get the (maybe partial) HMAC result in form of a hex string.
683 *
684 * Results:
685 * Tcl Result Code.
686 *
687 * Side effects:
688 * None.
689 *
690 *----------------------------------------------------------------------
691 */
692static int
693CryptoHmacGetObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
694{
695 int result = TCL_OK0;
696 HMAC_CTX *ctx;
697 Tcl_Obj *ctxObj;
698 Ns_BinaryEncoding encoding = RESULT_ENCODING_HEX;
699
700 Ns_ObjvSpec lopts[] = {
701 {"-encoding", Ns_ObjvIndex, &encoding, binaryencodings},
702 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
703 };
704 Ns_ObjvSpec args[] = {
705 {"ctx", Ns_ObjvObj, &ctxObj, NULL((void*)0)},
706 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
707 };
708
709 if (Ns_ParseObjv(lopts, args, interp, 2, objc, objv) != NS_OK) {
710 result = TCL_ERROR1;
711
712 } else if (Ns_TclGetOpaqueFromObj(ctxObj, hmacCtxType, (void **)&ctx) != TCL_OK0) {
713 Ns_TclPrintfResult(interp, "argument is not of type \"%s\"", hmacCtxType);
714 result = TCL_ERROR1;
715
716 } else {
717 unsigned char digest[EVP_MAX_MD_SIZE64];
718 char digestChars[EVP_MAX_MD_SIZE64*2 + 1];
719 unsigned int mdLength;
720 HMAC_CTX *partial_ctx;
721
722 partial_ctx = HMAC_CTX_new();
723 HMAC_CTX_copy(partial_ctx, ctx);
724 HMAC_Final(partial_ctx, digest, &mdLength);
725 HMAC_CTX_free(partial_ctx);
726
727 /*
728 * Convert the result to the output format and set the interp
729 * result.
730 */
731 Tcl_SetObjResult(interp, EncodedObj(digest, mdLength, digestChars, encoding));
732 }
733 return result;
734}
735
736
737/*
738 *----------------------------------------------------------------------
739 *
740 * CryptoHmacFreeObjCmd -- Subcommand of NsTclCryptoHmacObjCmd
741 *
742 * Implements "ns_crypto::hmac free". Frees a previously
743 * allocated HMAC context.
744 *
745 * Results:
746 * Tcl Result Code.
747 *
748 * Side effects:
749 * Freeing memory
750 *
751 *----------------------------------------------------------------------
752 */
753static int
754CryptoHmacFreeObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
755{
756 int result = TCL_OK0;
757 HMAC_CTX *ctx;
758 Tcl_Obj *ctxObj;
759 Ns_ObjvSpec args[] = {
760 {"ctx", Ns_ObjvObj, &ctxObj, NULL((void*)0)},
761 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
762 };
763
764 if (Ns_ParseObjv(NULL((void*)0), args, interp, 2, objc, objv) != NS_OK) {
765 result = TCL_ERROR1;
766
767 } else if (Ns_TclGetOpaqueFromObj(ctxObj, hmacCtxType, (void **)&ctx) != TCL_OK0) {
768 Ns_TclPrintfResult(interp, "argument is not of type \"%s\"", hmacCtxType);
769 result = TCL_ERROR1;
770
771 } else {
772
773 HMAC_CTX_free(ctx);
774 Ns_TclResetObjType(ctxObj, NULL((void*)0));
775 }
776 return result;
777}
778
779
780/*
781 *----------------------------------------------------------------------
782 *
783 * CryptoHmacStringObjCmd -- Subcommand of NsTclCryptoHmacObjCmd
784 *
785 * Implements "ns_crypto::hmac string". Single command to
786 * obtain an HMAC from the provided data. Technically, this is
787 * a combination of the other subcommands, but requires that
788 * the all data for the HMAC computation is provided in the
789 * contents of a Tcl_Obj in memory. The command returns the
790 * HMAC in form of a hex string.
791 *
792 * Results:
793 * Tcl Result Code.
794 *
795 * Side effects:
796 * None.
797 *
798 *----------------------------------------------------------------------
799 */
800static int
801CryptoHmacStringObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
802{
803 int result, isBinary = 0;
804 Tcl_Obj *keyObj, *messageObj;
805 char *digestName = (char *)"sha256";
806 Ns_BinaryEncoding encoding = RESULT_ENCODING_HEX;
807
808 Ns_ObjvSpec lopts[] = {
809 {"-binary", Ns_ObjvBool, &isBinary, INT2PTR(NS_TRUE)((void *)(intptr_t)(1))},
810 {"-digest", Ns_ObjvString, &digestName, NULL((void*)0)},
811 {"-encoding", Ns_ObjvIndex, &encoding, binaryencodings},
812 {"--", Ns_ObjvBreak, NULL((void*)0), NULL((void*)0)},
813 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
814 };
815 Ns_ObjvSpec args[] = {
816 {"key", Ns_ObjvObj, &keyObj, NULL((void*)0)},
817 {"message", Ns_ObjvObj, &messageObj, NULL((void*)0)},
818 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
819 };
820
821 if (Ns_ParseObjv(lopts, args, interp, 2, objc, objv) != NS_OK) {
822 result = TCL_ERROR1;
823
824 } else {
825 const EVP_MD *md;
826
827 /*
828 * Look up the Message digest from OpenSSL
829 */
830 result = GetDigest(interp, digestName, &md);
831 if (result != TCL_ERROR1) {
832 unsigned char digest[EVP_MAX_MD_SIZE64];
833 char digestChars[EVP_MAX_MD_SIZE64*2 + 1];
834 HMAC_CTX *ctx;
835 const unsigned char *keyString, *messageString;
836 unsigned int mdLength;
837 int keyLength, messageLength;
838 Tcl_DString keyDs, messageDs;
839
840 /*
841 * All input parameters are valid, get key and data.
842 */
843 Tcl_DStringInit(&keyDs);
844 Tcl_DStringInit(&messageDs);
845 keyString = Ns_GetBinaryString(keyObj, isBinary == 1, &keyLength, &keyDs);
846 messageString = Ns_GetBinaryString(messageObj, isBinary == 1, &messageLength, &messageDs);
847 hexPrint("hmac key", keyString, (size_t)keyLength);
848 hexPrint("hmac message", messageString, (size_t)messageLength);
849
850 /*
851 * Call the HMAC computation.
852 */
853 ctx = HMAC_CTX_new();
854 HMAC(md,
855 (const void *)keyString, keyLength,
856 (const void *)messageString, (size_t)messageLength,
857 digest, &mdLength);
858 HMAC_CTX_free(ctx);
859
860 /*
861 * Convert the result to the output format and set the interp
862 * result.
863 */
864 Tcl_SetObjResult(interp, EncodedObj(digest, mdLength, digestChars, encoding));
865
866 Tcl_DStringFree(&keyDs);
867 Tcl_DStringFree(&messageDs);
868 }
869 }
870 return result;
871}
872
873/*
874 *----------------------------------------------------------------------
875 *
876 * NsTclCryptoHmacObjCmd --
877 *
878 * Implements "ns_crypto::hmac" with various subcmds for handling
879 * Hash-based message authentications codes (HMAC)
880 *
881 * Results:
882 * NS_OK
883 *
884 * Side effects:
885 * Tcl result is set to a string value.
886 *
887 *----------------------------------------------------------------------
888 */
889
890int
891NsTclCryptoHmacObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
892{
893 const Ns_SubCmdSpec subcmds[] = {
894 {"string", CryptoHmacStringObjCmd},
895 {"new", CryptoHmacNewObjCmd},
896 {"add", CryptoHmacAddObjCmd},
897 {"get", CryptoHmacGetObjCmd},
898 {"free", CryptoHmacFreeObjCmd},
899 {NULL((void*)0), NULL((void*)0)}
900 };
901
902 return Ns_SubcmdObjv(subcmds, clientData, interp, objc, objv);
903}
904
905
906
907
908
909
910
911
912/*
913 *----------------------------------------------------------------------
914 *
915 * CryptoMdNewObjCmd -- Subcommand of NsTclCryptoMdObjCmd
916 *
917 * Implements "ns_crypto::md new". Incremental command to
918 * initialize a MD context. This command is typically followed
919 * by a sequence of "add" subcommands until the content is read
920 * with the "get" subcommand and then freed.
921 *
922 * Results:
923 * Tcl Result Code.
924 *
925 * Side effects:
926 * Creating MD context
927 *
928 *----------------------------------------------------------------------
929 */
930static int
931CryptoMdNewObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
932{
933 int result;
934 char *digestName = (char *)"sha256";
935 Ns_ObjvSpec args[] = {
936 {"digest", Ns_ObjvString, &digestName, NULL((void*)0)},
937 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
938 };
939
940 if (Ns_ParseObjv(NULL((void*)0), args, interp, 2, objc, objv) != NS_OK) {
941 result = TCL_ERROR1;
942 } else {
943 const EVP_MD *md;
944
945 /*
946 * Look up the Message Digest from OpenSSL
947 */
948 result = GetDigest(interp, digestName, &md);
949 if (result != TCL_ERROR1) {
950 EVP_MD_CTX *mdctx;
951
952 mdctx = NS_EVP_MD_CTX_newEVP_MD_CTX_new();
953 EVP_DigestInit_ex(mdctx, md, NULL((void*)0));
954 Ns_TclSetAddrObj(Tcl_GetObjResult(interp), mdCtxType, mdctx);
955 }
956 }
957 return result;
958}
959
960
961
962/*
963 *----------------------------------------------------------------------
964 *
965 * CryptoMdAddObjCmd -- Subcommand of NsTclCryptoMdObjCmd
966 *
967 * Implements "ns_crypto::md add". Incremental command to add a
968 * message chunk to a predefined MD context, which was
969 * previously created via the "new" subcommand.
970 *
971 * Results:
972 * Tcl Result Code.
973 *
974 * Side effects:
975 * Updating MD context.
976 *
977 *----------------------------------------------------------------------
978 */
979static int
980CryptoMdAddObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
981{
982 int result = TCL_OK0, isBinary = 0;
983 EVP_MD_CTX *mdctx;
984 Tcl_Obj *ctxObj;
985 Tcl_Obj *messageObj;
986 Ns_ObjvSpec opts[] = {
Similar code here
987 {"-binary", Ns_ObjvBool, &isBinary, INT2PTR(NS_TRUE)((void *)(intptr_t)(1))},
988 {"--", Ns_ObjvBreak, NULL((void*)0), NULL((void*)0)},
989 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
990 };
991 Ns_ObjvSpec args[] = {
992 {"ctx", Ns_ObjvObj, &ctxObj, NULL((void*)0)},
993 {"message", Ns_ObjvObj, &messageObj, NULL((void*)0)},
994 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
995 };
996
997 if (Ns_ParseObjv(opts, args, interp, 2, objc, objv) != NS_OK) {
998 result = TCL_ERROR1;
999
1000 } else if (Ns_TclGetOpaqueFromObj(ctxObj, mdCtxType, (void **)&mdctx) != TCL_OK0) {
1001 Ns_TclPrintfResult(interp, "argument is not of type \"%s\"", mdCtxType);
1002 result = TCL_ERROR1;
1003
1004 } else {
1005 const unsigned char *message;
1006 int messageLength;
1007 Tcl_DString messageDs;
1008
1009 Tcl_DStringInit(&messageDs);
1010 message = Ns_GetBinaryString(messageObj, isBinary == 1, &messageLength, &messageDs);
1011 EVP_DigestUpdate(mdctx, message, (size_t)messageLength);
1012 Tcl_DStringFree(&messageDs);
1013 }
1014
1015 return result;
1016}
1017
1018
1019/*
1020 *----------------------------------------------------------------------
1021 *
1022 * CryptoMdGetObjCmd -- Subcommand of NsTclCryptoMdObjCmd
1023 *
1024 * Implements "ns_crypto::md get". Incremental command to get
1025 * the (maybe partial) MD result.
1026 *
1027 * Results:
1028 * Tcl Result Code.
1029 *
1030 * Side effects:
1031 * None.
1032 *
1033 *----------------------------------------------------------------------
1034 */
1035static int
1036CryptoMdGetObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1037{
1038 int result = TCL_OK0;
1039 EVP_MD_CTX *mdctx;
1040 Tcl_Obj *ctxObj;
1041 Ns_BinaryEncoding encoding = RESULT_ENCODING_HEX;
1042
1043 Ns_ObjvSpec lopts[] = {
1044 {"-encoding", Ns_ObjvIndex, &encoding, binaryencodings},
1045 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
1046 };
1047 Ns_ObjvSpec args[] = {
1048 {"ctx", Ns_ObjvObj, &ctxObj, NULL((void*)0)},
1049 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
1050 };
1051
1052 if (Ns_ParseObjv(lopts, args, interp, 2, objc, objv) != NS_OK) {
1053 result = TCL_ERROR1;
1054
1055 } else if (Ns_TclGetOpaqueFromObj(ctxObj, mdCtxType, (void **)&mdctx) != TCL_OK0) {
1056 Ns_TclPrintfResult(interp, "argument is not of type \"%s\"", mdCtxType);
1057 result = TCL_ERROR1;
1058
1059 } else {
1060 unsigned char digest[EVP_MAX_MD_SIZE64];
1061 char digestChars[EVP_MAX_MD_SIZE64*2 + 1];
1062 unsigned int mdLength;
1063 EVP_MD_CTX *partial_ctx;
1064
1065 partial_ctx = NS_EVP_MD_CTX_newEVP_MD_CTX_new();
1066 EVP_MD_CTX_copy(partial_ctx, mdctx);
1067 EVP_DigestFinal_ex(partial_ctx, digest, &mdLength);
1068 NS_EVP_MD_CTX_freeEVP_MD_CTX_free(partial_ctx);
1069
1070 /*
1071 * Convert the result to the output format and set the interp
1072 * result.
1073 */
1074 Tcl_SetObjResult(interp, EncodedObj(digest, mdLength, digestChars, encoding));
1075 }
1076 return result;
1077}
1078
1079
1080/*
1081 *----------------------------------------------------------------------
1082 *
1083 * CryptoMdFreeObjCmd -- Subcommand of NsTclCryptoMdObjCmd
1084 *
1085 * Implements "ns_crypto::md free". Frees a previously
1086 * allocated MD context.
1087 *
1088 * Results:
1089 * Tcl Result Code.
1090 *
1091 * Side effects:
1092 * Freeing memory
1093 *
1094 *----------------------------------------------------------------------
1095 */
1096static int
1097CryptoMdFreeObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1098{
1099 int result = TCL_OK0;
1100 EVP_MD_CTX *mdctx;
1101 Tcl_Obj *ctxObj;
1102 Ns_ObjvSpec args[] = {
1103 {"ctx", Ns_ObjvObj, &ctxObj, NULL((void*)0)},
1104 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
1105 };
1106
1107 if (Ns_ParseObjv(NULL((void*)0), args, interp, 2, objc, objv) != NS_OK) {
1108 result = TCL_ERROR1;
1109
1110 } else if (Ns_TclGetOpaqueFromObj(ctxObj, mdCtxType, (void **)&mdctx) != TCL_OK0) {
1111 Ns_TclPrintfResult(interp, "argument is not of type \"%s\"", mdCtxType);
1112 result = TCL_ERROR1;
1113
1114 } else {
1115 NS_EVP_MD_CTX_freeEVP_MD_CTX_free(mdctx);
1116 Ns_TclResetObjType(ctxObj, NULL((void*)0));
1117 }
1118
1119 return result;
1120}
1121
1122/*
1123 *----------------------------------------------------------------------
1124 *
1125 * CryptoMdStringObjCmd -- Subcommand of NsTclCryptoMdObjCmd
1126 *
1127 * Implements "ns_crypto::md string", a command to obtain a MD
1128 * (message digest) from the provided data. Technically, this
1129 * is a combination of the other subcommands, but requires that
1130 * the all data for the MD computation is provided in the
1131 * contents of a Tcl_Obj in memory. The command returns the MD
1132 * in form of a hex string.
1133 *
1134 * Results:
1135 * Tcl Result Code.
1136 *
1137 * Side effects:
1138 * Creating HMAC context
1139 *
1140 *----------------------------------------------------------------------
1141 */
1142static int
1143CryptoMdStringObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1144{
1145 int result, isBinary = 0;
1146 Tcl_Obj *messageObj, *signatureObj = NULL((void*)0), *resultObj = NULL((void*)0);
1147 char *digestName = (char *)"sha256",
1148 *passPhrase = (char *)NS_EMPTY_STRING,
1149 *signKeyFile = NULL((void*)0),
1150 *verifyKeyFile = NULL((void*)0);
1151 Ns_BinaryEncoding encoding = RESULT_ENCODING_HEX;
1152
1153 Ns_ObjvSpec lopts[] = {
1154 {"-binary", Ns_ObjvBool, &isBinary, INT2PTR(NS_TRUE)((void *)(intptr_t)(1))},
1155 {"-digest", Ns_ObjvString, &digestName, NULL((void*)0)},
1156 {"-encoding", Ns_ObjvIndex, &encoding, binaryencodings},
1157 {"-passphrase", Ns_ObjvString, &passPhrase, NULL((void*)0)},
1158 {"-sign", Ns_ObjvString, &signKeyFile, NULL((void*)0)},
1159 {"-signature", Ns_ObjvObj, &signatureObj, NULL((void*)0)},
1160 {"-verify", Ns_ObjvString, &verifyKeyFile, NULL((void*)0)},
1161 {"--", Ns_ObjvBreak, NULL((void*)0), NULL((void*)0)},
1162 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
1163 };
1164 Ns_ObjvSpec args[] = {
1165 {"message", Ns_ObjvObj, &messageObj, NULL((void*)0)},
1166 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
1167 };
1168
1169 if (Ns_ParseObjv(lopts, args, interp, 2, objc, objv) != NS_OK) {
1170 result = TCL_ERROR1;
1171
1172 } else if (signKeyFile != NULL((void*)0) && verifyKeyFile != NULL((void*)0)) {
1173 Ns_TclPrintfResult(interp, "the options '-sign' and '-verify' are mutually exclusive");
1174 result = TCL_ERROR1;
1175
1176 } else if ((verifyKeyFile != NULL((void*)0) && signatureObj == NULL((void*)0))
1177 || (verifyKeyFile == NULL((void*)0) && signatureObj != NULL((void*)0))
1178 ) {
1179 Ns_TclPrintfResult(interp, "the options '-verify' requires '-signature' and vice versa");
1180 result = TCL_ERROR1;
1181
1182 } else {
1183 const EVP_MD *md;
1184 EVP_PKEY *pkey = NULL((void*)0);
1185 char *keyFile = NULL((void*)0);
1186
1187 /*
1188 * Compute Message Digest or sign or validate signature via OpenSSL.
1189 *
1190 * ::ns_crypto::md string -digest sha256 -sign /usr/local/src/naviserver/private.pem "hello\n"
1191 *
1192 * Example from https://medium.com/@bn121rajesh/rsa-sign-and-verify-using-openssl-behind-the-scene-bf3cac0aade2
1193 * ::ns_crypto::md string -digest sha1 -sign /usr/local/src/naviserver/myprivate.pem "abcdefghijklmnopqrstuvwxyz\n"
1194 *
1195 */
1196 result = GetDigest(interp, digestName, &md);
1197 if (signKeyFile != NULL((void*)0)) {
1198 keyFile = signKeyFile;
1199 } else if (verifyKeyFile != NULL((void*)0)) {
1200 keyFile = verifyKeyFile;
1201 }
1202 if (result != TCL_ERROR1 && keyFile != NULL((void*)0)) {
1203 pkey = GetPkeyFromPem(interp, keyFile, passPhrase, (signKeyFile != NULL((void*)0)));
1204 if (pkey == NULL((void*)0)) {
1205 result = TCL_ERROR1;
1206 }
1207 }
1208 if (result != TCL_ERROR1) {
1209 unsigned char digestBuffer[EVP_MAX_MD_SIZE64], *digest = digestBuffer;
1210 char digestChars[EVP_MAX_MD_SIZE64*2 + 1], *outputBuffer = digestChars;
1211 EVP_MD_CTX *mdctx;
1212 const unsigned char *messageString;
1213 int messageLength;
1214 unsigned int mdLength = 0u;
1215 Tcl_DString messageDs, signatureDs;
1216
1217 /*
1218 * All input parameters are valid, get data.
1219 */
1220 Tcl_DStringInit(&messageDs);
1221 Tcl_DStringInit(&signatureDs);
1222
1223 messageString = Ns_GetBinaryString(messageObj, isBinary == 1, &messageLength, &messageDs);
1224 hexPrint("md", messageString, (size_t)messageLength);
1225
1226 /*
1227 * Call the Digest or Signature computation
1228 */
1229 mdctx = NS_EVP_MD_CTX_newEVP_MD_CTX_new();
1230 if (signKeyFile != NULL((void*)0) || verifyKeyFile != NULL((void*)0)) {
1231 EVP_PKEY_CTX *pctx;
1232 int r;
1233
1234 if (signKeyFile != NULL((void*)0)) {
1235 r = EVP_DigestSignInit(mdctx, &pctx, md, NULL((void*)0) /*engine*/, pkey);
1236 } else {
1237 r = EVP_DigestVerifyInit(mdctx, &pctx, md, NULL((void*)0) /*engine*/, pkey);
1238 }
1239
1240 if (r == 0) {
1241 Ns_TclPrintfResult(interp, "could not initialize signature context");
1242 result = TCL_ERROR1;
1243 pctx = NULL((void*)0);
1244 } else {
1245 size_t mdSize;
1246
1247 if (signKeyFile != NULL((void*)0)) {
1248 /*
1249 * A sign operation was requested.
1250 */
1251 r = EVP_DigestSignUpdate(mdctx, messageString, (size_t)messageLength);
1252
1253 if (r == 1) {
1254 r = EVP_DigestSignFinal(mdctx, NULL((void*)0), &mdSize);
1255 if (r == 1) {
1256 /*
1257 * Everything was fine, get a buffer
1258 * with the requested size and use
1259 * this as "digest".
1260 */
1261 Tcl_DStringSetLength(&signatureDs, (int)mdSize);
1262 digest = (unsigned char*)signatureDs.string;
1263
1264 r = EVP_DigestSignFinal(mdctx, digest, &mdSize);
1265
1266 outputBuffer = ns_malloc(mdSize * 2u + 1u);
1267 mdLength = (unsigned int)mdSize;
1268 mdctx = NULL((void*)0);
1269 } else {
1270 char errorBuffer[256];
1271
1272 Ns_TclPrintfResult(interp, "error while signing input: %s",
1273 ERR_error_string(ERR_get_error(), errorBuffer));
1274 result = TCL_ERROR1;
1275 mdctx = NULL((void*)0);
1276 }
1277 }
1278 if (r != 1) {
1279 Ns_TclPrintfResult(interp, "error while signing input");
1280 result = TCL_ERROR1;
1281 }
1282 } else {
1283 /*
1284 * A signature verification was requested.
1285 */
1286 r = EVP_DigestVerifyUpdate(mdctx,
1287 messageString,
1288 (size_t)messageLength);
1289
1290 if (r == 1) {
1291 int signatureLength;
1292 const unsigned char *signatureString;
1293
1294 signatureString = Ns_GetBinaryString(signatureObj, 1,
1295 &signatureLength,
1296 &signatureDs);
1297 r = EVP_DigestVerifyFinal(mdctx,
1298 signatureString,
1299 (size_t)signatureLength);
1300
1301 if (r == 1) {
1302 /*
1303 * The signature was successfully verified.
1304 */
1305 resultObj = Tcl_NewIntObj(1);
1306 mdctx = NULL((void*)0);
1307 } else if (r == 0) {
1308 /*
1309 * Signature verification failure.
1310 */
1311 resultObj = Tcl_NewIntObj(0);
1312 mdctx = NULL((void*)0);
1313 } else {
1314 Ns_TclPrintfResult(interp, "error while verifying signature");
1315 result = TCL_ERROR1;
1316 }
1317 } else {
1318 Ns_TclPrintfResult(interp, "error while updating verify digest");
1319 result = TCL_ERROR1;
1320 }
1321 }
1322 }
1323 if (pctx != NULL((void*)0)) {
1324 EVP_PKEY_CTX_free(pctx);
1325 }
1326 EVP_PKEY_free(pkey);
1327
1328 } else {
1329 EVP_DigestInit_ex(mdctx, md, NULL((void*)0));
1330 EVP_DigestUpdate(mdctx, messageString, (unsigned long)messageLength);
1331 EVP_DigestFinal_ex(mdctx, digest, &mdLength);
1332 }
1333
1334 if (mdctx != NULL((void*)0)) {
1335 NS_EVP_MD_CTX_freeEVP_MD_CTX_free(mdctx);
1336 }
1337
1338 if (result == TCL_OK0) {
1339 /*
1340 * Convert the result to the requested output format,
1341 * unless we have already some resultObj.
1342 */
1343 if (resultObj == NULL((void*)0)) {
1344 resultObj = EncodedObj(digest, mdLength, outputBuffer, encoding);
1345 }
1346
1347 Tcl_SetObjResult(interp, resultObj);
1348 }
1349 if (outputBuffer != digestChars) {
1350 ns_free(outputBuffer);
1351 }
1352 Tcl_DStringFree(&messageDs);
1353 Tcl_DStringFree(&signatureDs);
1354 }
1355 }
1356
1357 return result;
1358}
1359
1360# ifndef OPENSSL_NO_EC
1361/*
1362 *----------------------------------------------------------------------
1363 *
1364 * CryptoMdVapidSignObjCmd -- Subcommand of NsTclCryptoMdObjCmd
1365 *
1366 * Implements "ns_crypto::md vapidsign". Aubcommand to sign a
1367 * message according to the Voluntary Application Server
1368 * Identification (VAPID) for Web Push
1369 * https://tools.ietf.org/id/draft-ietf-webpush-vapid-03.html
1370 *
1371 * See also: Generic Event Delivery Using HTTP Push
1372 * https://tools.ietf.org/html/rfc8030
1373 *
1374 * Essentially, this is a special form of a signed message
1375 * digest based on elliptic curve cryptography.
1376 *
1377 * Results:
1378 * Tcl Result Code.
1379 *
1380 * Side effects:
1381 * None
1382 *
1383 *----------------------------------------------------------------------
1384 */
1385static int
1386CryptoMdVapidSignObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1387{
1388 int result, isBinary = 0;
1389 Tcl_Obj *messageObj;
1390 char *digestName = (char *)"sha256", *pemFile = NULL((void*)0),
1391 *passPhrase = (char *)NS_EMPTY_STRING;
1392 Ns_BinaryEncoding encoding = RESULT_ENCODING_HEX;
1393
1394 Ns_ObjvSpec lopts[] = {
1395 {"-binary", Ns_ObjvBool, &isBinary, INT2PTR(NS_TRUE)((void *)(intptr_t)(1))},
1396 {"-digest", Ns_ObjvString, &digestName, NULL((void*)0)},
1397 {"-encoding", Ns_ObjvIndex, &encoding, binaryencodings},
1398 {"-passphrase", Ns_ObjvString, &passPhrase, NULL((void*)0)},
1399 {"-pem", Ns_ObjvString, &pemFile, NULL((void*)0)},
1400 {"--", Ns_ObjvBreak, NULL((void*)0), NULL((void*)0)},
1401 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
1402 };
1403 Ns_ObjvSpec args[] = {
1404 {"message", Ns_ObjvObj, &messageObj, NULL((void*)0)},
1405 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
1406 };
1407
1408 /*
1409 set ::pemFile /usr/local/ns/modules/vapid/prime256v1_key.pem
1410 ::ns_crypto::md vapidsign -digest sha256 -pem $::pemFile "hello"
1411 ::ns_crypto::md vapidsign -digest sha256 -pem $::pemFile -encoding hex "hello"
1412 ::ns_crypto::md vapidsign -digest sha256 -pem $::pemFile -encoding base64url "hello"
1413
1414 proc vapidToken {string} {
1415 return $string.[::ns_crypto::md vapidsign -digest sha256 -pem $::pemFile -encoding base64url $string]
1416 }
1417
1418 regsub -all {[\s]} [subst {{
1419 "sub" : "mailto:h0325904foo@bar.com",
1420 "aud" : "https://updates.push.services.mozilla.com",
1421 "exp" : "[expr [clock seconds] + 60*120]"
1422 }}] "" claim
1423 set JWTHeader [ns_base64urlencode {{"typ":"JWT","alg":"ES256"}}]
1424 set JWTbody [ns_base64urlencode $claim]
1425
1426 vapidToken $JWTHeader.$JWTbody
1427
1428 # check result: https://jwt.io/
1429 */
1430
1431 if (Ns_ParseObjv(lopts, args, interp, 2, objc, objv) != NS_OK) {
1432 result = TCL_ERROR1;
1433
1434 } else if (pemFile == NULL((void*)0)) {
1435 Ns_TclPrintfResult(interp, "no pem file specified");
1436 result = TCL_ERROR1;
1437
1438 } else {
1439 const EVP_MD *md;
1440 EC_KEY *eckey = NULL((void*)0);
1441
1442 /*
1443 * Look up the Message Digest from OpenSSL
1444 */
1445 result = GetDigest(interp, digestName, &md);
1446 if (result != TCL_ERROR1) {
1447
1448 eckey = GetEckeyFromPem(interp, pemFile, passPhrase, NS_TRUE1);
1449 if (eckey == NULL((void*)0)) {
1450 /*
1451 * GetEckeyFromPem handles error message
1452 */
1453 result = TCL_ERROR1;
1454 }
1455 }
1456 if (result != TCL_ERROR1) {
1457 unsigned char digest[EVP_MAX_MD_SIZE64];
1458 EVP_MD_CTX *mdctx;
1459 const unsigned char *messageString;
1460 int messageLength;
1461 unsigned int sigLen, mdLength, rLen, sLen;
1462 Tcl_DString messageDs;
1463 ECDSA_SIG *sig;
1464 const BIGNUM *r, *s;
1465 uint8_t *rawSig;
1466
1467 /*
1468 * All input parameters are valid, get key and data.
1469 */
1470 Tcl_DStringInit(&messageDs);
1471 messageString = Ns_GetBinaryString(messageObj, isBinary == 1, &messageLength, &messageDs);
1472
1473 /*
1474 * Call the Digest or Signature computation
1475 */
1476 mdctx = NS_EVP_MD_CTX_newEVP_MD_CTX_new();
1477
1478 EVP_DigestInit_ex(mdctx, md, NULL((void*)0));
1479 EVP_DigestUpdate(mdctx, messageString, (unsigned long)messageLength);
1480 EVP_DigestFinal_ex(mdctx, digest, &mdLength);
1481
1482 sig = ECDSA_do_sign(digest, SHA256_DIGEST_LENGTH32, eckey);
1483 ECDSA_SIG_get0(sig, &r, &s);
1484 rLen = (unsigned int) BN_num_bytes(r)((BN_num_bits(r)+7)/8);
1485 sLen = (unsigned int) BN_num_bytes(s)((BN_num_bits(s)+7)/8);
1486 sigLen = rLen + sLen;
1487 //fprintf(stderr, "siglen r %u + s%u -> %u\n", rLen, sLen, sigLen);
1488
1489 rawSig = ns_calloc(sigLen, sizeof(uint8_t));
1490 assert(rawSig != NULL)((void) (0));
1491
1492 BN_bn2bin(r, rawSig);
1493 hexPrint("r", rawSig, rLen);
1494 BN_bn2bin(s, &rawSig[rLen]);
1495 hexPrint("s", &rawSig[rLen], sLen);
1496
1497 /*
1498 * Convert the result to the output format and set the interp
1499 * result.
1500 */
1501 Tcl_SetObjResult(interp, EncodedObj(rawSig, sigLen, NULL((void*)0), encoding));
1502
1503 /*
1504 * Clean up.
1505 */
1506 EC_KEY_free(eckey);
1507 NS_EVP_MD_CTX_freeEVP_MD_CTX_free(mdctx);
1508 ns_free(rawSig);
1509 Tcl_DStringFree(&messageDs);
1510 }
1511 }
1512
1513 return result;
1514}
1515# endif /* OPENSSL_NO_EC */
1516
1517# ifdef HAVE_OPENSSL_HKDF
1518/*
1519 *----------------------------------------------------------------------
1520 *
1521 * CryptoMdHkdfObjCmd -- Subcommand of NsTclCryptoMdObjCmd
1522 *
1523 * Implements "ns_crypto::md hkdf", a command md to derive keys
1524 * based on message digests.
1525 *
1526 * See: RFC 5869: HMAC-based Extract-and-Expand Key Derivation Function (HKDF)
1527 * https://tools.ietf.org/html/rfc5869
1528 *
1529 * Results:
1530 * Tcl Result Code.
1531 *
1532 * Side effects:
1533 * None
1534 *
1535 *----------------------------------------------------------------------
1536 */
1537
1538static int
1539CryptoMdHkdfObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1540{
1541 int result, isBinary = 0, outLength = 0;
1542 Tcl_Obj *saltObj = NULL((void*)0), *secretObj = NULL((void*)0), *infoObj = NULL((void*)0);
1543 char *digestName = (char *)"sha256";
1544 Ns_BinaryEncoding encoding = RESULT_ENCODING_HEX;
1545 Ns_ObjvSpec lopts[] = {
1546 {"-binary", Ns_ObjvBool, &isBinary, INT2PTR(NS_TRUE)((void *)(intptr_t)(1))},
1547 {"-digest", Ns_ObjvString, &digestName, NULL((void*)0)},
1548 {"-salt", Ns_ObjvObj, &saltObj, NULL((void*)0)},
1549 {"-secret", Ns_ObjvObj, &secretObj, NULL((void*)0)},
1550 {"-info", Ns_ObjvObj, &infoObj, NULL((void*)0)},
1551 {"-encoding", Ns_ObjvIndex, &encoding, binaryencodings},
1552 {"--", Ns_ObjvBreak, NULL((void*)0), NULL((void*)0)},
1553 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
1554 };
1555 Ns_ObjvSpec args[] = {
1556 {"length", Ns_ObjvInt, &outLength, &posIntRange0},
1557 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
1558 };
1559 /*
1560 ::ns_crypto::md hkdf -digest sha256 -salt foo -secret var -info "Content-Encoding: auth" 10
1561
1562 # test case 1 from RFC 5869
1563 ::ns_crypto::md hkdf -digest sha256 \
1564 -salt [binary format H* 000102030405060708090a0b0c] \
1565 -secret [binary format H* 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b] \
1566 -info [binary format H* f0f1f2f3f4f5f6f7f8f9] \
1567 42
1568 3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865
1569
1570 # test case 3 from RFC 5869
1571 ::ns_crypto::md hkdf -digest sha256 \
1572 -salt "" \
1573 -secret [binary format H* 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b] \
1574 -info "" \
1575 42
1576 8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8
1577
1578 # test case 4 from RFC 5869
1579 ::ns_crypto::md hkdf -digest sha1 \
1580 -salt [binary format H* 000102030405060708090a0b0c] \
1581 -secret [binary format H* 0b0b0b0b0b0b0b0b0b0b0b] \
1582 -info [binary format H* f0f1f2f3f4f5f6f7f8f9] \
1583 42
1584 085a01ea1b10f36933068b56efa5ad81a4f14b822f5b091568a9cdd4f155fda2c22e422478d305f3f896
1585
1586 */
1587 if (Ns_ParseObjv(lopts, args, interp, 2, objc, objv) != NS_OK) {
1588 result = TCL_ERROR1;
1589
1590 } else if (saltObj == NULL((void*)0)) {
1591 Ns_TclPrintfResult(interp, "no -salt specified");
1592 result = TCL_ERROR1;
1593
1594 } else if (secretObj == NULL((void*)0)) {
1595 Ns_TclPrintfResult(interp, "no -secret specified");
1596 result = TCL_ERROR1;
1597
1598 } else if (infoObj == NULL((void*)0)) {
1599 Ns_TclPrintfResult(interp, "no -info specified");
1600 result = TCL_ERROR1;
1601
1602 } else {
1603 const EVP_MD *md;
1604 EVP_PKEY_CTX *pctx = NULL((void*)0);
1605
1606 /*
1607 * Look up the Message Digest from OpenSSL
1608 */
1609 result = GetDigest(interp, digestName, &md);
1610
1611 if (result != TCL_ERROR1) {
1612 pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF1036, NULL((void*)0));
1613 if (pctx == NULL((void*)0)) {
1614 Ns_TclPrintfResult(interp, "could not obtain context HKDF");
1615 result = TCL_ERROR1;
1616 }
1617 }
1618 if (result != TCL_ERROR1 && (EVP_PKEY_derive_init(pctx) <= 0)) {
1619 Ns_TclPrintfResult(interp, "could not initialize for derivation");
1620 result = TCL_ERROR1;
1621 }
1622 if (result != TCL_ERROR1 && (EVP_PKEY_CTX_set_hkdf_md(pctx, md) <= 0)) {
1623 Ns_TclPrintfResult(interp, "could not set digest algorithm");
1624 result = TCL_ERROR1;
1625 }
1626 if (result != TCL_ERROR1) {
1627 const unsigned char *infoString, *saltString, *secretString;
1628 unsigned char *keyString;
1629 Tcl_DString infoDs, saltDs, secretDs;
1630 int infoLength, saltLength, secretLength;
1631 size_t outSize = (size_t)outLength;
1632
1633 /*
1634 * All input parameters are valid, get key and data.
1635 */
1636 Tcl_DStringInit(&saltDs);
1637 Tcl_DStringInit(&secretDs);
1638 Tcl_DStringInit(&infoDs);
1639 keyString = ns_malloc((size_t)outLength);
1640
1641 saltString = Ns_GetBinaryString(saltObj, isBinary == 1, &saltLength, &saltDs);
1642 secretString = Ns_GetBinaryString(secretObj, isBinary == 1, &secretLength, &secretDs);
1643 infoString = Ns_GetBinaryString(infoObj, isBinary == 1, &infoLength, &infoDs);
1644
1645 // hexPrint("salt ", saltString, (size_t)saltLength);
1646 // hexPrint("secret", secretString, (size_t)secretLength);
1647 // hexPrint("info ", infoString, (size_t)infoLength);
1648
1649 if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, saltString, saltLength) <= 0) {
1650 Ns_TclPrintfResult(interp, "could not set salt");
1651 result = TCL_ERROR1;
1652 } else if (EVP_PKEY_CTX_set1_hkdf_key(pctx, secretString, secretLength) <= 0) {
1653 Ns_TclPrintfResult(interp, "could not set secret");
1654 result = TCL_ERROR1;
1655 } else if (EVP_PKEY_CTX_add1_hkdf_info(pctx, infoString, infoLength) <= 0) {
1656 Ns_TclPrintfResult(interp, "could not set info");
1657 result = TCL_ERROR1;
1658 } else if (EVP_PKEY_derive(pctx, keyString, &outSize) <= 0) {
1659 Ns_TclPrintfResult(interp, "could not obtain derived key");
1660 result = TCL_ERROR1;
1661 }
1662
1663 if (result == TCL_OK0) {
1664 /*
1665 * Convert the result to the output format and set the interp
1666 * result.
1667 */
1668 Tcl_SetObjResult(interp, EncodedObj(keyString, outSize, NULL((void*)0), encoding));
1669 }
1670
1671 /*
1672 * Clean up.
1673 */
1674 ns_free((char*)keyString);
1675 Tcl_DStringFree(&saltDs);
1676 Tcl_DStringFree(&secretDs);
1677 Tcl_DStringFree(&infoDs);
1678 }
1679
1680 EVP_PKEY_CTX_free(pctx);
1681 }
1682 return result;
1683}
1684# endif
1685
1686/*
1687 *----------------------------------------------------------------------
1688 *
1689 * NsTclCryptoMdObjCmd --
1690 *
1691 * Implements "ns_crypto::md" with subcommands for Hash-based
1692 * message authentication codes.
1693 *
1694 * Results:
1695 * NS_OK
1696 *
1697 * Side effects:
1698 * Tcl result is set to a string value.
1699 *
1700 *----------------------------------------------------------------------
1701 */
1702int
1703NsTclCryptoMdObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1704{
1705 const Ns_SubCmdSpec subcmds[] = {
1706 {"string", CryptoMdStringObjCmd},
1707 {"new", CryptoMdNewObjCmd},
1708 {"add", CryptoMdAddObjCmd},
1709 {"get", CryptoMdGetObjCmd},
1710 {"free", CryptoMdFreeObjCmd},
1711# ifndef OPENSSL_NO_EC
1712 {"vapidsign", CryptoMdVapidSignObjCmd},
1713# endif
1714# ifdef HAVE_OPENSSL_HKDF
1715 {"hkdf", CryptoMdHkdfObjCmd},
1716# endif
1717 {NULL((void*)0), NULL((void*)0)}
1718 };
1719
1720 return Ns_SubcmdObjv(subcmds, clientData, interp, objc, objv);
1721}
1722
1723
1724# ifdef HAVE_OPENSSL_31
1725/*
1726 * We could provide SCRYPT as well via EVP_PKEY_CTX provided in
1727 * OpenSSL 1.1.1:
1728 *
1729 * https://www.openssl.org/docs/man1.1.1/man7/scrypt.html
1730 *
1731 * but the future interface is the OpenSSL 3.* way, via
1732 * EVP_KDF_fetch() + OSSL_PARAM_*. Not sure, whether LibreSSL and
1733 * friends will follow.
1734 */
1735/*
1736 *----------------------------------------------------------------------
1737 *
1738 * NsTclCryptoScryptObjCmd --
1739 *
1740 * Compute a "password hash" using the scrypt Password-Based
1741 * Key Derivation Function (RFC 7914) as defined in OpenSSL 3.
1742 *
1743 * Implements "ns_crypto::scrypt".
1744 *
1745 * Results:
1746 * Tcl result code
1747 *
1748 * Side effects:
1749 * None
1750 *
1751 *----------------------------------------------------------------------
1752 */
1753int
1754NsTclCryptoScryptObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1755{
1756 int result, isBinary = 0, nValue = 1024, rValue = 8, pValue = 16;
1757 Tcl_Obj *saltObj = NULL((void*)0), *secretObj = NULL((void*)0);
1758 Ns_BinaryEncoding encoding = RESULT_ENCODING_HEX;
1759 Ns_ObjvSpec lopts[] = {
1760 {"-binary", Ns_ObjvBool, &isBinary, INT2PTR(NS_TRUE)((void *)(intptr_t)(1))},
1761 {"-salt", Ns_ObjvObj, &saltObj, NULL((void*)0)},
1762 {"-secret", Ns_ObjvObj, &secretObj, NULL((void*)0)},
1763 {"-n", Ns_ObjvInt, &nValue, &posIntRange1},
1764 {"-p", Ns_ObjvInt, &pValue, &posIntRange1},
1765 {"-r", Ns_ObjvInt, &rValue, &posIntRange1},
1766 {"-encoding", Ns_ObjvIndex, &encoding, binaryencodings},
1767 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
1768 };
1769 Ns_ObjvSpec args[] = {
1770 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
1771 };
1772 /*
1773 ############################################################################
1774 # Test Case 1: RFC 7914 (example 2 in sect 12)
1775 ############################################################################
1776 ::ns_crypto::scrypt -secret "password" -salt NaCl -n 1024 -r 8 -p 16
1777
1778 fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b373162
1779 2eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640
1780
1781 % time {::ns_crypto::scrypt -secret "password" -salt NaCl -n 1024 -r 8 -p 16}
1782 42011 microseconds per iteration
1783
1784 ############################################################################
1785 # Test Case 2: RFC 7914 (example 3 in sect 12)
1786 ############################################################################
1787 ::ns_crypto::scrypt -secret "pleaseletmein" -salt SodiumChloride -n 16384 -r 8 -p 1
1788
1789 7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2
1790 d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887
1791
1792 % time {::ns_crypto::scrypt -secret "pleaseletmein" -salt SodiumChloride -n 16384 -r 8 -p 1}
1793 47901 microseconds per iteration
1794
1795 ############################################################################
1796 # Test Case 3: RFC 7914 (example 4 in sect 12)
1797 ############################################################################
1798 ::ns_crypto::scrypt -secret "pleaseletmein" -salt SodiumChloride -n 1048576 -r 8 -p 1
1799
1800 2101cb9b6a511aaeaddbbe09cf70f881ec568d574a2ffd4dabe5ee9820adaa47
1801 8e56fd8f4ba5d09ffa1c6d927c40f4c337304049e8a952fbcbf45c6fa77a41a4
1802
1803 % time {::ns_crypto::scrypt -secret "pleaseletmein" -salt SodiumChloride -n 1048576 -r 8 -p 1}
1804 3095741 microseconds per iteration
1805 */
1806
1807 if (Ns_ParseObjv(lopts, args, interp, 1, objc, objv) != NS_OK) {
1808 result = TCL_ERROR1;
1809
1810 } else if (saltObj == NULL((void*)0)) {
1811 Ns_TclPrintfResult(interp, "no -salt specified");
1812 result = TCL_ERROR1;
1813
1814 } else if (secretObj == NULL((void*)0)) {
1815 Ns_TclPrintfResult(interp, "no -secret specified");
1816 result = TCL_ERROR1;
1817
1818 } else {
1819 EVP_KDF *kdf;
1820 EVP_KDF_CTX *kctx;
1821 unsigned char out[64];
1822 Tcl_DString saltDs, secretDs;
1823 int saltLength, secretLength;
1824 const unsigned char *saltString, *secretString;
1825 OSSL_PARAM params[6], *p = params;
1826 uint64_t nValueSSL = (uint64_t)nValue;
1827 uint32_t pValueSSL = (uint32_t)pValue;
1828 uint32_t rValueSSL = (uint32_t)rValue;
1829
1830 /*
1831 * All input parameters are valid, get key and data.
1832 */
1833 Tcl_DStringInit(&saltDs);
1834 Tcl_DStringInit(&secretDs);
1835 //keyString = ns_malloc((size_t)outLength);
1836
1837 saltString = Ns_GetBinaryString(saltObj, isBinary == 1, &saltLength, &saltDs);
1838 secretString = Ns_GetBinaryString(secretObj, isBinary == 1, &secretLength, &secretDs);
1839
1840 kdf = EVP_KDF_fetch(NULL((void*)0), "SCRYPT", NULL((void*)0));
1841 kctx = EVP_KDF_CTX_new(kdf);
1842 EVP_KDF_free(kdf);
1843
1844 *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_PASSWORD"pass",
1845 (void*)secretString, (size_t)secretLength);
1846 *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT"salt",
1847 (void*)saltString, (size_t)saltLength);
1848 *p++ = OSSL_PARAM_construct_uint64(OSSL_KDF_PARAM_SCRYPT_N"n", &nValueSSL);
1849 *p++ = OSSL_PARAM_construct_uint32(OSSL_KDF_PARAM_SCRYPT_R"r", &pValueSSL);
1850 *p++ = OSSL_PARAM_construct_uint32(OSSL_KDF_PARAM_SCRYPT_P"p", &rValueSSL);
1851 *p = OSSL_PARAM_construct_end();
1852
1853 if (EVP_KDF_CTX_set_params(kctx, params) <= 0) {
1854 Ns_TclPrintfResult(interp, "could not set parameters");
1855 result = TCL_ERROR1;
1856
1857 } else if (EVP_KDF_derive(kctx, out, sizeof(out), NULL((void*)0)) <= 0) {
1858 Ns_TclPrintfResult(interp, "could not derive key");
1859 result = TCL_ERROR1;
1860
1861 } else {
1862 /*
1863 * Convert the result to the output format and set the interp
1864 * result.
1865 */
1866 Tcl_SetObjResult(interp, EncodedObj(out, sizeof(out), NULL((void*)0), encoding));
1867 result = TCL_OK0;
1868 }
1869
1870 /*
1871 * Clean up.
1872 */
1873 Tcl_DStringFree(&saltDs);
1874 Tcl_DStringFree(&secretDs);
1875
1876 EVP_KDF_CTX_free(kctx);
1877 }
1878
1879 return result;
1880}
1881# else
1882int
1883NsTclCryptoScryptObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int UNUSED(objc)UNUSED_objc __attribute__((__unused__)), Tcl_Obj *const* UNUSED(objv)UNUSED_objv __attribute__((__unused__)))
1884{
1885 Ns_TclPrintfResult(interp, "Command requires support for OpenSSL 3.0 built into NaviServer");
1886 return TCL_ERROR1;
1887}
1888# endif
1889
1890
1891/*
1892 *----------------------------------------------------------------------
1893 *
1894 * NsTclCryptoPbkdf2hmacObjCmd --
1895 *
1896 * Compute a password hash using PBKDF2 (Password-Based Key
1897 * Derivation Function 2). This function is used to reduce
1898 * vulnerabilities of brute-force attacks against password hashes
1899 * and is used e.g. in SCRAM (Salted Challenge Response
1900 * Authentication Mechanism).
1901 *
1902 * The hash function of SCRAM is PBKDF2 [RFC2898] with HMAC() as the
1903 * pseudorandom function (PRF) and with dkLen == output length of
1904 * HMAC() == output length of H().
1905 *
1906 * Implements "ns_crypto::pbkdf2_hmac".
1907 *
1908 * Results:
1909 * Tcl result code
1910 *
1911 * Side effects:
1912 * None
1913 *
1914 *----------------------------------------------------------------------
1915 */
1916int
1917NsTclCryptoPbkdf2hmacObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1918{
1919 int result, isBinary = 0, iter = 4096, dkLength = -1;
1920 Tcl_Obj *saltObj = NULL((void*)0), *secretObj = NULL((void*)0);
1921 char *digestName = (char *)"sha256";
1922 Ns_BinaryEncoding encoding = RESULT_ENCODING_HEX;
1923 Ns_ObjvSpec opts[] = {
1924 {"-binary", Ns_ObjvBool, &isBinary, INT2PTR(NS_TRUE)((void *)(intptr_t)(1))},
1925 {"-digest", Ns_ObjvString, &digestName, NULL((void*)0)},
1926 {"-dklen", Ns_ObjvInt, &dkLength, &posIntRange1},
1927 {"-iterations", Ns_ObjvInt, &iter, &posIntRange1},
1928 {"-salt", Ns_ObjvObj, &saltObj, NULL((void*)0)},
1929 {"-secret", Ns_ObjvObj, &secretObj, NULL((void*)0)},
1930 {"-encoding", Ns_ObjvIndex, &encoding, binaryencodings},
1931 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
1932 };
1933 /*
1934 ############################################################################
1935 # Test Cases for pbkdf2-hmac-sha1 based on RFC 6070
1936 # (PKCS #5_ Password-Based Key Derivation Function 2 (PBKDF2) Test Vectors)
1937 ############################################################################
1938 ::ns_crypto::pbkdf2_hmac -secret "password" -iterations 1 -salt "salt" -digest sha1
1939 0c60c80f961f0e71f3a9b524af6012062fe037a6
1940
1941 ::ns_crypto::pbkdf2_hmac -secret "password" -iterations 2 -salt "salt" -digest sha1
1942 ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957
1943
1944 ::ns_crypto::pbkdf2_hmac -secret "password" -iterations 4096 -salt "salt" -digest sha1
1945 4b007901b765489abead49d926f721d065a429c1
1946
1947 ::ns_crypto::pbkdf2_hmac -secret "password" -iterations 16777216 -salt "salt" -digest sha1
1948 eefe3d61cd4da4e4e9945b3d6ba2158c2634e984
1949
1950 ::ns_crypto::pbkdf2_hmac -secret "pass\0word" -iterations 4096 -salt "sa\0lt" -digest sha1 -dklen 16
1951 56fa6aa75548099dcc37d7f03425e0c3
1952
1953
1954 ############################################################################
1955 # Test Cases for pbkdf2-hmac-sha2 from
1956 * https://stackoverflow.com/questions/5130513/pbkdf2-hmac-sha2-test-vectors
1957 ############################################################################
1958
1959 ::ns_crypto::pbkdf2_hmac -secret "password" -iterations 1 -salt "salt"
1960 120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b
1961
1962 ::ns_crypto::pbkdf2_hmac -secret "password" -iterations 2 -salt "salt"
1963 ae4d0c95af6b46d32d0adff928f06dd02a303f8ef3c251dfd6e2d85a95474c43
1964
1965 ::ns_crypto::pbkdf2_hmac -secret "password" -iterations 4096 -salt "salt"
1966 c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a
1967
1968 ::ns_crypto::pbkdf2_hmac -secret "pass\0word" -iterations 4096 -salt "sa\0lt" -dklen 16
1969 89b69d0516f829893c696226650a8687
1970
1971 ############################################################################
1972 # Performance considerations
1973 ############################################################################
1974
1975 # PostgreSQL 10 uses 4096 (very low value)
1976 time {::ns_crypto::pbkdf2_hmac -secret "pass\0word" -iterations 4096 -salt "sa\0lt" -dklen 16}
1977 4172 microseconds per iteration
1978
1979 # Recommendation from RFC 7677
1980 time {::ns_crypto::pbkdf2_hmac -secret "pass\0word" -iterations 15000 -salt "sa\0lt" -dklen 16}
1981 16027 microseconds per iteration
1982
1983 # Comparison with higher value
1984 time {::ns_crypto::pbkdf2_hmac -secret "pass\0word" -iterations 65536 -salt "sa\0lt" -dklen 16}
1985 65891 microseconds per iteration
1986 */
1987
1988 if (Ns_ParseObjv(opts, NULL((void*)0), interp, 1, objc, objv) != NS_OK) {
1989 result = TCL_ERROR1;
1990
1991 } else if (saltObj == NULL((void*)0)) {
1992 Ns_TclPrintfResult(interp, "no -salt specified");
1993 result = TCL_ERROR1;
1994
1995 } else if (secretObj == NULL((void*)0)) {
1996 Ns_TclPrintfResult(interp, "no -secret specified");
1997 result = TCL_ERROR1;
1998
1999 } else {
2000 const EVP_MD *md;
2001
2002 /*
2003 * Look up the Message Digest from OpenSSL
2004 */
2005 result = GetDigest(interp, digestName, &md);
2006 if (result == TCL_OK0) {
2007 Tcl_DString saltDs, secretDs;
2008 int saltLength, secretLength;
2009 const unsigned char *saltString, *secretString;
2010 unsigned char *out = NULL((void*)0);
2011
2012 /*
2013 * All input parameters are valid, get salt and secret
2014 */
2015 Tcl_DStringInit(&saltDs);
2016 Tcl_DStringInit(&secretDs);
2017 if (dkLength == -1) {
2018 dkLength = EVP_MD_sizeEVP_MD_get_size(md);
2019 }
2020 out = ns_malloc((size_t)dkLength);
2021
2022 saltString = Ns_GetBinaryString(saltObj, isBinary == 1, &saltLength, &saltDs);
2023 secretString = Ns_GetBinaryString(secretObj, isBinary == 1, &secretLength, &secretDs);
2024
2025 if (PKCS5_PBKDF2_HMAC((const char *)secretString, secretLength,
2026 saltString, saltLength,
2027 iter, md,
2028 dkLength, out) == 1) {
2029 Tcl_SetObjResult(interp, EncodedObj(out, (size_t)dkLength, NULL((void*)0), encoding));
2030 result = TCL_OK0;
2031 } else {
2032 Ns_TclPrintfResult(interp, "could not derive key");
2033 result = TCL_ERROR1;
2034 }
2035 if (out != NULL((void*)0)) {
2036 ns_free(out);
2037 }
2038 }
2039 }
2040 return result;
2041}
2042
2043
2044
2045# ifndef OPENSSL_NO_EC
2046# ifdef HAVE_OPENSSL_EC_PRIV2OCT
2047/*
2048 *----------------------------------------------------------------------
2049 *
2050 * CryptoEckeyPrivObjCmd -- Subcommand of NsTclCryptoEckeyObjCmd
2051 *
2052 * Implements "ns_crypto::eckey priv". Subcommand to obtain the
2053 * private key in various encodings from an elliptic curves PEM
2054 * file.
2055 *
2056 * Results:
2057 * Tcl Result Code.
2058 *
2059 * Side effects:
2060 * None
2061 *
2062 *----------------------------------------------------------------------
2063 */
2064static int
2065CryptoEckeyPrivObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
2066{
2067 int result;
2068 char *pemFile = NULL((void*)0),
2069 *passPhrase = (char *)NS_EMPTY_STRING;
2070 Ns_BinaryEncoding encoding = RESULT_ENCODING_HEX;
2071
2072 Ns_ObjvSpec lopts[] = {
2073 {"-encoding", Ns_ObjvIndex, &encoding, binaryencodings},
2074 {"-passphrase", Ns_ObjvString, &passPhrase, NULL((void*)0)},
2075 {"-pem", Ns_ObjvString, &pemFile, NULL((void*)0)},
2076 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
2077 };
2078 /*
2079 ns_crypto::eckey priv -pem /usr/local/ns/modules/vapid/prime256v1_key.pem -encoding base64url
2080 pwLi7T1QqrgTiNBFBLUcndjNxzx_vZiKuCcvapwjQlM
2081 */
2082
2083 if (Ns_ParseObjv(lopts, NULL((void*)0), interp, 2, objc, objv) != NS_OK) {
2084 result = TCL_ERROR1;
2085
2086 } else if (pemFile == NULL((void*)0)) {
2087 Ns_TclPrintfResult(interp, "no pem file specified");
2088 result = TCL_ERROR1;
2089
2090 } else {
2091 EVP_PKEY *pkey;
2092 EC_KEY *eckey = NULL((void*)0);
2093
2094 pkey = GetPkeyFromPem(interp, pemFile, passPhrase, NS_TRUE1);
2095 if (pkey == NULL((void*)0)) {
2096 /*
2097 * GetPkeyFromPem handles error message
2098 */
2099 result = TCL_ERROR1;
2100 } else {
2101 eckey = EVP_PKEY_get1_EC_KEY(pkey);
2102 if (eckey == NULL((void*)0)) {
2103 EVP_PKEY_free(pkey);
2104 Ns_TclPrintfResult(interp, "no valid EC key in specified pem file");
2105 result = TCL_ERROR1;
2106 } else {
2107 result = TCL_OK0;
2108 }
2109 }
2110 if (result != TCL_ERROR1) {
2111 Tcl_DString ds;
2112 size_t octLength = EC_KEY_priv2oct(eckey, NULL((void*)0), 0);
2113
2114 Tcl_DStringInit(&ds);
2115 Tcl_DStringSetLength(&ds, (int)octLength);
2116 octLength = EC_KEY_priv2oct(eckey, (unsigned char *)ds.string, octLength);
2117 Tcl_SetObjResult(interp, EncodedObj((unsigned char *)ds.string, octLength, NULL((void*)0), encoding));
2118
2119 /*
2120 * Clean up.
2121 */
2122 EVP_PKEY_free(pkey);
2123 Tcl_DStringFree(&ds);
2124 }
2125 }
2126
2127 return result;
2128}
2129# endif
2130
2131static void
2132SetResultFromEC_POINT(
2133 Tcl_Interp *interp,
2134 Tcl_DString *dsPtr,
2135 EC_KEY *eckey,
2136 const EC_POINT *ecpoint,
2137 BN_CTX *bn_ctx,
2138 Ns_BinaryEncoding encoding)
2139{
2140 size_t octLength = EC_POINT_point2oct(EC_KEY_get0_group(eckey), ecpoint,
2141 POINT_CONVERSION_UNCOMPRESSED, NULL((void*)0), 0, NULL((void*)0));
2142
2143 Ns_Log(Debug, "import: octet length %" PRIuz"zu", octLength);
2144
2145 Tcl_DStringSetLength(dsPtr, (int)octLength);
2146 octLength = EC_POINT_point2oct(EC_KEY_get0_group(eckey), ecpoint, POINT_CONVERSION_UNCOMPRESSED,
2147 (unsigned char *)dsPtr->string, octLength, bn_ctx);
2148 Tcl_SetObjResult(interp, EncodedObj((unsigned char *)dsPtr->string, octLength, NULL((void*)0), encoding));
2149}
2150
2151
2152/*
2153 *----------------------------------------------------------------------
2154 *
2155 * CryptoEckeyPubObjCmd -- Subcommand of NsTclCryptoEckeyObjCmd
2156 *
2157 * Implements "ns_crypto::eckey pub". Subcommand to obtain the
2158 * public key in various encodings from an elliptic curves PEM
2159 * file.
2160 *
2161 * Results:
2162 * Tcl Result Code.
2163 *
2164 * Side effects:
2165 * None
2166 *
2167 *----------------------------------------------------------------------
2168 */
2169static int
2170CryptoEckeyPubObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
2171{
2172 int result;
2173 char *pemFile = NULL((void*)0),
2174 *passPhrase = (char *)NS_EMPTY_STRING;
2175 Ns_BinaryEncoding encoding = RESULT_ENCODING_HEX;
2176
2177 Ns_ObjvSpec lopts[] = {
2178 {"-encoding", Ns_ObjvIndex, &encoding, binaryencodings},
2179 {"-passphrase", Ns_ObjvString, &passPhrase, NULL((void*)0)},
2180 {"-pem", Ns_ObjvString, &pemFile, NULL((void*)0)},
2181 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
2182 };
2183 /*
2184 ns_crypto::eckey pub -pem /usr/local/ns/modules/vapid/prime256v1_key.pem -encoding base64url
2185 BBGNrqwUWW4dedpYHZnoS8hzZZNMmO-i3nYButngeZ5KtJ73ZaGa00BZxke2h2RCRGm-6Rroni8tDPR_RMgNib0
2186 */
2187
2188 if (Ns_ParseObjv(lopts, NULL((void*)0), interp, 2, objc, objv) != NS_OK) {
2189 result = TCL_ERROR1;
2190
2191 } else if (pemFile == NULL((void*)0)) {
2192 Ns_TclPrintfResult(interp, "no pem file specified");
2193 result = TCL_ERROR1;
2194
2195 } else {
2196 EC_KEY *eckey;
2197 const EC_POINT *ecpoint = NULL((void*)0);
2198
2199 /*
2200 * The .pem file does not have a separate pub-key included,
2201 * but we get the pub-key grom the priv-key in form of an
2202 * EC_POINT.
2203 */
2204 eckey = GetEckeyFromPem(interp, pemFile, passPhrase, NS_TRUE1);
2205 if (eckey != NULL((void*)0)) {
2206 ecpoint = EC_KEY_get0_public_key(eckey);
2207 if (ecpoint == NULL((void*)0)) {
2208 Ns_TclPrintfResult(interp, "no valid EC key in specified pem file");
2209 result = TCL_ERROR1;
2210 } else {
2211 result = TCL_OK0;
2212 }
2213 } else {
2214 result = TCL_ERROR1;
2215 }
2216 if (result != TCL_ERROR1) {
2217 Tcl_DString ds;
2218 BN_CTX *bn_ctx = BN_CTX_new();
2219
2220 Tcl_DStringInit(&ds);
2221 SetResultFromEC_POINT(interp, &ds, eckey, ecpoint, bn_ctx, encoding);
2222 BN_CTX_free(bn_ctx);
2223 Tcl_DStringFree(&ds);
2224 }
2225 if (eckey != NULL((void*)0)) {
2226 EC_KEY_free(eckey);
2227 }
2228 }
2229
2230 return result;
2231}
2232
2233
2234# ifdef HAVE_OPENSSL_EC_PRIV2OCT
2235/*
2236 *----------------------------------------------------------------------
2237 *
2238 * CryptoEckeyImportObjCmd -- Subcommand of NsTclCryptoEckeyObjCmd
2239 *
2240 * Implements "ns_crypto::eckey import". Subcommand to import a
2241 * public key into the OpenSSL EC_KEY structure in order to
2242 * apply conversions of it. Can be most likely dropped.
2243 *
2244 * Results:
2245 * Tcl Result Code.
2246 *
2247 * Side effects:
2248 * None
2249 *
2250 *----------------------------------------------------------------------
2251 */
2252static int
2253CryptoEckeyImportObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
2254{
2255 int result, isBinary = 0;
2256 Tcl_Obj *importObj = NULL((void*)0);
2257 Ns_BinaryEncoding encoding = RESULT_ENCODING_HEX;
2258 Ns_ObjvSpec lopts[] = {
2259 {"-binary", Ns_ObjvBool, &isBinary, INT2PTR(NS_TRUE)((void *)(intptr_t)(1))},
2260 {"-string", Ns_ObjvObj, &importObj, NULL((void*)0)},
2261 {"-encoding", Ns_ObjvIndex, &encoding, binaryencodings},
2262 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
2263 };
2264 /*
2265 ns_crypto::eckey import -encoding base64url \
2266 -string [ns_base64urldecode BBGNrqwUWW4dedpYHZnoS8hzZZNMmO-i3nYButngeZ5KtJ73ZaGa00BZxke2h2RCRGm-6Rroni8tDPR_RMgNib0]
2267
2268 ns_crypto::eckey import -encoding base64url \
2269 -string [ns_base64urldecode BDwwYm4O5dZG9SO6Vaz168iDLGWMmitkj5LFvunvMfgmI2fZdAEaiHTDfKR0fvr0D3V56cSGSeUwP0xNdrXho5k]
2270 */
2271
2272 if (Ns_ParseObjv(lopts, NULL((void*)0), interp, 2, objc, objv) != NS_OK) {
2273 result = TCL_ERROR1;
2274
2275 } else if (importObj == NULL((void*)0)) {
2276 Ns_TclPrintfResult(interp, "no import string specified");
2277 result = TCL_ERROR1;
2278
2279 } else {
2280 int rawKeyLength;
2281 const unsigned char *rawKeyString;
2282 EC_KEY *eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1415);
2283 Tcl_DString keyDs;
2284
2285 Tcl_DStringInit(&keyDs);
2286 rawKeyString = Ns_GetBinaryString(importObj, isBinary == 1, &rawKeyLength, &keyDs);
2287
2288 Ns_Log(Debug, "import: raw key length %d", rawKeyLength);
2289 hexPrint("key", rawKeyString, (size_t)rawKeyLength);
2290
2291 if (EC_KEY_oct2key(eckey, rawKeyString, (size_t)rawKeyLength, NULL((void*)0)) != 1) {
2292 Ns_TclPrintfResult(interp, "could not import string to ec key");
2293 result = TCL_ERROR1;
2294 } else {
2295 Tcl_DString ds;
2296 const EC_POINT *ecpoint = EC_KEY_get0_public_key(eckey);
2297
2298 Tcl_DStringInit(&ds);
2299 if (ecpoint == NULL((void*)0)) {
2300 Ns_TclPrintfResult(interp, "no valid public key");
2301 result = TCL_ERROR1;
2302 } else {
2303 BN_CTX *bn_ctx = BN_CTX_new();
2304
2305 SetResultFromEC_POINT(interp, &ds, eckey, ecpoint, bn_ctx, encoding);
2306 BN_CTX_free(bn_ctx);
2307
2308 result = TCL_OK0;
2309 }
2310 Tcl_DStringFree(&ds);
2311 }
2312
2313 /*
2314 * Clean up.
2315 */
2316 if (eckey != NULL((void*)0)) {
2317 EC_KEY_free(eckey);
2318 }
2319 Tcl_DStringFree(&keyDs);
2320 }
2321
2322 return result;
2323}
2324# endif
2325
2326
2327/*
2328 *----------------------------------------------------------------------
2329 *
2330 * CryptoEckeyGenerateObjCmd -- Subcommand of NsTclCryptoEckeyObjCmd
2331 *
2332 * Implements "ns_crypto::eckey generate". Subcommand to
2333 * generate an EC pemfile without the need of an external
2334 * command.
2335 *
2336 * Results:
2337 * Tcl Result Code.
2338 *
2339 * Side effects:
2340 * None
2341 *
2342 *----------------------------------------------------------------------
2343 */
2344static int
2345CryptoEckeyGenerateObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
2346{
2347 int result, nid;
2348 char *curvenameString = (char *)"prime256v1", *pemFileName = NULL((void*)0);
2349 Ns_ObjvSpec lopts[] = {
2350 {"-name", Ns_ObjvString, &curvenameString, NULL((void*)0)},
2351 {"-pem", Ns_ObjvString, &pemFileName, NULL((void*)0)},
2352 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
2353 };
2354 /*
2355 ns_crypto::eckey generate -name prime256v1 -pem /tmp/foo.pem
2356 */
2357
2358 if (Ns_ParseObjv(lopts, NULL((void*)0), interp, 2, objc, objv) != NS_OK) {
2359 result = TCL_ERROR1;
2360
2361 } else if (GetCurve(interp, curvenameString, &nid) == TCL_ERROR1) {
2362 /*
2363 * Function cares about error message
2364 */
2365 result = TCL_ERROR1;
2366
2367 } else if (pemFileName == NULL((void*)0)) {
2368 Ns_TclPrintfResult(interp, "no pem filename provided");
2369 result = TCL_ERROR1;
2370
2371 } else {
2372 EC_KEY *eckey;
2373
2374 eckey = EC_KEY_new_by_curve_name(nid);
2375 if (eckey == NULL((void*)0)) {
2376 Ns_TclPrintfResult(interp, "could not create ec key");
2377 result = TCL_ERROR1;
2378
2379 } else if (EC_KEY_generate_key(eckey) == 0) {
2380 Ns_TclPrintfResult(interp, "could not generate ec key");
2381 result = TCL_ERROR1;
2382
2383 } else {
2384 BIO *bio;
2385
2386 bio = BIO_new_file(pemFileName, "w");
2387 if (bio == NULL((void*)0)) {
2388 Ns_TclPrintfResult(interp, "could not open pem-file '%s' for writing", pemFileName);
2389 result = TCL_ERROR1;
2390 } else {
2391 (void) PEM_write_bio_ECPrivateKey(bio, eckey, NULL((void*)0),
2392 NULL((void*)0), 0, NULL((void*)0), NULL((void*)0));
2393 BIO_free(bio);
2394 result = TCL_OK0;
2395 }
2396 EC_KEY_free(eckey);
2397 }
2398 }
2399
2400 return result;
2401}
2402
2403# ifndef HAVE_OPENSSL_PRE_1_1
2404/*
2405 *----------------------------------------------------------------------
2406 *
2407 * CryptoEckeySharedsecretObjCmd -- Subcommand of NsTclCryptoEckeyObjCmd
2408 *
2409 * Implements "ns_crypto::eckey sharedsecret". Subcommand to
2410 * generate a shared secret based on the private key from the
2411 * .pem file and the provided public key.
2412 *
2413 * Results:
2414 * Tcl Result Code.
2415 *
2416 * Side effects:
2417 * None
2418 *
2419 *----------------------------------------------------------------------
2420 */
2421static int
2422CryptoEckeySharedsecretObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
2423{
2424 int result, isBinary = 0;
2425 char *pemFileName = NULL((void*)0),
2426 *passPhrase = (char *)NS_EMPTY_STRING;
2427 Tcl_Obj *pubkeyObj = NULL((void*)0);
2428 Ns_BinaryEncoding encoding = RESULT_ENCODING_HEX;
2429 EC_KEY *eckey = NULL((void*)0);
2430
2431 Ns_ObjvSpec lopts[] = {
2432 {"-binary", Ns_ObjvBool, &isBinary, INT2PTR(NS_TRUE)((void *)(intptr_t)(1))},
2433 {"-encoding", Ns_ObjvIndex, &encoding, binaryencodings},
2434 {"-passphrase", Ns_ObjvString, &passPhrase, NULL((void*)0)},
2435 {"-pem", Ns_ObjvString, &pemFileName, NULL((void*)0)},
2436 {"--", Ns_ObjvBreak, NULL((void*)0), NULL((void*)0)},
2437 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
2438 };
2439 Ns_ObjvSpec args[] = {
2440 {"pubkey", Ns_ObjvObj, &pubkeyObj, NULL((void*)0)},
2441 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
2442 };
2443
2444 /*
2445 ns_crypto::eckey sharedsecret -pem /usr/local/ns/modules/vapid/prime256v1_key.pem \
2446 [ns_base64urldecode BBGNrqwUWW4dedpYHZnoS8hzZZNMmO-i3nYButngeZ5KtJ73ZaGa00BZxke2h2RCRGm-6Rroni8tDPR_RMgNib0]
2447 */
2448
2449 if (Ns_ParseObjv(lopts, args, interp, 2, objc, objv) != NS_OK) {
2450 result = TCL_ERROR1;
2451
2452 } else if (pemFileName == NULL((void*)0)) {
2453 Ns_TclPrintfResult(interp, "no pem file specified");
2454 result = TCL_ERROR1;
2455
2456 } else {
2457
2458 eckey = GetEckeyFromPem(interp, pemFileName, passPhrase, NS_TRUE1);
2459 if (eckey == NULL((void*)0)) {
2460 /*
2461 * GetEckeyFromPem handles error message
2462 */
2463 result = TCL_ERROR1;
2464 } else {
2465 result = TCL_OK0;
2466 }
2467 }
2468
2469 if (result == TCL_OK0) {
2470 int pubkeyLength;
2471 const unsigned char *pubkeyString;
2472 Tcl_DString importDs;
2473 const EC_GROUP *group;
2474 EC_POINT *pubKeyPt;
2475 BN_CTX *bn_ctx = BN_CTX_new();
2476
2477 /*
2478 * Ingredients:
2479 * eckey : private key, from PEM, EC_KEY (currently redundant)
2480 * pubkeyString: public key of peer as octet string
2481 */
2482
2483 Tcl_DStringInit(&importDs);
2484 pubkeyString = Ns_GetBinaryString(pubkeyObj, isBinary == 1, &pubkeyLength, &importDs);
2485
2486 /*
2487 ns_crypto::eckey generate -name prime256v1 -pem /tmp/prime256v1_key.pem
2488 ns_crypto::eckey sharedsecret -pem /tmp/prime256v1_key.pem [ns_base64urldecode BBGNrqwUWW4dedpYHZnoS8hzZZNMmO-i3nYButngeZ5KtJ73ZaGa00BZxke2h2RCRGm-6Rroni8tDPR_RMgNib0]
2489 */
2490
2491 group = EC_KEY_get0_group(eckey);
2492
2493#if 0
2494 {
2495 /*
2496 * Steps as recommended from OpenSSL wiki page. However,
2497 * the code based on the low-level EC_POINT_oct2point()
2498 * appears to be correct.
2499 */
2500
2501 /*
2502 * Further ingredients:
2503 *
2504 * pkey : private key, from PEM, EVP_PKEY
2505 * peerKeyEC : peer key locally regnerated, same curve as pkey, get filled with octets
2506 * peerKey : peer key as EVP_PKEY, filled with peerKeyEC
2507 * pctx : parameter generation contenxt
2508 * params : parameter object
2509 * kctx : key generation context, uses params, used for EVP_PKEY_keygen_init and EVP_PKEY_keygen
2510 * ctx : shared secret generation context, uses pkey
2511 */
2512 EVP_PKEY_CTX *pctx, *ctx = NULL((void*)0), *kctx = NULL((void*)0);
2513 EC_KEY *peerKeyEC;
2514 EVP_PKEY *peerKey, *params = NULL((void*)0);
2515 EVP_PKEY *pkey;
2516
2517 pkey = GetPkeyFromPem(interp, pemFileName, NS_EMPTY_STRING, NS_TRUE1);
2518 peerKeyEC = EC_KEY_new_by_curve_name(EC_GROUP_get_curve_name(group));
2519 peerKey = EVP_PKEY_new();
2520
2521 pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC408, NULL((void*)0));
2522 EVP_PKEY_paramgen_init(pctx);
2523 EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, EC_GROUP_get_curve_name(group));
2524 Ns_Log(Notice, "NID X9_62_prime256v1 %d, privKey curve %d ", NID_X9_62_prime256v1415, EC_GROUP_get_curve_name(group));
2525
2526 result = TCL_ERROR1;
2527 if (EC_KEY_oct2key(peerKeyEC, pubkeyString, (size_t)pubkeyLength, NULL((void*)0)) != 1) {
2528 Ns_Log(Notice, "could not import peer key");
2529 Ns_TclPrintfResult(interp, "could not import peer key");
2530 } else if (EVP_PKEY_set1_EC_KEY(peerKey, peerKeyEC) != 1) {
2531 Ns_Log(Notice, "could not convert EC key to EVP key");
2532 Ns_TclPrintfResult(interp, "could not convert EC key to EVP key");
2533 } else if (EVP_PKEY_paramgen(pctx, &params) != 1) {
2534 Ns_Log(Notice, "could not generate parameters");
2535 Ns_TclPrintfResult(interp, "could not generate parameters");
2536 } else if ((kctx = EVP_PKEY_CTX_new(params, NULL((void*)0))) == NULL((void*)0)) {
2537 Ns_Log(Notice, "could not generate kctx");
2538 Ns_TclPrintfResult(interp, "could not generate kctx");
2539 } else if (EVP_PKEY_keygen_init(kctx) != 1) {
2540 Ns_Log(Notice, "could not init kctx");
2541 Ns_TclPrintfResult(interp, "could not init kctx");
2542 } else if (EVP_PKEY_keygen(kctx, &pkey) != 1) {
2543 Ns_Log(Notice, "could not generate key for kctx");
2544 Ns_TclPrintfResult(interp, "could not generate key for ctx");
2545 } else if ((ctx = EVP_PKEY_CTX_new(pkey, NULL((void*)0))) == NULL((void*)0)) {
2546 Ns_TclPrintfResult(interp, "could not create ctx");
2547 } else if (EVP_PKEY_derive_init(ctx) != 1) {
2548 Ns_Log(Notice, "could not derive init ctx");
2549 Ns_TclPrintfResult(interp, "could not derive init ctx");
2550 } else if (EVP_PKEY_derive_set_peer(ctx, peerKey) != 1) {
2551 Ns_Log(Notice, "could set peer key");
2552 Ns_TclPrintfResult(interp, "could not set peer key");
2553 result = TCL_OK0;
2554 } else {
2555 Tcl_DString ds;
2556 size_t sharedKeySize = 0u;
2557
2558 Tcl_DStringInit(&ds);
2559 (void)EVP_PKEY_derive(ctx, NULL((void*)0), &sharedKeySize);
2560 if (sharedKeySize > 0) {
2561 Tcl_DStringSetLength(&ds, (int)sharedKeySize);
2562 (void)EVP_PKEY_derive(ctx, (unsigned char *)ds.string, &sharedKeySize);
2563 hexPrint("recommended", (unsigned char *)ds.string, sharedKeySize);
2564 result = TCL_OK0;
2565 }
2566 Tcl_DStringFree(&ds);
2567 }
2568
2569 EC_KEY_free(peerKeyEC);
2570 EVP_PKEY_free(peerKey);
2571 EVP_PKEY_free(pkey);
2572 EVP_PKEY_CTX_free(ctx);
2573 EVP_PKEY_CTX_free(pctx);
2574 EVP_PKEY_CTX_free(kctx);
2575 EVP_PKEY_free(params);
2576 }
2577#endif
2578 /*
2579 * Computes the ECDH shared secret, used as the input key material (IKM) for
2580 * HKDF.
2581 */
2582
2583 pubKeyPt = EC_POINT_new(group);
2584
2585 if (EC_POINT_oct2point(group, pubKeyPt, pubkeyString, (size_t)pubkeyLength, bn_ctx) != 1) {
2586 Ns_TclPrintfResult(interp, "could not derive EC point from provided key");
2587 result = TCL_ERROR1;
2588
2589 } else {
2590 size_t sharedSecretLength;
2591 Tcl_DString ds;
2592
2593 Tcl_DStringInit(&ds);
2594 sharedSecretLength = (size_t)((EC_GROUP_get_degree(group) + 7) / 8);
2595 Tcl_DStringSetLength(&ds, (int)sharedSecretLength);
2596
2597 if (ECDH_compute_key(ds.string, sharedSecretLength, pubKeyPt, eckey, NULL((void*)0)) <= 0) {
2598 Ns_TclPrintfResult(interp, "could not derive shared secret");
2599 result = TCL_ERROR1;
2600
2601 } else {
2602 /*
2603 * Success: we were able to convert the octets to EC
2604 * points and to compute a shared secret from this. So
2605 * we can return the shared secret in the requested
2606 * encoding.
2607 */
2608 /* hexPrint("ecec ", (unsigned char *)ds.string, sharedSecretLength);*/
2609 Tcl_SetObjResult(interp, EncodedObj((unsigned char *)ds.string, sharedSecretLength, NULL((void*)0), encoding));
2610 }
2611 Tcl_DStringFree(&ds);
2612 }
2613 /*
2614 * Clean up.
2615 */
2616 BN_CTX_free(bn_ctx);
2617 EC_POINT_free(pubKeyPt);
2618 Tcl_DStringFree(&importDs);
2619
2620 if (eckey != NULL((void*)0)) {
2621 EC_KEY_free(eckey);
2622 }
2623 }
2624
2625 return result;
2626}
2627# endif
2628
2629
2630/*
2631 *----------------------------------------------------------------------
2632 *
2633 * NsTclCryptoEckeyObjCmd --
2634 *
2635 * Implements "ns_crypto::eckey" with various subcommands to
2636 * provide subcommands to handle EC (elliptic curve) cryptography
2637 * related commands.
2638 *
2639 * Results:
2640 * Tcl Return code.
2641 *
2642 * Side effects:
2643 * None.
2644 *
2645 *----------------------------------------------------------------------
2646 */
2647int
2648NsTclCryptoEckeyObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
2649{
2650 const Ns_SubCmdSpec subcmds[] = {
2651 {"generate", CryptoEckeyGenerateObjCmd},
2652# ifdef HAVE_OPENSSL_EC_PRIV2OCT
2653 {"import", CryptoEckeyImportObjCmd},
2654 {"priv", CryptoEckeyPrivObjCmd},
2655# endif
2656# ifndef HAVE_OPENSSL_PRE_1_1
2657 {"sharedsecret", CryptoEckeySharedsecretObjCmd},
2658# endif
2659 {"pub", CryptoEckeyPubObjCmd},
2660 {NULL((void*)0), NULL((void*)0)}
2661 };
2662
2663 return Ns_SubcmdObjv(subcmds, clientData, interp, objc, objv);
2664}
2665# endif /* OPENSSL_NO_EC */
2666
2667
2668
2669
2670
2671
2672/*
2673 *----------------------------------------------------------------------
2674 *
2675 * CryptoAeadStringGetArguments --
2676 *
2677 * Helper function for CryptoAeadEncryptStringObjCmd and
2678 * CryptoAeadDecryptStringObjCmd. The argument passing for
2679 * both functions is very similar and is quite long. This
2680 * function factors out communalities to avoid code
2681 * duplication. It should be possible to reuse this function
2682 * largely when we support incremental updates similar to
2683 * "ns_md" and "ns_hmac".
2684 *
2685 * Results:
2686 * Tcl Result Code (and many output arguments)
2687 *
2688 * Side effects:
2689 * None
2690 *
2691 *----------------------------------------------------------------------
2692 */
2693
2694static int
2695CryptoAeadStringGetArguments(
2696 Tcl_Interp *interp, int objc, Tcl_Obj *const* objv, bool_Bool encrypt,
2697 Tcl_DString *ivDsPtr, Tcl_DString *keyDsPtr, Tcl_DString *aadDsPtr,
2698 Tcl_DString *tagDsPtr,
2699 const unsigned char **keyStringPtr, int *keyLengthPtr,
2700 const unsigned char **ivStringPtr, int *ivLengthPtr,
2701 const unsigned char **aadStringPtr, int *aadLengthPtr,
2702 char **tagStringPtr, int *tagLengthPtr,
2703 const unsigned char **inputStringPtr, int *inputLengthPtr,
2704 const EVP_CIPHER **cipherPtr, Ns_BinaryEncoding *encodingPtr, EVP_CIPHER_CTX **ctxPtr
2705) {
2706 Tcl_Obj *ivObj = NULL((void*)0), *keyObj = NULL((void*)0), *aadObj = NULL((void*)0), *tagObj = NULL((void*)0), *inputObj;
2707 int result, isBinary = 0;
2708 char *cipherName = (char *)"aes-128-gcm";
2709 Tcl_DString ivDs, inputDs;
2710
2711 Ns_ObjvSpec lopts_encrypt[] = {
2712 {"-binary", Ns_ObjvBool, &isBinary, INT2PTR(NS_TRUE)((void *)(intptr_t)(1))},
2713 {"-aad", Ns_ObjvObj, &aadObj, NULL((void*)0)},
2714 {"-cipher", Ns_ObjvString, &cipherName, NULL((void*)0)},
2715 {"-encoding", Ns_ObjvIndex, encodingPtr, binaryencodings},
2716 {"-iv", Ns_ObjvObj, &ivObj, NULL((void*)0)},
2717 {"-key", Ns_ObjvObj, &keyObj, NULL((void*)0)},
2718 {"--", Ns_ObjvBreak, NULL((void*)0), NULL((void*)0)},
2719 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
2720 };
2721 Ns_ObjvSpec lopts_decrypt[] = {
2722 {"-binary", Ns_ObjvBool, &isBinary, INT2PTR(NS_TRUE)((void *)(intptr_t)(1))},
2723 {"-aad", Ns_ObjvObj, &aadObj, NULL((void*)0)},
2724 {"-cipher", Ns_ObjvString, &cipherName, NULL((void*)0)},
2725 {"-encoding", Ns_ObjvIndex, encodingPtr, binaryencodings},
2726 {"-iv", Ns_ObjvObj, &ivObj, NULL((void*)0)},
2727 {"-key", Ns_ObjvObj, &keyObj, NULL((void*)0)},
2728 {"-tag", Ns_ObjvObj, &tagObj, NULL((void*)0)},
2729 {"--", Ns_ObjvBreak, NULL((void*)0), NULL((void*)0)},
2730 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
2731 };
2732 Ns_ObjvSpec args[] = {
2733 {"input", Ns_ObjvObj, &inputObj, NULL((void*)0)},
2734 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
2735 };
2736
2737 *encodingPtr = RESULT_ENCODING_HEX;
2738 *ctxPtr = NULL((void*)0);
2739
2740 Tcl_DStringInit(&ivDs);
2741
2742 if (Ns_ParseObjv(encrypt ? lopts_encrypt : lopts_decrypt, args, interp, 2, objc, objv) != NS_OK) {
2743 result = TCL_ERROR1;
2744
2745 } else if (keyObj == NULL((void*)0)) {
2746 Ns_TclPrintfResult(interp, "no key in specified");
2747 result = TCL_ERROR1;
2748
2749 } else if ((result = GetCipher(interp, cipherName, EVP_CIPH_GCM_MODE0x6, "gcm", cipherPtr)) == TCL_OK0) {
2750
2751 *ctxPtr = EVP_CIPHER_CTX_new();
2752 *keyStringPtr = Ns_GetBinaryString(keyObj, isBinary == 1, keyLengthPtr, keyDsPtr);
2753
2754 /*
2755 * Get optional additional authenticated data (AAD)
2756 */
2757 if (aadObj != NULL((void*)0)) {
2758 *aadStringPtr = Ns_GetBinaryString(aadObj, isBinary == 1, aadLengthPtr, aadDsPtr);
2759 } else {
2760 *aadLengthPtr = 0;
2761 *aadStringPtr = 0;
2762 }
2763
2764 /*
2765 * Get sometimes optional initialization vector (IV)
2766 */
2767 if (ivObj != NULL((void*)0)) {
2768 *ivStringPtr = Ns_GetBinaryString(ivObj, isBinary == 1, ivLengthPtr, ivDsPtr);
2769 } else {
2770 *ivStringPtr = NULL((void*)0);
2771 *ivLengthPtr = 0;
2772 }
2773
2774 if (tagObj != NULL((void*)0)) {
2775 *tagStringPtr = (char *)Ns_GetBinaryString(tagObj, isBinary == 1, tagLengthPtr, tagDsPtr);
2776 } else {
2777 *tagStringPtr = NULL((void*)0);
2778 *tagLengthPtr = 0;
2779 }
2780
2781 if (*ivLengthPtr > EVP_MAX_IV_LENGTH16
2782 || (*ivLengthPtr == 0 && EVP_CIPHER_iv_lengthEVP_CIPHER_get_iv_length(*cipherPtr) > 0)
2783 ) {
2784 Ns_TclPrintfResult(interp, "initialization vector is invalid (default length for %s: %d bytes)",
2785 cipherName, EVP_CIPHER_iv_lengthEVP_CIPHER_get_iv_length(*cipherPtr));
2786 result = TCL_ERROR1;
2787
2788 } else if (*ctxPtr == NULL((void*)0)) {
2789 Ns_TclPrintfResult(interp, "could not create encryption context");
2790 result = TCL_ERROR1;
2791
2792 } else {
2793 *inputStringPtr = Ns_GetBinaryString(inputObj, isBinary == 1, inputLengthPtr, &inputDs);
2794 result = TCL_OK0;
2795 }
2796 }
2797 return result;
2798}
2799
2800/*
2801 *----------------------------------------------------------------------
2802 *
2803 * CryptoAeadStringObjCmd -- Subcommand of NsTclCryptoAeadObjCmd
2804 *
2805 * Implements "ns_crypto::aead::encrypt string" and
2806 * "ns_crypto::aead::decrypt string". Subcommand to encrypt or
2807 * decrypt string data. Encryption returns a dict with "bytes"
2808 * and the "tag" necessary for decoding.
2809 *
2810 * Results:
2811 * Tcl Result Code.
2812 *
2813 * Side effects:
2814 * None
2815 *
2816 *----------------------------------------------------------------------
2817 */
2818
2819static int
2820CryptoAeadStringObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv, bool_Bool encrypt)
2821{
2822 int result;
2823 const EVP_CIPHER *cipher = NULL((void*)0);
2824 Tcl_DString ivDs, keyDs, aadDs, tagDs, inputDs;
2825 Ns_BinaryEncoding encoding = RESULT_ENCODING_HEX;
2826 EVP_CIPHER_CTX *ctx;
2827 const unsigned char *inputString = NULL((void*)0), *ivString, *aadString, *keyString = NULL((void*)0);
2828 char *tagString = NULL((void*)0);
2829 int inputLength, keyLength, ivLength, aadLength, tagLength;
2830
2831 /*
2832 ::ns_crypto::aead::encrypt string -cipher aes-128-gcm -iv 123456789 -key secret "hello world"
2833
2834 set d [::ns_crypto::aead::encrypt string -cipher aes-128-gcm -iv 123456789 -key secret -encoding binary "hello world"]
2835 ns_crypto::aead::decrypt string -cipher aes-128-gcm -iv 123456789 -key secret -tag [dict get $d tag] -encoding binary [dict get $d bytes]
2836
2837 */
2838
2839 Tcl_DStringInit(&inputDs);
2840 Tcl_DStringInit(&aadDs);
2841 Tcl_DStringInit(&keyDs);
2842 Tcl_DStringInit(&ivDs);
2843 Tcl_DStringInit(&tagDs);
2844
2845 result = CryptoAeadStringGetArguments(interp, objc, objv, encrypt,
2846 &ivDs, &keyDs, &aadDs, &tagDs,
2847 &keyString, &keyLength,
2848 &ivString, &ivLength,
2849 &aadString, &aadLength,
2850 &tagString, &tagLength,
2851 &inputString, &inputLength,
2852 &cipher, &encoding, &ctx);
2853 if (result == TCL_OK0) {
2854 int length;
2855
2856 if (encrypt) {
2857 /*
2858 * Encrypt ...
2859 */
2860 if ((EVP_EncryptInit_ex(ctx, cipher, NULL((void*)0), NULL((void*)0), NULL((void*)0)) != 1)
2861 || (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN0x9, ivLength, NULL((void*)0)) != 1)
2862 || (EVP_EncryptInit_ex(ctx, NULL((void*)0), NULL((void*)0), keyString, ivString) != 1)
2863 ) {
2864 Ns_TclPrintfResult(interp, "could not initialize encryption context");
2865 result = TCL_ERROR1;
2866
2867 } else if (EVP_EncryptUpdate(ctx, NULL((void*)0), &length, aadString, aadLength) != 1) {
2868 /*
2869 * To specify additional authenticated data (AAD), a call
2870 * to EVP_CipherUpdate(), EVP_EncryptUpdate() or
2871 * EVP_DecryptUpdate() should be made with the output
2872 * parameter out set to NULL.
2873 */
2874 Ns_TclPrintfResult(interp, "could not set additional authenticated data (AAD)");
2875 result = TCL_ERROR1;
2876
2877 } else {
2878 int cipherBlockSize = EVP_CIPHER_block_sizeEVP_CIPHER_get_block_size(cipher), outputLength;
2879 Tcl_Obj *listObj;
2880 Tcl_DString outputDs;
2881
2882 Tcl_DStringInit(&outputDs);
2883
2884 /*
2885 * Everything is set up successfully, now do the "real" encryption work.
2886 *
2887 * Provide the message to be encrypted, and obtain the
2888 * encrypted output. EVP_EncryptUpdate can be called
2889 * multiple times if necessary.
2890 */
2891 Tcl_DStringSetLength(&outputDs, inputLength + cipherBlockSize);
2892 (void)EVP_EncryptUpdate(ctx, (unsigned char *)outputDs.string, &length,
2893 inputString, inputLength);
2894 outputLength = length;
2895
2896 //fprintf(stderr, "allocated size %d, inputLength %d cipherBlockSize %d actual size %d\n",
2897 // (inputLength + cipherBlockSize), inputLength, cipherBlockSize, outputLength);
2898 assert((inputLength + cipherBlockSize) >= outputLength)((void) (0));
2899
2900 (void)EVP_EncryptFinal_ex(ctx, (unsigned char *)(outputDs.string + length), &length);
2901 outputLength += length;
2902 //fprintf(stderr, "allocated size %d, final size %d\n", (inputLength + cipherBlockSize), outputLength);
2903 Tcl_DStringSetLength(&outputDs, outputLength);
2904
2905 /*
2906 * Get the tag
2907 */
2908 Tcl_DStringSetLength(&tagDs, 16);
2909 EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG0x10, tagDs.length, tagDs.string);
2910
2911 listObj = Tcl_NewListObj(0, NULL((void*)0));
2912 /*
2913 * Convert the result to the output format and return a
2914 * dict containing "bytes" and "tag" as the interp result.
2915 */
2916 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("bytes", 5));
2917 Tcl_ListObjAppendElement(interp, listObj, EncodedObj((unsigned char *)outputDs.string,
2918 (size_t)outputDs.length,
2919 NULL((void*)0), encoding));
2920 Tcl_ListObjAppendElement(interp, listObj, Tcl_NewStringObj("tag", 3));
2921 Tcl_ListObjAppendElement(interp, listObj, EncodedObj((unsigned char *)tagDs.string,
2922 (size_t)tagDs.length,
2923 NULL((void*)0), encoding));
2924 Tcl_SetObjResult(interp, listObj);
2925 assert(result == TCL_OK)((void) (0));
2926 Tcl_DStringFree(&outputDs);
2927 }
2928
2929 } else {
2930 /*
2931 * Decrypt ...
2932 */
2933 assert(!encrypt)((void) (0));
2934
2935 if (tagString == NULL((void*)0)) {
2936 Ns_TclPrintfResult(interp, "option '-tag' has to be provided for decryption");
2937 result = TCL_ERROR1;
2938
2939 } else if ((EVP_DecryptInit_ex(ctx, cipher, NULL((void*)0), NULL((void*)0), NULL((void*)0)) != 1)
2940 || (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN0x9, ivLength, NULL((void*)0)) != 1)
2941 || (EVP_DecryptInit_ex(ctx, NULL((void*)0), NULL((void*)0), keyString, ivString) != 1)
2942 ) {
2943 Ns_TclPrintfResult(interp, "could not initialize decryption context");
2944 result = TCL_ERROR1;
2945
2946 } else if (EVP_DecryptUpdate(ctx, NULL((void*)0), &length, aadString, aadLength) != 1) {
2947 /*
2948 * To specify additional authenticated data (AAD), a call
2949 * to EVP_CipherUpdate(), EVP_EncryptUpdate() or
2950 * EVP_DecryptUpdate() should be made with the output
2951 * parameter out set to NULL.
2952 */
2953 Ns_TclPrintfResult(interp, "could not set additional authenticated data (AAD)");
2954 result = TCL_ERROR1;
2955
2956 } else {
2957 int outputLength;
2958 Tcl_DString outputDs;
2959
2960 Tcl_DStringInit(&outputDs);
2961
2962 /*
2963 * Everything is set up successfully, now do the "real" decryption work.
2964 *
2965 * Provide the input to be decrypted, and obtain the plaintext output.
2966 * EVP_DecryptUpdate can be called multiple times if necessary.
2967 */
2968 Tcl_DStringSetLength(&outputDs, inputLength);
2969 (void)EVP_DecryptUpdate(ctx,
2970 (unsigned char *)outputDs.string, &length,
2971 inputString, inputLength);
2972 outputLength = length;
2973
2974 /*
2975 * Set expected tag value. Works in OpenSSL 1.0.1d and later
2976 */
2977 if(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG0x11, tagLength, tagString) != 1) {
2978 Ns_TclPrintfResult(interp, "could not set tag value");
2979 result = TCL_ERROR1;
2980 } else {
2981
2982 (void)EVP_DecryptFinal_ex(ctx, (unsigned char *)(outputDs.string + length), &length);
2983 outputLength += length;
2984 //fprintf(stderr, "allocated size %d, final size %d\n", inputLength, outputLength);
2985 Tcl_DStringSetLength(&outputDs, outputLength);
2986 Tcl_SetObjResult(interp, EncodedObj((unsigned char *)outputDs.string,
2987 (size_t)outputDs.length,
2988 NULL((void*)0), encoding));
2989 assert(result == TCL_OK)((void) (0));
2990 }
2991 Tcl_DStringFree(&outputDs);
2992 }
2993
2994 }
2995 /* Clean up */
2996 EVP_CIPHER_CTX_free(ctx);
2997 }
2998
2999 Tcl_DStringFree(&inputDs);
3000 Tcl_DStringFree(&aadDs);
3001 Tcl_DStringFree(&keyDs);
3002 Tcl_DStringFree(&ivDs);
3003 Tcl_DStringFree(&tagDs);
3004
3005 return result;
3006}
3007
3008static int
3009CryptoAeadEncryptStringObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
3010{
3011 return CryptoAeadStringObjCmd(clientData, interp, objc, objv, NS_TRUE1);
3012}
3013static int
3014CryptoAeadDecryptStringObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
3015{
3016 return CryptoAeadStringObjCmd(clientData, interp, objc, objv, NS_FALSE0);
3017}
3018
3019
3020
3021/*
3022 *----------------------------------------------------------------------
3023 *
3024 * NsTclCryptoAeadEncryptObjCmd, NsTclCryptoAeadDecryptObjCmd --
3025 *
3026 * Implements "ns_crypto::aead::encrypt" and
3027 * "ns_crypto::aead::dncrypt". Returns encrypted/decrypted data.
3028 *
3029 * Results:
3030 * NS_OK
3031 *
3032 * Side effects:
3033 * Tcl result is set to a string value.
3034 *
3035 *----------------------------------------------------------------------
3036 */
3037int
3038NsTclCryptoAeadEncryptObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
3039{
3040 const Ns_SubCmdSpec subcmds[] = {
3041 {"string", CryptoAeadEncryptStringObjCmd},
3042 {NULL((void*)0), NULL((void*)0)}
3043 };
3044
3045 return Ns_SubcmdObjv(subcmds, clientData, interp, objc, objv);
3046}
3047int
3048NsTclCryptoAeadDecryptObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
3049{
3050 const Ns_SubCmdSpec subcmds[] = {
3051 {"string", CryptoAeadDecryptStringObjCmd},
3052 {NULL((void*)0), NULL((void*)0)}
3053 };
3054
3055 return Ns_SubcmdObjv(subcmds, clientData, interp, objc, objv);
3056}
3057
3058/*
3059 *----------------------------------------------------------------------
3060 *
3061 * NsTclCryptoRandomBytesObjCmd --
3062 *
3063 * Implements "ns_crypto::randombytes". Returns random bytes
3064 * from OpenSSL.
3065 *
3066 * Example: ns_crypto::randombytes 20
3067 *
3068 * Results:
3069 * Tcl Result Code.
3070 *
3071 * Side effects:
3072 * None
3073 *
3074 *----------------------------------------------------------------------
3075 */
3076int
3077NsTclCryptoRandomBytesObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
3078{
3079 int result, nrBytes = 0;
3080 Ns_BinaryEncoding encoding = RESULT_ENCODING_HEX;
3081 Ns_ObjvValueRange lengthRange = {1, INT_MAX2147483647};
3082 Ns_ObjvSpec lopts[] = {
3083 {"-encoding", Ns_ObjvIndex, &encoding, binaryencodings},
3084 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
3085 };
3086 Ns_ObjvSpec args[] = {
3087 {"bytes", Ns_ObjvInt, &nrBytes, &lengthRange},
3088 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
3089 };
3090
3091 if (Ns_ParseObjv(lopts, args, interp, 1, objc, objv) != NS_OK) {
3092 result = TCL_ERROR1;
3093
3094 } else {
3095 Tcl_DString ds;
3096 int rc;
3097
3098 Tcl_DStringInit(&ds);
3099 Tcl_DStringSetLength(&ds, nrBytes);
3100 rc = RAND_bytes((unsigned char *)ds.string, nrBytes);
3101 if (likely(rc == 1)(__builtin_expect((rc == 1), 1))) {
3102 Tcl_SetObjResult(interp, EncodedObj((unsigned char *)ds.string, (size_t)nrBytes, NULL((void*)0), encoding));
3103 result = TCL_OK0;
3104 } else {
3105 Ns_TclPrintfResult(interp, "could not obtain random bytes from OpenSSL");
3106 result = TCL_ERROR1;
3107 }
3108 Tcl_DStringFree(&ds);
3109 }
3110
3111 return result;
3112}
3113
3114# ifdef OPENSSL_NO_EC
3115int
3116NsTclCryptoEckeyObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int UNUSED(objc)UNUSED_objc __attribute__((__unused__)), Tcl_Obj *const* UNUSED(objv)UNUSED_objv __attribute__((__unused__)))
3117{
3118 Ns_TclPrintfResult(interp, "The used version of OpenSSL was built without EC support");
3119 return TCL_ERROR1;
3120}
3121# endif
3122
3123#else
3124/*
3125 * Compile without OpenSSL support or too old OpenSSL versions
3126 */
3127
3128int
3129NsTclCryptoHmacObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int UNUSED(objc)UNUSED_objc __attribute__((__unused__)), Tcl_Obj *const* UNUSED(objv)UNUSED_objv __attribute__((__unused__)))
3130{
3131 Ns_TclPrintfResult(interp, "Command requires support for OpenSSL built into NaviServer");
3132 return TCL_ERROR1;
3133}
3134
3135int
3136NsTclCryptoMdObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int UNUSED(objc)UNUSED_objc __attribute__((__unused__)), Tcl_Obj *const* UNUSED(objv)UNUSED_objv __attribute__((__unused__)))
3137{
3138 Ns_TclPrintfResult(interp, "Command requires support for OpenSSL built into NaviServer");
3139 return TCL_ERROR1;
3140}
3141
3142int
3143NsTclCryptoAeadDecryptObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int UNUSED(objc)UNUSED_objc __attribute__((__unused__)), Tcl_Obj *const* UNUSED(objv)UNUSED_objv __attribute__((__unused__)))
3144{
3145 Ns_TclPrintfResult(interp, "Command requires support for OpenSSL built into NaviServer");
3146 return TCL_ERROR1;
3147}
3148int
3149NsTclCryptoAeadEncryptObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int UNUSED(objc)UNUSED_objc __attribute__((__unused__)), Tcl_Obj *const* UNUSED(objv)UNUSED_objv __attribute__((__unused__)))
3150{
3151 Ns_TclPrintfResult(interp, "Command requires support for OpenSSL built into NaviServer");
3152 return TCL_ERROR1;
3153}
3154
3155int
3156NsTclCryptoRandomBytesObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int UNUSED(objc)UNUSED_objc __attribute__((__unused__)), Tcl_Obj *const* UNUSED(objv)UNUSED_objv __attribute__((__unused__)))
3157{
3158 Ns_TclPrintfResult(interp, "Command requires support for OpenSSL built into NaviServer");
3159 return TCL_ERROR1;
3160}
3161
3162int
3163NsTclCryptoEckeyObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int UNUSED(objc)UNUSED_objc __attribute__((__unused__)), Tcl_Obj *const* UNUSED(objv)UNUSED_objv __attribute__((__unused__)))
3164{
3165 Ns_TclPrintfResult(interp, "Command requires support for OpenSSL built into NaviServer");
3166 return TCL_ERROR1;
3167}
3168
3169int
3170NsTclCryptoScryptObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int UNUSED(objc)UNUSED_objc __attribute__((__unused__)), Tcl_Obj *const* UNUSED(objv)UNUSED_objv __attribute__((__unused__)))
3171{
3172 Ns_TclPrintfResult(interp, "Command requires support for OpenSSL 3.0 built into NaviServer");
3173 return TCL_ERROR1;
3174}
3175#endif
3176
3177/*
3178 * Local Variables:
3179 * mode: c
3180 * c-basic-offset: 4
3181 * fill-column: 70
3182 * indent-tabs-mode: nil
3183 * End:
3184 */