diff options
author | cvs2hg <devnull@localhost> | 2000-10-07 00:56:21 +0000 |
---|---|---|
committer | cvs2hg <devnull@localhost> | 2000-10-07 00:56:21 +0000 |
commit | b8b14c9aa1ded53b794f9f20fc4b5d883f4ae610 (patch) | |
tree | 1eccd54d8a6935dad221211d92094638a5c5548f | |
parent | c92cf38ebc0454af6b4f9f7ea53e3a7d4484071d (diff) | |
download | nss-hg-b8b14c9aa1ded53b794f9f20fc4b5d883f4ae610.tar.gz |
fixup commit for branch 'NSS_30_BRANCH'NSS_3_0_1_RTM
-rw-r--r-- | security/coreconf/tree.mk | 114 | ||||
-rw-r--r-- | security/nss/cmd/modutil/Makefile | 80 | ||||
-rw-r--r-- | security/nss/cmd/modutil/installparse.c | 429 | ||||
-rw-r--r-- | security/nss/cmd/modutil/installparse.h | 3 | ||||
-rw-r--r-- | security/nss/cmd/modutil/rules.mk | 54 | ||||
-rw-r--r-- | security/nss/cmd/strsclnt/strsclnt.c | 1121 | ||||
-rw-r--r-- | security/nss/lib/fortcrypt/genci.h | 145 | ||||
-rw-r--r-- | security/nss/lib/jar/jarevil.c | 571 | ||||
-rw-r--r-- | security/nss/lib/jar/jarnav.c | 107 | ||||
-rw-r--r-- | security/nss/lib/jar/jarsign.c | 377 | ||||
-rw-r--r-- | security/nss/lib/jar/jarver.c | 2029 | ||||
-rw-r--r-- | security/nss/lib/pk11wrap/pk11skey.c | 4878 | ||||
-rwxr-xr-x | security/nss/tests/ssl/ssl.sh | 329 | ||||
-rw-r--r-- | security/nss/tests/ssl/sslstress.txt | 14 |
14 files changed, 10251 insertions, 0 deletions
diff --git a/security/coreconf/tree.mk b/security/coreconf/tree.mk new file mode 100644 index 000000000..9c4c78bbc --- /dev/null +++ b/security/coreconf/tree.mk @@ -0,0 +1,114 @@ +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Netscape security libraries. +# +# The Initial Developer of the Original Code is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1994-2000 Netscape Communications Corporation. All +# Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the +# terms of the GNU General Public License Version 2 or later (the +# "GPL"), in which case the provisions of the GPL are applicable +# instead of those above. If you wish to allow use of your +# version of this file only under the terms of the GPL and not to +# allow others to use your version of this file under the MPL, +# indicate your decision by deleting the provisions above and +# replace them with the notice and other provisions required by +# the GPL. If you do not delete the provisions above, a recipient +# may use your version of this file under either the MPL or the +# GPL. +# + +####################################################################### +# Master "Core Components" file system "release" prefixes # +####################################################################### + +# RELEASE_TREE = $(CORE_DEPTH)/../coredist + + +ifndef RELEASE_TREE + ifdef BUILD_SHIP + ifdef USE_SHIPS + RELEASE_TREE = $(BUILD_SHIP) + else + RELEASE_TREE = /m/dist + endif + else + RELEASE_TREE = /m/dist + endif + ifeq ($(OS_TARGET), WINNT) + ifdef BUILD_SHIP + ifdef USE_SHIPS + RELEASE_TREE = $(NTBUILD_SHIP) + else + RELEASE_TREE = //iridium/components + endif + else + RELEASE_TREE = //iridium/components + endif + endif + + ifeq ($(OS_TARGET), WIN95) + ifdef BUILD_SHIP + ifdef USE_SHIPS + RELEASE_TREE = $(NTBUILD_SHIP) + else + RELEASE_TREE = //iridium/components + endif + else + RELEASE_TREE = //iridium/components + endif + endif + ifeq ($(OS_TARGET), WIN16) + ifdef BUILD_SHIP + ifdef USE_SHIPS + RELEASE_TREE = $(NTBUILD_SHIP) + else + RELEASE_TREE = //iridium/components + endif + else + RELEASE_TREE = //iridium/components + endif + endif +endif + +# +# NOTE: export control policy enforced for XP and MD files +# released to the binary release tree +# + +ifeq ($(POLICY), domestic) + RELEASE_XP_DIR = domestic + RELEASE_MD_DIR = domestic/$(PLATFORM) +else + ifeq ($(POLICY), export) + RELEASE_XP_DIR = export + RELEASE_MD_DIR = export/$(PLATFORM) + else + ifeq ($(POLICY), france) + RELEASE_XP_DIR = france + RELEASE_MD_DIR = france/$(PLATFORM) + else + RELEASE_XP_DIR = + RELEASE_MD_DIR = $(PLATFORM) + endif + endif +endif + + +REPORTER_TREE = $(subst \,\\,$(RELEASE_TREE)) + +IMPORT_XP_DIR = +IMPORT_MD_DIR = $(PLATFORM) diff --git a/security/nss/cmd/modutil/Makefile b/security/nss/cmd/modutil/Makefile new file mode 100644 index 000000000..3d2bbb412 --- /dev/null +++ b/security/nss/cmd/modutil/Makefile @@ -0,0 +1,80 @@ +#! gmake +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Netscape security libraries. +# +# The Initial Developer of the Original Code is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1994-2000 Netscape Communications Corporation. All +# Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the +# terms of the GNU General Public License Version 2 or later (the +# "GPL"), in which case the provisions of the GPL are applicable +# instead of those above. If you wish to allow use of your +# version of this file only under the terms of the GPL and not to +# allow others to use your version of this file under the MPL, +# indicate your decision by deleting the provisions above and +# replace them with the notice and other provisions required by +# the GPL. If you do not delete the provisions above, a recipient +# may use your version of this file under either the MPL or the +# GPL. +# + +####################################################################### +# (1) Include initial platform-independent assignments (MANDATORY). # +####################################################################### + +include manifest.mn + +####################################################################### +# (2) Include "global" configuration information. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/config.mk + +####################################################################### +# (3) Include "component" configuration information. (OPTIONAL) # +####################################################################### + +####################################################################### +# (4) Include "local" platform-dependent assignments (OPTIONAL). # +####################################################################### +include ../platlibs.mk + +####################################################################### +# (5) Execute "global" rules. (OPTIONAL) # +####################################################################### + +include $(CORE_DEPTH)/coreconf/rules.mk + +####################################################################### +# (6) Execute "component" rules. (OPTIONAL) # +####################################################################### + + + +####################################################################### +# (7) Execute "local" rules. (OPTIONAL). # +####################################################################### + + +include ../platrules.mk + +# +# Cancel the built-in implicit yacc and lex rules. +# + +%.c: %.y +%.c: %.l diff --git a/security/nss/cmd/modutil/installparse.c b/security/nss/cmd/modutil/installparse.c new file mode 100644 index 000000000..16f81b6eb --- /dev/null +++ b/security/nss/cmd/modutil/installparse.c @@ -0,0 +1,429 @@ +#ifndef lint +char yysccsid[] = "@(#)yaccpar 1.4 (Berkeley) 02/25/90"; +#endif +#line 37 "installparse.y" + +#define yyparse Pk11Install_yyparse +#define yylex Pk11Install_yylex +#define yyerror Pk11Install_yyerror +#define yychar Pk11Install_yychar +#define yyval Pk11Install_yyval +#define yylval Pk11Install_yylval +#define yydebug Pk11Install_yydebug +#define yynerrs Pk11Install_yynerrs +#define yyerrflag Pk11Install_yyerrflag +#define yyss Pk11Install_yyss +#define yyssp Pk11Install_yyssp +#define yyvs Pk11Install_yyvs +#define yyvsp Pk11Install_yyvsp +#define yylhs Pk11Install_yylhs +#define yylen Pk11Install_yylen +#define yydefred Pk11Install_yydefred +#define yydgoto Pk11Install_yydgoto +#define yysindex Pk11Install_yysindex +#define yyrindex Pk11Install_yyrindex +#define yygindex Pk11Install_yygindex +#define yytable Pk11Install_yytable +#define yycheck Pk11Install_yycheck +#define yyname Pk11Install_yyname +#define yyrule Pk11Install_yyrule + +/* C Stuff */ +#include "install-ds.h" +#include <prprf.h> + +#define YYSTYPE Pk11Install_Pointer +extern char *Pk11Install_yytext; +char *Pk11Install_yyerrstr=NULL; + +#line 40 "ytab.c" +#define OPENBRACE 257 +#define CLOSEBRACE 258 +#define STRING 259 +#define YYERRCODE 256 +short yylhs[] = { -1, + 0, 1, 1, 2, 2, 3, 4, +}; +short yylen[] = { 2, + 1, 2, 0, 1, 1, 4, 1, +}; +short yydefred[] = { 0, + 0, 0, 1, 0, 4, 0, 2, 0, 0, 6, +}; +short yydgoto[] = { 2, + 3, 4, 5, 6, +}; +short yysindex[] = { -257, + 0, 0, 0, -257, 0, -252, 0, -257, -251, 0, +}; +short yyrindex[] = { 6, + 1, 0, 0, 3, 0, 0, 0, -250, 0, 0, +}; +short yygindex[] = { 0, + -4, 0, 0, 0, +}; +#define YYTABLESIZE 261 +short yytable[] = { 7, + 5, 1, 3, 9, 8, 3, 10, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 7, 5, 5, + 3, +}; +short yycheck[] = { 4, + 0, 259, 0, 8, 257, 0, 258, 258, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 257, 258, 259, + 258, +}; +#define YYFINAL 2 +#ifndef YYDEBUG +#define YYDEBUG 0 +#endif +#define YYMAXTOKEN 259 +#if YYDEBUG +char *yyname[] = { +"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"OPENBRACE","CLOSEBRACE","STRING", +}; +char *yyrule[] = { +"$accept : toplist", +"toplist : valuelist", +"valuelist : value valuelist", +"valuelist :", +"value : key_value_pair", +"value : STRING", +"key_value_pair : key OPENBRACE valuelist CLOSEBRACE", +"key : STRING", +}; +#endif +#ifndef YYSTYPE +typedef int YYSTYPE; +#endif +#define yyclearin (yychar=(-1)) +#define yyerrok (yyerrflag=0) +#ifndef YYSTACKSIZE +#ifdef YYMAXDEPTH +#define YYSTACKSIZE YYMAXDEPTH +#else +#define YYSTACKSIZE 300 +#endif +#endif +int yydebug; +int yynerrs; +int yyerrflag; +int yychar; +short *yyssp; +YYSTYPE *yyvsp; +YYSTYPE yyval; +YYSTYPE yylval; +#define yystacksize YYSTACKSIZE +short yyss[YYSTACKSIZE]; +YYSTYPE yyvs[YYSTACKSIZE]; +#line 118 "installparse.y" +/*----------------------- Program Section --------------------------------*/ + +/*************************************************************************/ +void +Pk11Install_yyerror(char *message) +{ + char *tmp; + if(Pk11Install_yyerrstr) { + tmp=PR_smprintf("%sline %d: %s\n", Pk11Install_yyerrstr, + Pk11Install_yylinenum, message); + PR_smprintf_free(Pk11Install_yyerrstr); + } else { + tmp = PR_smprintf("line %d: %s\n", Pk11Install_yylinenum, message); + } + Pk11Install_yyerrstr=tmp; +} +#line 191 "ytab.c" +#define YYABORT goto yyabort +#define YYACCEPT goto yyaccept +#define YYERROR goto yyerrlab +int +yyparse() +{ + register int yym, yyn, yystate; +#if YYDEBUG + register char *yys; + extern char *getenv(); + + if (yys = getenv("YYDEBUG")) + { + yyn = *yys; + if (yyn >= '0' && yyn <= '9') + yydebug = yyn - '0'; + } +#endif + + yynerrs = 0; + yyerrflag = 0; + yychar = (-1); + + yyssp = yyss; + yyvsp = yyvs; + *yyssp = yystate = 0; + +yyloop: + if (yyn = yydefred[yystate]) goto yyreduce; + if (yychar < 0) + { + if ((yychar = yylex()) < 0) yychar = 0; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("yydebug: state %d, reading %d (%s)\n", yystate, + yychar, yys); + } +#endif + } + if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yychar) + { +#if YYDEBUG + if (yydebug) + printf("yydebug: state %d, shifting to state %d\n", + yystate, yytable[yyn]); +#endif + if (yyssp >= yyss + yystacksize - 1) + { + goto yyoverflow; + } + *++yyssp = yystate = yytable[yyn]; + *++yyvsp = yylval; + yychar = (-1); + if (yyerrflag > 0) --yyerrflag; + goto yyloop; + } + if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yychar) + { + yyn = yytable[yyn]; + goto yyreduce; + } + if (yyerrflag) goto yyinrecovery; +#ifdef lint + goto yynewerror; +#endif +yynewerror: + yyerror("syntax error"); +#ifdef lint + goto yyerrlab; +#endif +yyerrlab: + ++yynerrs; +yyinrecovery: + if (yyerrflag < 3) + { + yyerrflag = 3; + for (;;) + { + if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE) + { +#if YYDEBUG + if (yydebug) + printf("yydebug: state %d, error recovery shifting\ + to state %d\n", *yyssp, yytable[yyn]); +#endif + if (yyssp >= yyss + yystacksize - 1) + { + goto yyoverflow; + } + *++yyssp = yystate = yytable[yyn]; + *++yyvsp = yylval; + goto yyloop; + } + else + { +#if YYDEBUG + if (yydebug) + printf("yydebug: error recovery discarding state %d\n", + *yyssp); +#endif + if (yyssp <= yyss) goto yyabort; + --yyssp; + --yyvsp; + } + } + } + else + { + if (yychar == 0) goto yyabort; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("yydebug: state %d, error recovery discards token %d (%s)\n", + yystate, yychar, yys); + } +#endif + yychar = (-1); + goto yyloop; + } +yyreduce: +#if YYDEBUG + if (yydebug) + printf("yydebug: state %d, reducing by rule %d (%s)\n", + yystate, yyn, yyrule[yyn]); +#endif + yym = yylen[yyn]; + yyval = yyvsp[1-yym]; + switch (yyn) + { +case 1: +#line 84 "installparse.y" +{ + Pk11Install_valueList = yyvsp[0].list; +} +break; +case 2: +#line 89 "installparse.y" +{ + Pk11Install_ValueList_AddItem(yyvsp[0].list,yyvsp[-1].value); + yyval .list = yyvsp[0].list; +} +break; +case 3: +#line 94 "installparse.y" +{ + yyval .list = Pk11Install_ValueList_new(); +} +break; +case 4: +#line 99 "installparse.y" +{ + yyval .value= Pk11Install_Value_new(PAIR_VALUE,yyvsp[0]); +} +break; +case 5: +#line 103 "installparse.y" +{ + yyval .value= Pk11Install_Value_new(STRING_VALUE, yyvsp[0]); +} +break; +case 6: +#line 108 "installparse.y" +{ + yyval .pair = Pk11Install_Pair_new(yyvsp[-3].string,yyvsp[-1].list); +} +break; +case 7: +#line 113 "installparse.y" +{ + yyval .string = yyvsp[0].string; +} +break; +#line 374 "ytab.c" + } + yyssp -= yym; + yystate = *yyssp; + yyvsp -= yym; + yym = yylhs[yyn]; + if (yystate == 0 && yym == 0) + { +#ifdef YYDEBUG + if (yydebug) + printf("yydebug: after reduction, shifting from state 0 to\ + state %d\n", YYFINAL); +#endif + yystate = YYFINAL; + *++yyssp = YYFINAL; + *++yyvsp = yyval; + if (yychar < 0) + { + if ((yychar = yylex()) < 0) yychar = 0; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("yydebug: state %d, reading %d (%s)\n", + YYFINAL, yychar, yys); + } +#endif + } + if (yychar == 0) goto yyaccept; + goto yyloop; + } + if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yystate) + yystate = yytable[yyn]; + else + yystate = yydgoto[yym]; +#ifdef YYDEBUG + if (yydebug) + printf("yydebug: after reduction, shifting from state %d \ +to state %d\n", *yyssp, yystate); +#endif + if (yyssp >= yyss + yystacksize - 1) + { + goto yyoverflow; + } + *++yyssp = yystate; + *++yyvsp = yyval; + goto yyloop; +yyoverflow: + yyerror("yacc stack overflow"); +yyabort: + return (1); +yyaccept: + return (0); +} diff --git a/security/nss/cmd/modutil/installparse.h b/security/nss/cmd/modutil/installparse.h new file mode 100644 index 000000000..75686d4fd --- /dev/null +++ b/security/nss/cmd/modutil/installparse.h @@ -0,0 +1,3 @@ +#define OPENBRACE 257 +#define CLOSEBRACE 258 +#define STRING 259 diff --git a/security/nss/cmd/modutil/rules.mk b/security/nss/cmd/modutil/rules.mk new file mode 100644 index 000000000..b34662513 --- /dev/null +++ b/security/nss/cmd/modutil/rules.mk @@ -0,0 +1,54 @@ +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Netscape security libraries. +# +# The Initial Developer of the Original Code is Netscape +# Communications Corporation. Portions created by Netscape are +# Copyright (C) 1994-2000 Netscape Communications Corporation. All +# Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the +# terms of the GNU General Public License Version 2 or later (the +# "GPL"), in which case the provisions of the GPL are applicable +# instead of those above. If you wish to allow use of your +# version of this file only under the terms of the GPL and not to +# allow others to use your version of this file under the MPL, +# indicate your decision by deleting the provisions above and +# replace them with the notice and other provisions required by +# the GPL. If you do not delete the provisions above, a recipient +# may use your version of this file under either the MPL or the +# GPL. +# + +# +# Some versions of yacc generate files that include platform-specific +# system headers. For example, the yacc in Solaris 2.6 inserts +# #include <values.h> +# which does not exist on NT. For portability, always use Berkeley +# yacc (such as the yacc in Linux) to generate files. +# + +generate: installparse.c installparse.l + +installparse.c: + yacc -p Pk11Install_yy -d installparse.y + mv y.tab.c installparse.c + mv y.tab.h installparse.h + +installparse.l: + lex -olex.Pk11Install_yy.c -PPk11Install_yy installparse.l + @echo + @echo "**YOU MUST COMMENT OUT UNISTD.H FROM lex.Pk11Install_yy.cpp**" + +install.c: install-ds.h install.h diff --git a/security/nss/cmd/strsclnt/strsclnt.c b/security/nss/cmd/strsclnt/strsclnt.c new file mode 100644 index 000000000..977d6dc24 --- /dev/null +++ b/security/nss/cmd/strsclnt/strsclnt.c @@ -0,0 +1,1121 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ +#include <stdio.h> +#include <string.h> + +#include "secutil.h" + +#if defined(XP_UNIX) +#include <unistd.h> +#endif +#include <stdlib.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> + +#include "plgetopt.h" + +#include "nspr.h" +#include "prio.h" +#include "prnetdb.h" +#include "prerror.h" + +#include "pk11func.h" +#include "secitem.h" +#include "sslproto.h" +#include "nss.h" +#include "ssl.h" + +#ifndef PORT_Sprintf +#define PORT_Sprintf sprintf +#endif + +#ifndef PORT_Strstr +#define PORT_Strstr strstr +#endif + +#ifndef PORT_Malloc +#define PORT_Malloc PR_Malloc +#endif + +#define RD_BUF_SIZE (60 * 1024) + +/* Include these cipher suite arrays to re-use tstclnt's + * cipher selection code. + */ + +int ssl2CipherSuites[] = { + SSL_EN_RC4_128_WITH_MD5, /* A */ + SSL_EN_RC4_128_EXPORT40_WITH_MD5, /* B */ + SSL_EN_RC2_128_CBC_WITH_MD5, /* C */ + SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5, /* D */ + SSL_EN_DES_64_CBC_WITH_MD5, /* E */ + SSL_EN_DES_192_EDE3_CBC_WITH_MD5, /* F */ + 0 +}; + +int ssl3CipherSuites[] = { + SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA, /* a */ + SSL_FORTEZZA_DMS_WITH_RC4_128_SHA, /* b */ + SSL_RSA_WITH_RC4_128_MD5, /* c */ + SSL_RSA_WITH_3DES_EDE_CBC_SHA, /* d */ + SSL_RSA_WITH_DES_CBC_SHA, /* e */ + SSL_RSA_EXPORT_WITH_RC4_40_MD5, /* f */ + SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5, /* g */ + SSL_FORTEZZA_DMS_WITH_NULL_SHA, /* h */ + SSL_RSA_WITH_NULL_MD5, /* i */ + SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, /* j */ + SSL_RSA_FIPS_WITH_DES_CBC_SHA, /* k */ + TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, /* l */ + TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, /* m */ + 0 +}; + +/* This global string is so that client main can see + * which ciphers to use. + */ + +const char *cipherString; + +int certsTested; +int MakeCertOK; + +void +disableSSL2Ciphers(void) +{ + int i; + + /* disable all the SSL2 cipher suites */ + for (i = 0; ssl2CipherSuites[i] != 0; ++i) { + SECStatus rv; + rv = SSL_EnableCipher(ssl2CipherSuites[i], SSL_NOT_ALLOWED); + if (rv != SECSuccess) { + fprintf(stderr, "SSL_EnableCipher failed with value 0x%04x\n", + ssl2CipherSuites[i]); + exit(1); + } + } +} + +void +disableSSL3Ciphers(void) +{ + int i; + + /* disable all the SSL3 cipher suites */ + for (i = 0; ssl3CipherSuites[i] != 0; ++i) { + SECStatus rv; + rv = SSL_EnableCipher(ssl3CipherSuites[i], SSL_NOT_ALLOWED); + if (rv != SECSuccess) { + fprintf(stderr, "SSL_EnableCipher failed with value 0x%04x\n", + ssl3CipherSuites[i]); + exit(1); + } + } +} + +char * ownPasswd( PK11SlotInfo *slot, PRBool retry, void *arg) +{ + char *passwd = NULL; + + if ( (!retry) && arg ) { + passwd = PL_strdup((char *)arg); + } + + return passwd; +} + +int stopping; +int verbose; +SECItem bigBuf; + +#define PRINTF if (verbose) printf +#define FPRINTF if (verbose) fprintf + +static void +Usage(const char *progName) +{ + fprintf(stderr, + "Usage: %s [-n rsa_nickname] [-p port] [-d dbdir] [-c connections]\n" + " [-v] [-f fortezza_nickname] [-2 filename]\n" + " [-w dbpasswd] [-C cipher(s)] hostname\n", + progName); + exit(1); +} + +static void +networkStart(void) +{ +#if defined(XP_WIN) && !defined(NSPR20) + + WORD wVersionRequested; + WSADATA wsaData; + int err; + wVersionRequested = MAKEWORD(1, 1); + + err = WSAStartup(wVersionRequested, &wsaData); + + if (err != 0) { + /* Tell the user that we couldn't find a useable winsock.dll. */ + fputs("WSAStartup failed!\n", stderr); + exit(1); + } + +/* Confirm that the Windows Sockets DLL supports 1.1.*/ +/* Note that if the DLL supports versions greater */ +/* than 1.1 in addition to 1.1, it will still return */ +/* 1.1 in wVersion since that is the version we */ +/* requested. */ + + if ( LOBYTE( wsaData.wVersion ) != 1 || + HIBYTE( wsaData.wVersion ) != 1 ) { + /* Tell the user that we couldn't find a useable winsock.dll. */ + fputs("wrong winsock version\n", stderr); + WSACleanup(); + exit(1); + } + /* The Windows Sockets DLL is acceptable. Proceed. */ + +#endif +} + +static void +networkEnd(void) +{ +#if defined(XP_WIN) && !defined(NSPR20) + WSACleanup(); +#endif +} + +static void +errWarn(char * funcString) +{ + PRErrorCode perr = PR_GetError(); + const char * errString = SECU_Strerror(perr); + + fprintf(stderr, "%s returned error %d:\n%s\n", + funcString, perr, errString); +} + +static void +errExit(char * funcString) +{ +#if defined (XP_WIN) && !defined(NSPR20) + int err; + LPVOID lpMsgBuf; + + err = WSAGetLastError(); + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPTSTR) &lpMsgBuf, + 0, + NULL + ); + + /* Display the string. */ + /*MessageBox( NULL, lpMsgBuf, "GetLastError", MB_OK|MB_ICONINFORMATION ); */ + fprintf(stderr, "%s\n", lpMsgBuf); + + /* Free the buffer. */ + LocalFree( lpMsgBuf ); +#endif + + errWarn(funcString); + exit(1); +} + +/* This invokes the "default" AuthCert handler in libssl. +** The only reason to use this one is that it prints out info as it goes. +*/ +static int +mySSLAuthCertificate(void *arg, PRFileDesc *fd, PRBool checkSig, + PRBool isServer) +{ + SECStatus rv; + CERTCertificate * peerCert; + + peerCert = SSL_PeerCertificate(fd); + + PRINTF("Subject: %s\nIssuer : %s\n", + peerCert->subjectName, peerCert->issuerName); + /* invoke the "default" AuthCert handler. */ + rv = SSL_AuthCertificate(arg, fd, checkSig, isServer); + + ++certsTested; + if (rv == SECSuccess) { + fputs("-- SSL: Server Certificate Validated.\n", stderr); + } + /* error, if any, will be displayed by the Bad Cert Handler. */ + return rv; +} + +static int /* should be SECStatus but public prototype says int. */ +myBadCertHandler( void *arg, PRFileDesc *fd) +{ + int err = PR_GetError(); + fprintf(stderr, "-- SSL: Server Certificate Invalid, err %d.\n%s\n", + err, SECU_Strerror(err)); + return (MakeCertOK ? SECSuccess : SECFailure); +} + +/* statistics from ssl3_SendClientHello (sch) */ +extern long ssl3_sch_sid_cache_hits; +extern long ssl3_sch_sid_cache_misses; +extern long ssl3_sch_sid_cache_not_ok; + +/* statistics from ssl3_HandleServerHello (hsh) */ +extern long ssl3_hsh_sid_cache_hits; +extern long ssl3_hsh_sid_cache_misses; +extern long ssl3_hsh_sid_cache_not_ok; + +/* statistics from ssl3_HandleClientHello (hch) */ +extern long ssl3_hch_sid_cache_hits; +extern long ssl3_hch_sid_cache_misses; +extern long ssl3_hch_sid_cache_not_ok; + +void +printSecurityInfo(PRFileDesc *fd) +{ + char * cp; /* bulk cipher name */ + char * ip; /* cert issuer DN */ + char * sp; /* cert subject DN */ + int op; /* High, Low, Off */ + int kp0; /* total key bits */ + int kp1; /* secret key bits */ + int result; + + static int only_once; + + if (! only_once++ && fd) { + result = SSL_SecurityStatus(fd, &op, &cp, &kp0, &kp1, &ip, &sp); + if (result != SECSuccess) + return; +#if 0 + PRINTF("bulk cipher %s, %d secret key bits, %d key bits, status: %d\n" + "subject DN: %s\n" + "issuer DN: %s\n", cp, kp1, kp0, op, sp, ip); +#else + PRINTF("bulk cipher %s, %d secret key bits, %d key bits, status: %d\n", + cp, kp1, kp0, op); +#endif + PR_Free(cp); + PR_Free(ip); + PR_Free(sp); + } + + PRINTF("%ld cache hits; %ld cache misses, %ld cache not reusable\n", + ssl3_hsh_sid_cache_hits, + ssl3_hsh_sid_cache_misses, + ssl3_hsh_sid_cache_not_ok); + +} + +/************************************************************************** +** Begin thread management routines and data. +**************************************************************************/ + +#define MAX_THREADS 32 + +typedef int startFn(void *a, void *b, int c); + +PRLock * threadLock; +PRCondVar * threadStartQ; +PRCondVar * threadEndQ; + +int numUsed; +int numRunning; + +typedef enum { rs_idle = 0, rs_running = 1, rs_zombie = 2 } runState; + +typedef struct perThreadStr { + void * a; + void * b; + int c; + int rv; + startFn * startFunc; + PRThread * prThread; + PRBool inUse; + runState running; +} perThread; + +perThread threads[MAX_THREADS]; + +void +thread_wrapper(void * arg) +{ + perThread * slot = (perThread *)arg; + + /* wait for parent to finish launching us before proceeding. */ + PR_Lock(threadLock); + PR_Unlock(threadLock); + + slot->rv = (* slot->startFunc)(slot->a, slot->b, slot->c); + + /* Handle cleanup of thread here. */ + PRINTF("Thread in slot %d returned %d\n", slot - threads, slot->rv); + + PR_Lock(threadLock); + slot->running = rs_idle; + --numRunning; + + /* notify the thread launcher. */ + PR_NotifyCondVar(threadStartQ); + + PR_Unlock(threadLock); +} + +SECStatus +launch_thread( + startFn * startFunc, + void * a, + void * b, + int c) +{ + perThread * slot; + int i; + + if (!threadStartQ) { + threadLock = PR_NewLock(); + threadStartQ = PR_NewCondVar(threadLock); + threadEndQ = PR_NewCondVar(threadLock); + } + PR_Lock(threadLock); + while (numRunning >= MAX_THREADS) { + PR_WaitCondVar(threadStartQ, PR_INTERVAL_NO_TIMEOUT); + } + for (i = 0; i < numUsed; ++i) { + slot = threads + i; + if (slot->running == rs_idle) + break; + } + if (i >= numUsed) { + if (i >= MAX_THREADS) { + /* something's really wrong here. */ + PORT_Assert(i < MAX_THREADS); + PR_Unlock(threadLock); + return SECFailure; + } + ++numUsed; + PORT_Assert(numUsed == i + 1); + slot = threads + i; + } + + slot->a = a; + slot->b = b; + slot->c = c; + + slot->startFunc = startFunc; + + slot->prThread = PR_CreateThread(PR_USER_THREAD, + thread_wrapper, slot, + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, + PR_UNJOINABLE_THREAD, 0); + if (slot->prThread == NULL) { + PR_Unlock(threadLock); + printf("Failed to launch thread!\n"); + return SECFailure; + } + + slot->inUse = 1; + slot->running = 1; + ++numRunning; + PR_Unlock(threadLock); + PRINTF("Launched thread in slot %d \n", i); + + return SECSuccess; +} + +/* Wait until num_running == 0 */ +int +reap_threads(void) +{ + perThread * slot; + int i; + + if (!threadLock) + return 0; + PR_Lock(threadLock); + while (numRunning > 0) { + PR_WaitCondVar(threadStartQ, PR_INTERVAL_NO_TIMEOUT); + } + + /* Safety Sam sez: make sure count is right. */ + for (i = 0; i < numUsed; ++i) { + slot = threads + i; + if (slot->running != rs_idle) { + FPRINTF(stderr, "Thread in slot %d is in state %d!\n", + i, slot->running); + } + } + PR_Unlock(threadLock); + return 0; +} + +void +destroy_thread_data(void) +{ + PORT_Memset(threads, 0, sizeof threads); + + if (threadEndQ) { + PR_DestroyCondVar(threadEndQ); + threadEndQ = NULL; + } + if (threadStartQ) { + PR_DestroyCondVar(threadStartQ); + threadStartQ = NULL; + } + if (threadLock) { + PR_DestroyLock(threadLock); + threadLock = NULL; + } +} + +/************************************************************************** +** End thread management routines. +**************************************************************************/ + +PRBool useModelSocket = PR_TRUE; + +static const char stopCmd[] = { "GET /stop " }; +static const char outHeader[] = { + "HTTP/1.0 200 OK\r\n" + "Server: Netscape-Enterprise/2.0a\r\n" + "Date: Tue, 26 Aug 1997 22:10:05 GMT\r\n" + "Content-type: text/plain\r\n" + "\r\n" +}; + +struct lockedVarsStr { + PRLock * lock; + int count; + int waiters; + PRCondVar * condVar; +}; + +typedef struct lockedVarsStr lockedVars; + +void +lockedVars_Init( lockedVars * lv) +{ + lv->count = 0; + lv->waiters = 0; + lv->lock = PR_NewLock(); + lv->condVar = PR_NewCondVar(lv->lock); +} + +void +lockedVars_Destroy( lockedVars * lv) +{ + PR_DestroyCondVar(lv->condVar); + lv->condVar = NULL; + + PR_DestroyLock(lv->lock); + lv->lock = NULL; +} + +void +lockedVars_WaitForDone(lockedVars * lv) +{ + PR_Lock(lv->lock); + while (lv->count > 0) { + PR_WaitCondVar(lv->condVar, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(lv->lock); +} + +int /* returns count */ +lockedVars_AddToCount(lockedVars * lv, int addend) +{ + int rv; + + PR_Lock(lv->lock); + rv = lv->count += addend; + if (rv <= 0) { + PR_NotifyCondVar(lv->condVar); + } + PR_Unlock(lv->lock); + return rv; +} + +int +do_writes( + void * a, + void * b, + int c) +{ + PRFileDesc * ssl_sock = (PRFileDesc *)a; + lockedVars * lv = (lockedVars *)b; + int sent = 0; + int count = 0; + + while (sent < bigBuf.len) { + + count = PR_Write(ssl_sock, bigBuf.data + sent, bigBuf.len - sent); + if (count < 0) { + errWarn("PR_Write bigBuf"); + break; + } + FPRINTF(stderr, "PR_Write wrote %d bytes from bigBuf\n", count ); + sent += count; + } + if (count >= 0) { /* last write didn't fail. */ + PR_Shutdown(ssl_sock, PR_SHUTDOWN_SEND); + } + + /* notify the reader that we're done. */ + lockedVars_AddToCount(lv, -1); + return (sent < bigBuf.len) ? SECFailure : SECSuccess; +} + +int +handle_fdx_connection( PRFileDesc * ssl_sock, int connection) +{ + SECStatus result; + int firstTime = 1; + int countRead = 0; + lockedVars lv; + char *buf; + + + lockedVars_Init(&lv); + lockedVars_AddToCount(&lv, 1); + + /* Attempt to launch the writer thread. */ + result = launch_thread(do_writes, ssl_sock, &lv, connection); + + if (result != SECSuccess) + goto cleanup; + + buf = PR_Malloc(RD_BUF_SIZE); + + if (buf) { + do { + /* do reads here. */ + PRInt32 count; + + count = PR_Read(ssl_sock, buf, RD_BUF_SIZE); + if (count < 0) { + errWarn("PR_Read"); + break; + } + countRead += count; + FPRINTF(stderr, "connection %d read %d bytes (%d total).\n", + connection, count, countRead ); + if (firstTime) { + firstTime = 0; + printSecurityInfo(ssl_sock); + } + } while (lockedVars_AddToCount(&lv, 0) > 0); + PR_Free(buf); + buf = 0; + } + + /* Wait for writer to finish */ + lockedVars_WaitForDone(&lv); + lockedVars_Destroy(&lv); + + FPRINTF(stderr, + "connection %d read %d bytes total. -----------------------------\n", + connection, countRead); + +cleanup: + /* Caller closes the socket. */ + + return SECSuccess; +} + +const char request[] = {"GET /abc HTTP/1.0\r\n\r\n" }; + +SECStatus +handle_connection( PRFileDesc *ssl_sock, int connection) +{ + int countRead = 0; + PRInt32 rv; + char *buf; + + buf = PR_Malloc(RD_BUF_SIZE); + if (!buf) + return SECFailure; + + /* compose the http request here. */ + + rv = PR_Write(ssl_sock, request, strlen(request)); + if (rv <= 0) { + errWarn("PR_Write"); + PR_Free(buf); + buf = 0; + return SECFailure; + } + printSecurityInfo(ssl_sock); + + /* read until EOF */ + while (1) { + rv = PR_Read(ssl_sock, buf, RD_BUF_SIZE); + if (rv == 0) { + break; /* EOF */ + } + if (rv < 0) { + errWarn("PR_Read"); + break; + } + + countRead += rv; + FPRINTF(stderr, "connection %d read %d bytes (%d total).\n", + connection, rv, countRead ); + } + PR_Free(buf); + buf = 0; + + /* Caller closes the socket. */ + + FPRINTF(stderr, + "connection %d read %d bytes total. -----------------------------\n", + connection, countRead); + + return SECSuccess; /* success */ +} + +/* one copy of this function is launched in a separate thread for each +** connection to be made. +*/ +int +do_connects( + void * a, + void * b, + int connection) +{ + PRNetAddr * addr = (PRNetAddr *) a; + PRFileDesc * model_sock = (PRFileDesc *) b; + PRFileDesc * ssl_sock = 0; + PRFileDesc * tcp_sock = 0; + PRStatus prStatus; + SECStatus result; + int rv = SECSuccess; + PRSocketOptionData opt; + +retry: + + tcp_sock = PR_NewTCPSocket(); + if (tcp_sock == NULL) { + errExit("PR_NewTCPSocket"); + } + + opt.option = PR_SockOpt_Nonblocking; + opt.value.non_blocking = PR_FALSE; + prStatus = PR_SetSocketOption(tcp_sock, &opt); + if (prStatus != PR_SUCCESS) { + PR_Close(tcp_sock); + return SECSuccess; + } + + prStatus = PR_Connect(tcp_sock, addr, PR_INTERVAL_NO_TIMEOUT); + if (prStatus != PR_SUCCESS) { + PRErrorCode err = PR_GetError(); + if ((err == PR_CONNECT_REFUSED_ERROR) || + (err == PR_CONNECT_RESET_ERROR) ) { + PR_Close(tcp_sock); + PR_Sleep(PR_MillisecondsToInterval(10)); + goto retry; + } + errWarn("PR_Connect"); + rv = SECFailure; + goto done; + } + + ssl_sock = SSL_ImportFD(model_sock, tcp_sock); + /* XXX if this import fails, close tcp_sock and return. */ + if (!ssl_sock) { + PR_Close(tcp_sock); + return SECSuccess; + } + + rv = SSL_ResetHandshake(ssl_sock, /* asServer */ 0); + if (rv != SECSuccess) { + errWarn("SSL_ResetHandshake"); + goto done; + } + + if (bigBuf.data != NULL) { + result = handle_fdx_connection( ssl_sock, connection); + } else { + result = handle_connection( ssl_sock, connection); + } + +done: + if (ssl_sock) { + PR_Close(ssl_sock); + } else if (tcp_sock) { + PR_Close(tcp_sock); + } + return SECSuccess; +} + +/* Returns IP address for hostname as PRUint32 in Host Byte Order. +** Since the value returned is an integer (not a string of bytes), +** it is inherently in Host Byte Order. +*/ +PRUint32 +getIPAddress(const char * hostName) +{ + const unsigned char *p; + PRStatus prStatus; + PRUint32 rv; + PRHostEnt prHostEnt; + char scratch[PR_NETDB_BUF_SIZE]; + + prStatus = PR_GetHostByName(hostName, scratch, sizeof scratch, &prHostEnt); + if (prStatus != PR_SUCCESS) + errExit("PR_GetHostByName"); + +#undef h_addr +#define h_addr h_addr_list[0] /* address, for backward compatibility */ + + p = (const unsigned char *)(prHostEnt.h_addr); /* in Network Byte order */ + FPRINTF(stderr, "%s -> %d.%d.%d.%d\n", hostName, p[0], p[1], p[2], p[3]); + rv = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; + return rv; +} + +void +client_main( + unsigned short port, + int connections, + SECKEYPrivateKey ** privKey, + CERTCertificate ** cert, + const char * hostName, + char * nickName) +{ + PRFileDesc *model_sock = NULL; + int i; + int rv; + PRUint32 ipAddress; /* in host byte order */ + PRNetAddr addr; + + networkStart(); + + /* Assemble NetAddr struct for connections. */ + ipAddress = getIPAddress(hostName); + + addr.inet.family = PR_AF_INET; + addr.inet.port = PR_htons(port); + addr.inet.ip = PR_htonl(ipAddress); + + /* all suites except RSA_NULL_MD5 are enabled by Domestic Policy */ + NSS_SetDomesticPolicy(); + +/* all the SSL2 and SSL3 cipher suites are enabled by default. */ + if (cipherString) { + int ndx; + + /* disable all the ciphers, then enable the ones we want. */ + disableSSL2Ciphers(); + disableSSL3Ciphers(); + + while (0 != (ndx = *cipherString++)) { + int *cptr; + int cipher; + + if (! isalpha(ndx)) + Usage("strsclnt"); + cptr = islower(ndx) ? ssl3CipherSuites : ssl2CipherSuites; + for (ndx &= 0x1f; (cipher = *cptr++) != 0 && --ndx > 0; ) + /* do nothing */; + if (cipher) { + SECStatus rv; + rv = SSL_EnableCipher(cipher, SSL_ALLOWED); + if (rv != SECSuccess) { + fprintf(stderr, + "SSL_EnableCipher failed with value 0x%04x\n", + cipher); + exit(1); + } + } + } + } + + /* configure model SSL socket. */ + + model_sock = PR_NewTCPSocket(); + if (model_sock == NULL) { + errExit("PR_NewTCPSocket on model socket"); + } + + model_sock = SSL_ImportFD(NULL, model_sock); + if (model_sock == NULL) { + errExit("SSL_ImportFD"); + } + + /* do SSL configuration. */ + + rv = SSL_Enable(model_sock, SSL_SECURITY, 1); + if (rv < 0) { + errExit("SSL_Enable SSL_SECURITY"); + } + + if (bigBuf.data) { /* doing FDX */ + rv = SSL_Enable(model_sock, SSL_ENABLE_FDX, 1); + if (rv < 0) { + errExit("SSL_Enable SSL_ENABLE_FDX"); + } + } + + SSL_SetURL(model_sock, hostName); + + SSL_AuthCertificateHook(model_sock, mySSLAuthCertificate, + (void *)CERT_GetDefaultCertDB()); + + SSL_BadCertHook(model_sock, myBadCertHandler, NULL); + + SSL_GetClientAuthDataHook(model_sock, NSS_GetClientAuthData, nickName); + + /* I'm not going to set the HandshakeCallback function. */ + + /* end of ssl configuration. */ + + rv = launch_thread(do_connects, &addr, model_sock, 1); + + if (connections > 1) { + /* wait for the first connection to terminate, then launch the rest. */ + reap_threads(); + /* Start up the connections */ + for (i = 2; i <= connections; ++i) { + + rv = launch_thread(do_connects, &addr, model_sock, i); + + } + } + + reap_threads(); + destroy_thread_data(); + + PR_Close(model_sock); + + networkEnd(); +} + +SECStatus +readBigFile(const char * fileName) +{ + PRFileInfo info; + PRStatus status; + SECStatus rv = SECFailure; + int count; + int hdrLen; + PRFileDesc *local_file_fd = NULL; + + status = PR_GetFileInfo(fileName, &info); + + if (status == PR_SUCCESS && + info.type == PR_FILE_FILE && + info.size > 0 && + NULL != (local_file_fd = PR_Open(fileName, PR_RDONLY, 0))) { + + hdrLen = PORT_Strlen(outHeader); + bigBuf.len = hdrLen + info.size; + bigBuf.data = PORT_Malloc(bigBuf.len + 4095); + if (!bigBuf.data) { + errWarn("PORT_Malloc"); + goto done; + } + + PORT_Memcpy(bigBuf.data, outHeader, hdrLen); + + count = PR_Read(local_file_fd, bigBuf.data + hdrLen, info.size); + if (count != info.size) { + errWarn("PR_Read local file"); + goto done; + } + rv = SECSuccess; +done: + PR_Close(local_file_fd); + } + return rv; +} + +int +main(int argc, char **argv) +{ + const char * dir = "."; + char * fNickName = NULL; + const char * fileName = NULL; + char * hostName = NULL; + char * nickName = NULL; + char * progName = NULL; + char * tmp = NULL; + char * passwd = NULL; + CERTCertificate * cert [kt_kea_size] = { NULL }; + SECKEYPrivateKey * privKey[kt_kea_size] = { NULL }; + int connections = 1; + int exitVal; + unsigned short port = 443; + SECStatus rv; + PLOptState * optstate; + PLOptStatus status; + + /* Call the NSPR initialization routines */ + PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); + + tmp = strrchr(argv[0], '/'); + tmp = tmp ? tmp + 1 : argv[0]; + progName = strrchr(tmp, '\\'); + progName = progName ? progName + 1 : tmp; + + + optstate = PL_CreateOptState(argc, argv, "2:C:c:d:f:n:op:vw:"); + while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { + switch(optstate->option) { + + case '2': + fileName = optstate->value; + break; + case 'C': + cipherString = optstate->value; + break; + + case 'c': + connections = PORT_Atoi(optstate->value); + break; + + case 'd': + dir = optstate->value; + break; + + case 'f': + fNickName = optstate->value; + break; + + case 'n': + nickName = optstate->value; + break; + case 'o': + MakeCertOK = 1; + break; + + case 'p': + port = PORT_Atoi(optstate->value); + break; + + case 'v': + verbose++; + break; + case 'w': + passwd = optstate->value; + break; + case '\0': + hostName = PL_strdup(optstate->value); + break; + default: + case '?': + Usage(progName); + break; + + } + } + if (!hostName || status == PL_OPT_BAD) + Usage(progName); + + if (port == 0) + Usage(progName); + + if (fileName) + readBigFile(fileName); + + /* set our password function */ + if ( passwd ) { + PK11_SetPasswordFunc(ownPasswd); + } else { + PK11_SetPasswordFunc(SECU_GetModulePassword); + } + + /* Call the libsec initialization routines */ + rv = NSS_Init(dir); + if (rv != SECSuccess) { + fputs("NSS_Init failed.\n", stderr); + exit(1); + } + + if (nickName) { + + cert[kt_rsa] = PK11_FindCertFromNickname(nickName, passwd); + if (cert[kt_rsa] == NULL) { + fprintf(stderr, "Can't find certificate %s\n", nickName); + exit(1); + } + + privKey[kt_rsa] = PK11_FindKeyByAnyCert(cert[kt_rsa], passwd); + if (privKey[kt_rsa] == NULL) { + fprintf(stderr, "Can't find Private Key for cert %s\n", nickName); + exit(1); + } + + } + if (fNickName) { + cert[kt_fortezza] = PK11_FindCertFromNickname(fNickName, passwd); + if (cert[kt_fortezza] == NULL) { + fprintf(stderr, "Can't find certificate %s\n", fNickName); + exit(1); + } + + privKey[kt_fortezza] = PK11_FindKeyByAnyCert(cert[kt_fortezza], passwd); + if (privKey[kt_fortezza] == NULL) { + fprintf(stderr, "Can't find Private Key for cert %s\n", fNickName); + exit(1); + } + } + + client_main(port, connections, privKey, cert, hostName, nickName); + + /* some final stats. */ + if (ssl3_hsh_sid_cache_hits + ssl3_hsh_sid_cache_misses + + ssl3_hsh_sid_cache_not_ok == 0) { + /* presumably we were testing SSL2. */ + printf("%d server certificates tested.\n", certsTested); + } else { + printf("%ld cache hits; %ld cache misses, %ld cache not reusable\n", + ssl3_hsh_sid_cache_hits, + ssl3_hsh_sid_cache_misses, + ssl3_hsh_sid_cache_not_ok); + } + exitVal = (ssl3_hsh_sid_cache_misses > 1) || + (ssl3_hsh_sid_cache_not_ok != 0) || + (certsTested > 1); + + NSS_Shutdown(); + PR_Cleanup(); + return exitVal; +} + diff --git a/security/nss/lib/fortcrypt/genci.h b/security/nss/lib/fortcrypt/genci.h new file mode 100644 index 000000000..f12ec9a77 --- /dev/null +++ b/security/nss/lib/fortcrypt/genci.h @@ -0,0 +1,145 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ +/* + * the following header file switches between MACI and CI based on + * compile options. That lest the rest of the source code operate + * without change, even if it only suports CI_ calls, not MACI_ calls + */ +#ifndef _GENCI_H_ +#define _GENCI_H_ 1 +#include "seccomon.h" + +#if defined (XP_UNIX) || defined (XP_WIN32) || defined (XP_OS2) + +/* + * On unix, NT, Windows '95, and OS/2 we use full maci + */ +#include "maci.h" + +#define MACI_SEL(x) + +/* + * for sec-for.c + */ +#define CI_Initialize MACI_Initialize +#define CI_Terminate() { HSESSION hs;\ + MACI_GetSessionID(&hs);\ + MACI_Terminate(hs); } + +#else + +/* + * On Mac we use the original CI_LIB + */ +#include "cryptint.h" + +/* + * MACI specific values not defined for CI lib + */ +#define MACI_SESSION_EXCEEDED (-53) + +#ifndef HSESSION_DEFINE +typedef unsigned int HSESSION; +#define HSESSION_DEFINE +#endif + +/* + * Map MACI_ calls to CI_ calls. NOTE: this assumes the proper CI_Select + * calls are issued in the CI_ case + */ +#define MACI_ChangePIN(s,pin,old,new) CI_ChangePIN(pin,old,new) +#define MACI_CheckPIN(s,type,pin) CI_CheckPIN(type,pin) +#define MACI_Close(s,flag,socket) CI_Close(flag,socket) +#define MACI_Decrypt(s,size,in,out) CI_Decrypt(size,in,out) +#define MACI_DeleteCertificate(s,cert) CI_DeleteCertificate(cert) +#define MACI_DeleteKey(s,index) CI_DeleteKey(index) +#define MACI_Encrypt(s,size,in,out) CI_Encrypt(size,in,out) +#define MACI_ExtractX(s,cert,type,pass,ySize,y,x,Ra,pgSize,qSize,p,q,g) \ + CI_ExtractX(cert,type,pass,ySize,y,x,Ra,pgSize,qSize,p,q,g) +#define MACI_FirmwareUpdate(s,flags,Cksum,len,size,data) \ + CI_FirmwareUpdate(flags,Cksum,len,size,data) +#define MACI_GenerateIV(s,iv) CI_GenerateIV(iv) +#define MACI_GenerateMEK(s,index,res) CI_GenerateMEK(index,res) +#define MACI_GenerateRa(s,Ra) CI_GenerateRa(Ra) +#define MACI_GenerateRandom(s,ran) CI_GenerateRandom(ran) +#define MACI_GenerateTEK(s,flag,index,Ra,Rb,size,Y) \ + CI_GenerateTEK(flag,index,Ra,Rb,size,Y) +#define MACI_GenerateX(s,cert,type,pgSize,qSize,p,q,g,ySize,y) \ + CI_GenerateX(cert,type,pgSize,qSize,p,q,g,ySize,y) +#define MACI_GetCertificate(s,cert,val) CI_GetCertificate(cert,val) +#define MACI_GetConfiguration(s,config) CI_GetConfiguration(config) +#define MACI_GetHash(s,size,data,val) CI_GetHash(size,data,val) +#define MACI_GetPersonalityList(s,cnt,list) CI_GetPersonalityList(cnt,list) +#define MACI_GetSessionID(s) CI_OK +#define MACI_GetState(s,state) CI_GetState(state) +#define MACI_GetStatus(s,status) CI_GetStatus(status) +#define MACI_GetTime(s,time) CI_GetTime(time) +#define MACI_Hash(s,size,data) CI_Hash(size,data) +#define MACI_Initialize(count) CI_Initialize(count) +#define MACI_InitializeHash(s) CI_InitializeHash() +#define MACI_InstallX(s,cert,type,pass,ySize,y,x,Ra,pgSize,qSize,p,q,g) \ + CI_InstallX(cert,type,pass,ySize,y,x,Ra,pgSize,qSize,p,q,g) +#define MACI_LoadCertificate(s,cert,label,data,res) \ + CI_LoadCertificate(cert,label,data,res) +#define MACI_LoadDSAParameters(s,pgSize,qSize,p,q,g) \ + CI_LoadDSAParameters(pgSize,qSize,p,q,g) +#define MACI_LoadInitValues(s,seed,Ks) CI_LoadInitValues(seed,Ks) +#define MACI_LoadIV(s,iv) CI_LoadIV(iv) +#define MACI_LoadX(s,cert,type,pgSize,qSize,p,q,g,x,ySize,y) \ + CI_LoadX(cert,type,pgSize,qSize,p,q,g,x,ySize,y) +#define MACI_Lock(s,flags) CI_Lock(flags) +#define MACI_Open(s,flags,index) CI_Open(flags,index) +#define MACI_RelayX(s,oPass,oSize,oY,oRa,oX,nPass,nSize,nY,nRa,nX) \ + CI_RelayX(oPass,oSize,oY,oRa,oX,nPass,nSize,nY,nRa,nX) +#define MACI_Reset(s) CI_Reset() +#define MACI_Restore(s,type,data) CI_Restore(type,data) +#define MACI_Save(s,type,data) CI_Save(type,data) +#define MACI_Select(s,socket) CI_Select(socket) +#define MACI_SetConfiguration(s,typ,sz,d) CI_SetConfiguration(typ,sz,d) +#define MACI_SetKey(s,key) CI_SetKey(key) +#define MACI_SetMode(s,type,mode) CI_SetMode(type,mode) +#define MACI_SetPersonality(s,index) CI_SetPersonality(index) +#define MACI_SetTime(s,time) CI_SetTime(time) +#define MACI_Sign(s,hash,sig) CI_Sign(hash,sig) +#define MACI_Terminate(s) CI_Terminate() +#define MACI_TimeStamp(s,val,sig,time) CI_TimeStamp(val,sig,time) +#define MACI_Unlock(s) CI_Unlock() +#define MACI_UnwrapKey(s,targ,wrap,key) CI_UnwrapKey(targ,wrap,key) +#define MACI_VerifySignature(s,h,siz,y,sig) CI_VerifySignature(h,siz,y,sig) +#define MACI_VerifyTimeStamp(s,hash,sig,tim) CI_VerityTimeStap(hash,sig,tim) +#define MACI_WrapKey(s,src,wrap,key) CI_WrapKey(src,wrap,key) +#define MACI_Zeroize(s) CI_Zeroize() + +#define MACI_SEL(x) CI_Select(x) +#endif /* ! XP_UNIX */ +#endif /* _GENCI_H_ */ diff --git a/security/nss/lib/jar/jarevil.c b/security/nss/lib/jar/jarevil.c new file mode 100644 index 000000000..08fa1ee6c --- /dev/null +++ b/security/nss/lib/jar/jarevil.c @@ -0,0 +1,571 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * JAREVIL + * + * Wrappers to callback in the mozilla thread + * + * Certificate code is unsafe when called outside the + * mozilla thread. These functions push an event on the + * queue to cause the cert function to run in that thread. + * + */ + +#include "jar.h" +#include "jarint.h" + +#include "jarevil.h" + +/* from libevent.h */ +#ifdef MOZILLA_CLIENT_OLD +typedef void (*ETVoidPtrFunc) (void * data); +extern void ET_moz_CallFunction (ETVoidPtrFunc fn, void *data); + +extern void *mozilla_event_queue; +#endif + + +/* Special macros facilitate running on Win 16 */ +#if defined(XP_PC) && !defined(_WIN32) /* then we are win 16 */ + + /* + * Allocate the data passed to the callback functions from the heap... + * + * This inter-thread structure cannot reside on a thread stack since the + * thread's stack is swapped away with the thread under Win16... + */ + + #define ALLOC_OR_DEFINE(type, pointer_var_name, out_of_memory_return_value) \ + type * pointer_var_name = PORT_ZAlloc (sizeof(type)); \ + do { \ + if (!pointer_var_name) \ + return (out_of_memory_return_value); \ + } while (0) /* and now a semicolon can follow :-) */ + + #define FREE_IF_ALLOC_IS_USED(pointer_var_name) PORT_Free(pointer_var_name) + +#else /* not win 16... so we can alloc via auto variables */ + + #define ALLOC_OR_DEFINE(type, pointer_var_name, out_of_memory_return_value) \ + type actual_structure_allocated_in_macro; \ + type * pointer_var_name = &actual_structure_allocated_in_macro; \ + PORT_Memset (pointer_var_name, 0, sizeof (*pointer_var_name)); \ + ((void) 0) /* and now a semicolon can follow */ + + #define FREE_IF_ALLOC_IS_USED(pointer_var_name) ((void) 0) + +#endif /* not Win 16 */ + +/* --- --- --- --- --- --- --- --- --- --- --- --- --- */ + +/* + * JAR_MOZ_encode + * + * Call SEC_PKCS7Encode inside + * the mozilla thread + * + */ + +struct EVIL_encode + { + int error; + SECStatus status; + SEC_PKCS7ContentInfo *cinfo; + SEC_PKCS7EncoderOutputCallback outputfn; + void *outputarg; + PK11SymKey *bulkkey; + SECKEYGetPasswordKey pwfn; + void *pwfnarg; + }; + + +/* This is called inside the mozilla thread */ + +PR_STATIC_CALLBACK(void) jar_moz_encode_fn (void *data) + { + SECStatus status; + struct EVIL_encode *encode_data = (struct EVIL_encode *)data; + + PORT_SetError (encode_data->error); + + status = SEC_PKCS7Encode (encode_data->cinfo, encode_data->outputfn, + encode_data->outputarg, encode_data->bulkkey, + encode_data->pwfn, encode_data->pwfnarg); + + encode_data->status = status; + encode_data->error = PORT_GetError(); + } + + +/* Wrapper for the ET_MOZ call */ + +SECStatus jar_moz_encode + ( + SEC_PKCS7ContentInfo *cinfo, + SEC_PKCS7EncoderOutputCallback outputfn, + void *outputarg, + PK11SymKey *bulkkey, + SECKEYGetPasswordKey pwfn, + void *pwfnarg + ) + { + SECStatus ret; + ALLOC_OR_DEFINE(struct EVIL_encode, encode_data, SECFailure); + + encode_data->error = PORT_GetError(); + encode_data->cinfo = cinfo; + encode_data->outputfn = outputfn; + encode_data->outputarg = outputarg; + encode_data->bulkkey = bulkkey; + encode_data->pwfn = pwfn; + encode_data->pwfnarg = pwfnarg; + + /* Synchronously invoke the callback function on the mozilla thread. */ +#ifdef MOZILLA_CLIENT_OLD + if (mozilla_event_queue) + ET_moz_CallFunction (jar_moz_encode_fn, encode_data); + else + jar_moz_encode_fn (encode_data); +#else + jar_moz_encode_fn (encode_data); +#endif + + PORT_SetError (encode_data->error); + ret = encode_data->status; + + /* Free the data passed to the callback function... */ + FREE_IF_ALLOC_IS_USED(encode_data); + return ret; + } + +/* --- --- --- --- --- --- --- --- --- --- --- --- --- */ + +/* + * JAR_MOZ_verify + * + * Call SEC_PKCS7VerifyDetachedSignature inside + * the mozilla thread + * + */ + +struct EVIL_verify + { + int error; + SECStatus status; + SEC_PKCS7ContentInfo *cinfo; + SECCertUsage certusage; + SECItem *detached_digest; + HASH_HashType digest_type; + PRBool keepcerts; + }; + +/* This is called inside the mozilla thread */ + +PR_STATIC_CALLBACK(void) jar_moz_verify_fn (void *data) + { + PRBool result; + struct EVIL_verify *verify_data = (struct EVIL_verify *)data; + + PORT_SetError (verify_data->error); + + result = SEC_PKCS7VerifyDetachedSignature + (verify_data->cinfo, verify_data->certusage, verify_data->detached_digest, + verify_data->digest_type, verify_data->keepcerts); + + + verify_data->status = result==PR_TRUE ? SECSuccess : SECFailure; + verify_data->error = PORT_GetError(); + } + + +/* Wrapper for the ET_MOZ call */ + +SECStatus jar_moz_verify + ( + SEC_PKCS7ContentInfo *cinfo, + SECCertUsage certusage, + SECItem *detached_digest, + HASH_HashType digest_type, + PRBool keepcerts + ) + { + SECStatus ret; + ALLOC_OR_DEFINE(struct EVIL_verify, verify_data, SECFailure); + + verify_data->error = PORT_GetError(); + verify_data->cinfo = cinfo; + verify_data->certusage = certusage; + verify_data->detached_digest = detached_digest; + verify_data->digest_type = digest_type; + verify_data->keepcerts = keepcerts; + + /* Synchronously invoke the callback function on the mozilla thread. */ +#ifdef MOZILLA_CLIENT_OLD + if (mozilla_event_queue) + ET_moz_CallFunction (jar_moz_verify_fn, verify_data); + else + jar_moz_verify_fn (verify_data); +#else + jar_moz_verify_fn (verify_data); +#endif + + PORT_SetError (verify_data->error); + ret = verify_data->status; + + /* Free the data passed to the callback function... */ + FREE_IF_ALLOC_IS_USED(verify_data); + return ret; + } + +/* --- --- --- --- --- --- --- --- --- --- --- --- --- */ + +/* + * JAR_MOZ_nickname + * + * Call CERT_FindCertByNickname inside + * the mozilla thread + * + */ + +struct EVIL_nickname + { + int error; + CERTCertDBHandle *certdb; + char *nickname; + CERTCertificate *cert; + }; + + +/* This is called inside the mozilla thread */ + +PR_STATIC_CALLBACK(void) jar_moz_nickname_fn (void *data) + { + CERTCertificate *cert; + struct EVIL_nickname *nickname_data = (struct EVIL_nickname *)data; + + PORT_SetError (nickname_data->error); + + cert = CERT_FindCertByNickname (nickname_data->certdb, nickname_data->nickname); + + nickname_data->cert = cert; + nickname_data->error = PORT_GetError(); + } + + +/* Wrapper for the ET_MOZ call */ + +CERTCertificate *jar_moz_nickname (CERTCertDBHandle *certdb, char *nickname) + { + CERTCertificate *cert; + ALLOC_OR_DEFINE(struct EVIL_nickname, nickname_data, NULL ); + + nickname_data->error = PORT_GetError(); + nickname_data->certdb = certdb; + nickname_data->nickname = nickname; + + /* Synchronously invoke the callback function on the mozilla thread. */ +#ifdef MOZILLA_CLIENT_OLD + if (mozilla_event_queue) + ET_moz_CallFunction (jar_moz_nickname_fn, nickname_data); + else + jar_moz_nickname_fn (nickname_data); +#else + jar_moz_nickname_fn (nickname_data); +#endif + + PORT_SetError (nickname_data->error); + cert = nickname_data->cert; + + /* Free the data passed to the callback function... */ + FREE_IF_ALLOC_IS_USED(nickname_data); + return cert; + } + +/* --- --- --- --- --- --- --- --- --- --- --- --- --- */ + +/* + * JAR_MOZ_perm + * + * Call CERT_AddTempCertToPerm inside + * the mozilla thread + * + */ + +struct EVIL_perm + { + int error; + SECStatus status; + CERTCertificate *cert; + char *nickname; + CERTCertTrust *trust; + }; + + +/* This is called inside the mozilla thread */ + +PR_STATIC_CALLBACK(void) jar_moz_perm_fn (void *data) + { + SECStatus status; + struct EVIL_perm *perm_data = (struct EVIL_perm *)data; + + PORT_SetError (perm_data->error); + + status = CERT_AddTempCertToPerm (perm_data->cert, perm_data->nickname, perm_data->trust); + + perm_data->status = status; + perm_data->error = PORT_GetError(); + } + + +/* Wrapper for the ET_MOZ call */ + +SECStatus jar_moz_perm + (CERTCertificate *cert, char *nickname, CERTCertTrust *trust) + { + SECStatus ret; + ALLOC_OR_DEFINE(struct EVIL_perm, perm_data, SECFailure); + + perm_data->error = PORT_GetError(); + perm_data->cert = cert; + perm_data->nickname = nickname; + perm_data->trust = trust; + + /* Synchronously invoke the callback function on the mozilla thread. */ +#ifdef MOZILLA_CLIENT_OLD + if (mozilla_event_queue) + ET_moz_CallFunction (jar_moz_perm_fn, perm_data); + else + jar_moz_perm_fn (perm_data); +#else + jar_moz_perm_fn (perm_data); +#endif + + PORT_SetError (perm_data->error); + ret = perm_data->status; + + /* Free the data passed to the callback function... */ + FREE_IF_ALLOC_IS_USED(perm_data); + return ret; + } + +/* --- --- --- --- --- --- --- --- --- --- --- --- --- */ + +/* + * JAR_MOZ_certkey + * + * Call CERT_FindCertByKey inside + * the mozilla thread + * + */ + +struct EVIL_certkey + { + int error; + CERTCertificate *cert; + CERTCertDBHandle *certdb; + SECItem *seckey; + }; + + +/* This is called inside the mozilla thread */ + +PR_STATIC_CALLBACK(void) jar_moz_certkey_fn (void *data) + { + CERTCertificate *cert; + struct EVIL_certkey *certkey_data = (struct EVIL_certkey *)data; + + PORT_SetError (certkey_data->error); + + cert = CERT_FindCertByKey (certkey_data->certdb, certkey_data->seckey); + + certkey_data->cert = cert; + certkey_data->error = PORT_GetError(); + } + + +/* Wrapper for the ET_MOZ call */ + +CERTCertificate *jar_moz_certkey (CERTCertDBHandle *certdb, SECItem *seckey) + { + CERTCertificate *cert; + ALLOC_OR_DEFINE(struct EVIL_certkey, certkey_data, NULL); + + certkey_data->error = PORT_GetError(); + certkey_data->certdb = certdb; + certkey_data->seckey = seckey; + + /* Synchronously invoke the callback function on the mozilla thread. */ +#ifdef MOZILLA_CLIENT_OLD + if (mozilla_event_queue) + ET_moz_CallFunction (jar_moz_certkey_fn, certkey_data); + else + jar_moz_certkey_fn (certkey_data); +#else + jar_moz_certkey_fn (certkey_data); +#endif + + PORT_SetError (certkey_data->error); + cert = certkey_data->cert; + + /* Free the data passed to the callback function... */ + FREE_IF_ALLOC_IS_USED(certkey_data); + return cert; + } + +/* --- --- --- --- --- --- --- --- --- --- --- --- --- */ + +/* + * JAR_MOZ_issuer + * + * Call CERT_FindCertIssuer inside + * the mozilla thread + * + */ + +struct EVIL_issuer + { + int error; + CERTCertificate *cert; + CERTCertificate *issuer; + }; + + +/* This is called inside the mozilla thread */ + +PR_STATIC_CALLBACK(void) jar_moz_issuer_fn (void *data) + { + CERTCertificate *issuer; + struct EVIL_issuer *issuer_data = (struct EVIL_issuer *)data; + + PORT_SetError (issuer_data->error); + + issuer = CERT_FindCertIssuer (issuer_data->cert, PR_Now(), + certUsageObjectSigner); + + issuer_data->issuer = issuer; + issuer_data->error = PORT_GetError(); + } + + +/* Wrapper for the ET_MOZ call */ + +CERTCertificate *jar_moz_issuer (CERTCertificate *cert) + { + CERTCertificate *issuer_cert; + ALLOC_OR_DEFINE(struct EVIL_issuer, issuer_data, NULL); + + issuer_data->error = PORT_GetError(); + issuer_data->cert = cert; + + /* Synchronously invoke the callback function on the mozilla thread. */ +#ifdef MOZILLA_CLIENT_OLD + if (mozilla_event_queue) + ET_moz_CallFunction (jar_moz_issuer_fn, issuer_data); + else + jar_moz_issuer_fn (issuer_data); +#else + jar_moz_issuer_fn (issuer_data); +#endif + + PORT_SetError (issuer_data->error); + issuer_cert = issuer_data->issuer; + + /* Free the data passed to the callback function... */ + FREE_IF_ALLOC_IS_USED(issuer_data); + return issuer_cert; + } + +/* --- --- --- --- --- --- --- --- --- --- --- --- --- */ + +/* + * JAR_MOZ_dup + * + * Call CERT_DupCertificate inside + * the mozilla thread + * + */ + +struct EVIL_dup + { + int error; + CERTCertificate *cert; + CERTCertificate *return_cert; + }; + + +/* This is called inside the mozilla thread */ + +PR_STATIC_CALLBACK(void) jar_moz_dup_fn (void *data) + { + CERTCertificate *return_cert; + struct EVIL_dup *dup_data = (struct EVIL_dup *)data; + + PORT_SetError (dup_data->error); + + return_cert = CERT_DupCertificate (dup_data->cert); + + dup_data->return_cert = return_cert; + dup_data->error = PORT_GetError(); + } + + +/* Wrapper for the ET_MOZ call */ + +CERTCertificate *jar_moz_dup (CERTCertificate *cert) + { + CERTCertificate *dup_cert; + ALLOC_OR_DEFINE(struct EVIL_dup, dup_data, NULL); + + dup_data->error = PORT_GetError(); + dup_data->cert = cert; + + /* Synchronously invoke the callback function on the mozilla thread. */ +#ifdef MOZILLA_CLIENT_OLD + if (mozilla_event_queue) + ET_moz_CallFunction (jar_moz_dup_fn, dup_data); + else + jar_moz_dup_fn (dup_data); +#else + jar_moz_dup_fn (dup_data); +#endif + + PORT_SetError (dup_data->error); + dup_cert = dup_data->return_cert; + + /* Free the data passed to the callback function... */ + FREE_IF_ALLOC_IS_USED(dup_data); + return dup_cert; + } + +/* --- --- --- --- --- --- --- --- --- --- --- --- --- */ diff --git a/security/nss/lib/jar/jarnav.c b/security/nss/lib/jar/jarnav.c new file mode 100644 index 000000000..865ded5da --- /dev/null +++ b/security/nss/lib/jar/jarnav.c @@ -0,0 +1,107 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * JARNAV.C + * + * JAR stuff needed for client only. + * + */ + +#include "jar.h" +#include "jarint.h" + +/* from proto.h */ +#ifdef MOZILLA_CLIENT_OLD +extern MWContext *XP_FindSomeContext(void); +#endif + +/* sigh */ +extern MWContext *FE_GetInitContext(void); + +/* To return an MWContext for Java */ +static MWContext *(*jar_fn_FindSomeContext) (void) = NULL; + +/* To fabricate an MWContext for FE_GetPassword */ +static MWContext *(*jar_fn_GetInitContext) (void) = NULL; + +/* + * J A R _ i n i t + * + * Initialize the JAR functions. + * + */ + +void JAR_init (void) + { +#ifdef MOZILLA_CLIENT_OLD + JAR_init_callbacks (XP_GetString, XP_FindSomeContext, FE_GetInitContext); +#else + JAR_init_callbacks (XP_GetString, NULL, NULL); +#endif + } + +/* + * J A R _ s e t _ c o n t e x t + * + * Set the jar window context for use by PKCS11, since + * it may be needed to prompt the user for a password. + * + */ + +int JAR_set_context (JAR *jar, MWContext *mw) + { + if (mw) + { + jar->mw = mw; + } + else + { + /* jar->mw = XP_FindSomeContext(); */ + jar->mw = NULL; + + /* + * We can't find a context because we're in startup state and none + * exist yet. go get an FE_InitContext that only works at initialization + * time. + */ + + /* Turn on the mac when we get the FE_ function */ + if (jar->mw == NULL) + { + jar->mw = jar_fn_GetInitContext(); + } + } + + return 0; + } diff --git a/security/nss/lib/jar/jarsign.c b/security/nss/lib/jar/jarsign.c new file mode 100644 index 000000000..d03f980b0 --- /dev/null +++ b/security/nss/lib/jar/jarsign.c @@ -0,0 +1,377 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * JARSIGN + * + * Routines used in signing archives. + */ + + +#define USE_MOZ_THREAD + +#include "jar.h" +#include "jarint.h" + +#ifdef USE_MOZ_THREAD +#include "jarevil.h" +#endif + +#include "pk11func.h" + +/* from libevent.h */ +typedef void (*ETVoidPtrFunc) (void * data); + +#ifdef MOZILLA_CLIENT_OLD + +extern void ET_moz_CallFunction (ETVoidPtrFunc fn, void *data); + +/* from proto.h */ +/* extern MWContext *XP_FindSomeContext(void); */ +extern void *XP_FindSomeContext(void); + +#endif + +/* key database wrapper */ + +/* static SECKEYKeyDBHandle *jar_open_key_database (void); */ + +/* CHUNQ is our bite size */ + +#define CHUNQ 64000 +#define FILECHUNQ 32768 + +/* + * J A R _ c a l c u l a t e _ d i g e s t + * + * Quick calculation of a digest for + * the specified block of memory. Will calculate + * for all supported algorithms, now MD5. + * + * This version supports huge pointers for WIN16. + * + */ + +JAR_Digest * PR_CALLBACK JAR_calculate_digest (void ZHUGEP *data, long length) + { + long chunq; + JAR_Digest *dig; + + unsigned int md5_length, sha1_length; + + PK11Context *md5 = 0; + PK11Context *sha1 = 0; + + dig = (JAR_Digest *) PORT_ZAlloc (sizeof (JAR_Digest)); + + if (dig == NULL) + { + /* out of memory allocating digest */ + return NULL; + } + +#if defined(XP_WIN16) + PORT_Assert ( !IsBadHugeReadPtr(data, length) ); +#endif + + md5 = PK11_CreateDigestContext (SEC_OID_MD5); + sha1 = PK11_CreateDigestContext (SEC_OID_SHA1); + + if (length >= 0) + { + PK11_DigestBegin (md5); + PK11_DigestBegin (sha1); + + do { + chunq = length; + +#ifdef XP_WIN16 + if (length > CHUNQ) chunq = CHUNQ; + + /* + * If the block of data crosses one or more segment + * boundaries then only pass the chunk of data in the + * first segment. + * + * This allows the data to be treated as FAR by the + * PK11_DigestOp(...) routine. + * + */ + + if (OFFSETOF(data) + chunq >= 0x10000) + chunq = 0x10000 - OFFSETOF(data); +#endif + + PK11_DigestOp (md5, (unsigned char*)data, chunq); + PK11_DigestOp (sha1, (unsigned char*)data, chunq); + + length -= chunq; + data = ((char ZHUGEP *) data + chunq); + } + while (length > 0); + + PK11_DigestFinal (md5, dig->md5, &md5_length, MD5_LENGTH); + PK11_DigestFinal (sha1, dig->sha1, &sha1_length, SHA1_LENGTH); + + PK11_DestroyContext (md5, PR_TRUE); + PK11_DestroyContext (sha1, PR_TRUE); + } + + return dig; + } + +/* + * J A R _ d i g e s t _ f i l e + * + * Calculates the MD5 and SHA1 digests for a file + * present on disk, and returns these in JAR_Digest struct. + * + */ + +int JAR_digest_file (char *filename, JAR_Digest *dig) + { + JAR_FILE fp; + + int num; + unsigned char *buf; + + PK11Context *md5 = 0; + PK11Context *sha1 = 0; + + unsigned int md5_length, sha1_length; + + buf = (unsigned char *) PORT_ZAlloc (FILECHUNQ); + if (buf == NULL) + { + /* out of memory */ + return JAR_ERR_MEMORY; + } + + if ((fp = JAR_FOPEN (filename, "rb")) == 0) + { + /* perror (filename); FIX XXX XXX XXX XXX XXX XXX */ + PORT_Free (buf); + return JAR_ERR_FNF; + } + + md5 = PK11_CreateDigestContext (SEC_OID_MD5); + sha1 = PK11_CreateDigestContext (SEC_OID_SHA1); + + if (md5 == NULL || sha1 == NULL) + { + /* can't generate digest contexts */ + PORT_Free (buf); + JAR_FCLOSE (fp); + return JAR_ERR_GENERAL; + } + + PK11_DigestBegin (md5); + PK11_DigestBegin (sha1); + + while (1) + { + if ((num = JAR_FREAD (fp, buf, FILECHUNQ)) == 0) + break; + + PK11_DigestOp (md5, buf, num); + PK11_DigestOp (sha1, buf, num); + } + + PK11_DigestFinal (md5, dig->md5, &md5_length, MD5_LENGTH); + PK11_DigestFinal (sha1, dig->sha1, &sha1_length, SHA1_LENGTH); + + PK11_DestroyContext (md5, PR_TRUE); + PK11_DestroyContext (sha1, PR_TRUE); + + PORT_Free (buf); + JAR_FCLOSE (fp); + + return 0; + } + +/* + * J A R _ o p e n _ k e y _ d a t a b a s e + * + */ + +SECKEYKeyDBHandle *jar_open_key_database (void) + { + SECKEYKeyDBHandle *keydb; + + keydb = SECKEY_GetDefaultKeyDB(); + + if (keydb == NULL) + { /* open by file if this fails, if jartool is to call this */ ; } + + return keydb; + } + +int jar_close_key_database (SECKEYKeyDBHandle *keydb) + { + /* We never do close it */ + return 0; + } + + +/* + * j a r _ c r e a t e _ p k 7 + * + */ + +static void jar_pk7_out (void *arg, const char *buf, unsigned long len) + { + JAR_FWRITE ((JAR_FILE) arg, buf, len); + } + +int jar_create_pk7 + (CERTCertDBHandle *certdb, SECKEYKeyDBHandle *keydb, + CERTCertificate *cert, char *password, JAR_FILE infp, JAR_FILE outfp) + { + int nb; + unsigned char buffer [4096], digestdata[32]; + SECHashObject *hashObj; + void *hashcx; + unsigned int len; + + int status = 0; + char *errstring; + + SECItem digest; + SEC_PKCS7ContentInfo *cinfo; + SECStatus rv; + + void /*MWContext*/ *mw; + + if (outfp == NULL || infp == NULL || cert == NULL) + return JAR_ERR_GENERAL; + + /* we sign with SHA */ + hashObj = &SECHashObjects [HASH_AlgSHA1]; + + hashcx = (* hashObj->create)(); + if (hashcx == NULL) + return JAR_ERR_GENERAL; + + (* hashObj->begin)(hashcx); + + while (1) + { + /* nspr2.0 doesn't support feof + if (feof (infp)) break; */ + + nb = JAR_FREAD (infp, buffer, sizeof (buffer)); + if (nb == 0) + { +#if 0 + if (ferror(infp)) + { + /* PORT_SetError(SEC_ERROR_IO); */ /* FIX */ + (* hashObj->destroy) (hashcx, PR_TRUE); + return JAR_ERR_GENERAL; + } +#endif + /* eof */ + break; + } + (* hashObj->update) (hashcx, buffer, nb); + } + + (* hashObj->end) (hashcx, digestdata, &len, 32); + (* hashObj->destroy) (hashcx, PR_TRUE); + + digest.data = digestdata; + digest.len = len; + + /* signtool must use any old context it can find since it's + calling from inside javaland. */ + +#ifdef MOZILLA_CLIENT_OLD + mw = XP_FindSomeContext(); +#else + mw = NULL; +#endif + + PORT_SetError (0); + + cinfo = SEC_PKCS7CreateSignedData + (cert, certUsageObjectSigner, NULL, + SEC_OID_SHA1, &digest, NULL, (void *) mw); + + if (cinfo == NULL) + return JAR_ERR_PK7; + + rv = SEC_PKCS7IncludeCertChain (cinfo, NULL); + if (rv != SECSuccess) + { + status = PORT_GetError(); + SEC_PKCS7DestroyContentInfo (cinfo); + return status; + } + + /* Having this here forces signtool to always include + signing time. */ + + rv = SEC_PKCS7AddSigningTime (cinfo); + if (rv != SECSuccess) + { + /* don't check error */ + } + + PORT_SetError (0); + +#ifdef USE_MOZ_THREAD + /* if calling from mozilla */ + rv = jar_moz_encode + (cinfo, jar_pk7_out, outfp, + NULL, /* pwfn */ NULL, /* pwarg */ (void *) mw); +#else + /* if calling from mozilla thread*/ + rv = SEC_PKCS7Encode + (cinfo, jar_pk7_out, outfp, + NULL, /* pwfn */ NULL, /* pwarg */ (void *) mw): +#endif + + if (rv != SECSuccess) + status = PORT_GetError(); + + SEC_PKCS7DestroyContentInfo (cinfo); + + if (rv != SECSuccess) + { + errstring = JAR_get_error (status); + /*XP_TRACE (("Jar signing failed (reason %d = %s)", status, errstring));*/ + return status < 0 ? status : JAR_ERR_GENERAL; + } + + return 0; + } diff --git a/security/nss/lib/jar/jarver.c b/security/nss/lib/jar/jarver.c new file mode 100644 index 000000000..914818479 --- /dev/null +++ b/security/nss/lib/jar/jarver.c @@ -0,0 +1,2029 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +/* + * JARVER + * + * Jarnature Parsing & Verification + */ + +#define USE_MOZ_THREAD + +#include "jar.h" +#include "jarint.h" + +#ifdef USE_MOZ_THREAD +#include "jarevil.h" +#endif +#include "cdbhdl.h" + +/* to use huge pointers in win16 */ + +#if !defined(XP_WIN16) +#define xp_HUGE_MEMCPY PORT_Memcpy +#define xp_HUGE_STRCPY PORT_Strcpy +#define xp_HUGE_STRLEN PORT_Strlen +#define xp_HUGE_STRNCASECMP PORT_Strncasecmp +#else +#define xp_HUGE_MEMCPY hmemcpy +int xp_HUGE_STRNCASECMP (char ZHUGEP *buf, char *key, int len); +size_t xp_HUGE_STRLEN (char ZHUGEP *s); +char *xp_HUGE_STRCPY (char *to, char ZHUGEP *from); +#endif + +/* from certdb.h */ +#define CERTDB_USER (1<<6) + +#if 0 +/* from certdb.h */ +extern PRBool SEC_CertNicknameConflict + (char *nickname, CERTCertDBHandle *handle); +/* from certdb.h */ +extern SECStatus SEC_AddTempNickname + (CERTCertDBHandle *handle, char *nickname, SECItem *certKey); +/* from certdb.h */ +typedef SECStatus (* PermCertCallback)(CERTCertificate *cert, SECItem *k, void *pdata); +#endif + +/* from certdb.h */ +SECStatus SEC_TraversePermCerts + (CERTCertDBHandle *handle, PermCertCallback certfunc, void *udata); + + +#define SZ 512 + +static int jar_validate_pkcs7 + (JAR *jar, JAR_Signer *signer, char *data, long length); + +static int jar_decode (JAR *jar, char *data, long length); + +static void jar_catch_bytes + (void *arg, const char *buf, unsigned long len); + +static int jar_gather_signers + (JAR *jar, JAR_Signer *signer, SEC_PKCS7ContentInfo *cinfo); + +static char ZHUGEP *jar_eat_line + (int lines, int eating, char ZHUGEP *data, long *len); + +static JAR_Digest *jar_digest_section + (char ZHUGEP *manifest, long length); + +static JAR_Digest *jar_get_mf_digest (JAR *jar, char *path); + +static int jar_parse_digital_signature + (char *raw_manifest, JAR_Signer *signer, long length, JAR *jar); + +static int jar_add_cert + (JAR *jar, JAR_Signer *signer, int type, CERTCertificate *cert); + +static CERTCertificate *jar_get_certificate + (JAR *jar, long keylen, void *key, int *result); + +static char *jar_cert_element (char *name, char *tag, int occ); + +static char *jar_choose_nickname (CERTCertificate *cert); + +static char *jar_basename (const char *path); + +static int jar_signal + (int status, JAR *jar, const char *metafile, char *pathname); + +static int jar_insanity_check (char ZHUGEP *data, long length); + +int jar_parse_mf + (JAR *jar, char ZHUGEP *raw_manifest, + long length, const char *path, const char *url); + +int jar_parse_sf + (JAR *jar, char ZHUGEP *raw_manifest, + long length, const char *path, const char *url); + +int jar_parse_sig + (JAR *jar, const char *path, char ZHUGEP *raw_manifest, long length); + +int jar_parse_any + (JAR *jar, int type, JAR_Signer *signer, char ZHUGEP *raw_manifest, + long length, const char *path, const char *url); + +static int jar_internal_digest + (JAR *jar, const char *path, char *x_name, JAR_Digest *dig); + +/* + * J A R _ p a r s e _ m a n i f e s t + * + * Pass manifest files to this function. They are + * decoded and placed into internal representations. + * + * Accepts both signature and manifest files. Use + * the same "jar" for both. + * + */ + +int JAR_parse_manifest + (JAR *jar, char ZHUGEP *raw_manifest, + long length, const char *path, const char *url) + { + +#if defined(XP_WIN16) + PORT_Assert( !IsBadHugeReadPtr(raw_manifest, length) ); +#endif + + /* fill in the path, if supplied. This is a the location + of the jar file on disk, if known */ + + if (jar->filename == NULL && path) + { + jar->filename = PORT_Strdup (path); + if (jar->filename == NULL) + return JAR_ERR_MEMORY; + } + + /* fill in the URL, if supplied. This is the place + from which the jar file was retrieved. */ + + if (jar->url == NULL && url) + { + jar->url = PORT_Strdup (url); + if (jar->url == NULL) + return JAR_ERR_MEMORY; + } + + /* Determine what kind of file this is from the META-INF + directory. It could be MF, SF, or a binary RSA/DSA file */ + + if (!xp_HUGE_STRNCASECMP (raw_manifest, "Manifest-Version:", 17)) + { + return jar_parse_mf (jar, raw_manifest, length, path, url); + } + else if (!xp_HUGE_STRNCASECMP (raw_manifest, "Signature-Version:", 18)) + { + return jar_parse_sf (jar, raw_manifest, length, path, url); + } + else + { + /* This is probably a binary signature */ + return jar_parse_sig (jar, path, raw_manifest, length); + } + } + +/* + * j a r _ p a r s e _ s i g + * + * Pass some manner of RSA or DSA digital signature + * on, after checking to see if it comes at an appropriate state. + * + */ + +int jar_parse_sig + (JAR *jar, const char *path, char ZHUGEP *raw_manifest, long length) + { + JAR_Signer *signer; + int status = JAR_ERR_ORDER; + + if (length <= 128) + { + /* signature is way too small */ + return JAR_ERR_SIG; + } + + /* make sure that MF and SF have already been processed */ + + if (jar->globalmeta == NULL) + return JAR_ERR_ORDER; + +#if 0 + /* XXX Turn this on to disable multiple signers */ + if (jar->digest == NULL) + return JAR_ERR_ORDER; +#endif + + /* Determine whether or not this RSA file has + has an associated SF file */ + + if (path) + { + char *owner; + owner = jar_basename (path); + + if (owner == NULL) + return JAR_ERR_MEMORY; + + signer = jar_get_signer (jar, owner); + + PORT_Free (owner); + } + else + signer = jar_get_signer (jar, "*"); + + if (signer == NULL) + return JAR_ERR_ORDER; + + + /* Do not pass a huge pointer to this function, + since the underlying security code is unaware. We will + never pass >64k through here. */ + + if (length > 64000) + { + /* this digital signature is way too big */ + return JAR_ERR_SIG; + } + +#ifdef XP_WIN16 + /* + * For Win16, copy the portion of the raw_buffer containing the digital + * signature into another buffer... This insures that the data will + * NOT cross a segment boundary. Therefore, + * jar_parse_digital_signature(...) does NOT need to deal with HUGE + * pointers... + */ + + { + unsigned char *manifest_copy; + + manifest_copy = (unsigned char *) PORT_ZAlloc (length); + if (manifest_copy) + { + xp_HUGE_MEMCPY (manifest_copy, raw_manifest, length); + + status = jar_parse_digital_signature + (manifest_copy, signer, length, jar); + + PORT_Free (manifest_copy); + } + else + { + /* out of memory */ + return JAR_ERR_MEMORY; + } + } +#else + /* don't expense unneeded calloc overhead on non-win16 */ + status = jar_parse_digital_signature + (raw_manifest, signer, length, jar); +#endif + + return status; + } + +/* + * j a r _ p a r s e _ m f + * + * Parse the META-INF/manifest.mf file, whose + * information applies to all signers. + * + */ + +int jar_parse_mf + (JAR *jar, char ZHUGEP *raw_manifest, + long length, const char *path, const char *url) + { + if (jar->globalmeta) + { + /* refuse a second manifest file, if passed for some reason */ + return JAR_ERR_ORDER; + } + + + /* remember a digest for the global section */ + + jar->globalmeta = jar_digest_section (raw_manifest, length); + + if (jar->globalmeta == NULL) + return JAR_ERR_MEMORY; + + + return jar_parse_any + (jar, jarTypeMF, NULL, raw_manifest, length, path, url); + } + +/* + * j a r _ p a r s e _ s f + * + * Parse META-INF/xxx.sf, a digitally signed file + * pointing to a subset of MF sections. + * + */ + +int jar_parse_sf + (JAR *jar, char ZHUGEP *raw_manifest, + long length, const char *path, const char *url) + { + JAR_Signer *signer = NULL; + int status = JAR_ERR_MEMORY; + + if (jar->globalmeta == NULL) + { + /* It is a requirement that the MF file be passed before the SF file */ + return JAR_ERR_ORDER; + } + + signer = JAR_new_signer(); + + if (signer == NULL) + goto loser; + + if (path) + { + signer->owner = jar_basename (path); + if (signer->owner == NULL) + goto loser; + } + + + /* check for priors. When someone doctors a jar file + to contain identical path entries, prevent the second + one from affecting JAR functions */ + + if (jar_get_signer (jar, signer->owner)) + { + /* someone is trying to spoof us */ + status = JAR_ERR_ORDER; + goto loser; + } + + + /* remember its digest */ + + signer->digest = JAR_calculate_digest (raw_manifest, length); + + if (signer->digest == NULL) + goto loser; + + /* Add this signer to the jar */ + + ADDITEM (jar->signers, jarTypeOwner, + signer->owner, signer, sizeof (JAR_Signer)); + + + return jar_parse_any + (jar, jarTypeSF, signer, raw_manifest, length, path, url); + +loser: + + if (signer) + JAR_destroy_signer (signer); + + return status; + } + +/* + * j a r _ p a r s e _ a n y + * + * Parse a MF or SF manifest file. + * + */ + +int jar_parse_any + (JAR *jar, int type, JAR_Signer *signer, char ZHUGEP *raw_manifest, + long length, const char *path, const char *url) + { + int status; + + long raw_len; + + JAR_Digest *dig, *mfdig = NULL; + + char line [SZ]; + char x_name [SZ], x_md5 [SZ], x_sha [SZ]; + + char *x_info; + + char *sf_md5 = NULL, *sf_sha1 = NULL; + + *x_name = 0; + *x_md5 = 0; + *x_sha = 0; + + PORT_Assert( length > 0 ); + raw_len = length; + +#ifdef DEBUG + if ((status = jar_insanity_check (raw_manifest, raw_len)) < 0) + return status; +#endif + + + /* null terminate the first line */ + raw_manifest = jar_eat_line (0, PR_TRUE, raw_manifest, &raw_len); + + + /* skip over the preliminary section */ + /* This is one section at the top of the file with global metainfo */ + + while (raw_len) + { + JAR_Metainfo *met; + + raw_manifest = jar_eat_line (1, PR_TRUE, raw_manifest, &raw_len); + if (!*raw_manifest) break; + + met = (JAR_Metainfo*)PORT_ZAlloc (sizeof (JAR_Metainfo)); + if (met == NULL) + return JAR_ERR_MEMORY; + + /* Parse out the header & info */ + + if (xp_HUGE_STRLEN (raw_manifest) >= SZ) + { + /* almost certainly nonsense */ + continue; + } + + xp_HUGE_STRCPY (line, raw_manifest); + x_info = line; + + while (*x_info && *x_info != ' ' && *x_info != '\t' && *x_info != ':') + x_info++; + + if (*x_info) *x_info++ = 0; + + while (*x_info == ' ' || *x_info == '\t') + x_info++; + + /* metainfo (name, value) pair is now (line, x_info) */ + + met->header = PORT_Strdup (line); + met->info = PORT_Strdup (x_info); + + if (type == jarTypeMF) + { + ADDITEM (jar->metainfo, jarTypeMeta, + /* pathname */ NULL, met, sizeof (JAR_Metainfo)); + } + + /* For SF files, this metadata may be the digests + of the MF file, still in the "met" structure. */ + + if (type == jarTypeSF) + { + if (!PORT_Strcasecmp (line, "MD5-Digest")) + sf_md5 = (char *) met->info; + + if (!PORT_Strcasecmp (line, "SHA1-Digest") || !PORT_Strcasecmp (line, "SHA-Digest")) + sf_sha1 = (char *) met->info; + } + } + + if (type == jarTypeSF && jar->globalmeta) + { + /* this is a SF file which may contain a digest of the manifest.mf's + global metainfo. */ + + int match = 0; + JAR_Digest *glob = jar->globalmeta; + + if (sf_md5) + { + unsigned int md5_length; + unsigned char *md5_digest; + + md5_digest = ATOB_AsciiToData (sf_md5, &md5_length); + PORT_Assert( md5_length == MD5_LENGTH ); + + if (md5_length != MD5_LENGTH) + return JAR_ERR_CORRUPT; + + match = PORT_Memcmp (md5_digest, glob->md5, MD5_LENGTH); + } + + if (sf_sha1 && match == 0) + { + unsigned int sha1_length; + unsigned char *sha1_digest; + + sha1_digest = ATOB_AsciiToData (sf_sha1, &sha1_length); + PORT_Assert( sha1_length == SHA1_LENGTH ); + + if (sha1_length != SHA1_LENGTH) + return JAR_ERR_CORRUPT; + + match = PORT_Memcmp (sha1_digest, glob->sha1, SHA1_LENGTH); + } + + if (match != 0) + { + /* global digest doesn't match, SF file therefore invalid */ + jar->valid = JAR_ERR_METADATA; + return JAR_ERR_METADATA; + } + } + + /* done with top section of global data */ + + + while (raw_len) + { + *x_md5 = 0; + *x_sha = 0; + *x_name = 0; + + + /* If this is a manifest file, attempt to get a digest of the following section, + without damaging it. This digest will be saved later. */ + + if (type == jarTypeMF) + { + char ZHUGEP *sec; + long sec_len = raw_len; + + if (!*raw_manifest || *raw_manifest == '\n') + { + /* skip the blank line */ + sec = jar_eat_line (1, PR_FALSE, raw_manifest, &sec_len); + } + else + sec = raw_manifest; + + if (!xp_HUGE_STRNCASECMP (sec, "Name:", 5)) + { + if (type == jarTypeMF) + mfdig = jar_digest_section (sec, sec_len); + else + mfdig = NULL; + } + } + + + while (raw_len) + { + raw_manifest = jar_eat_line (1, PR_TRUE, raw_manifest, &raw_len); + if (!*raw_manifest) break; /* blank line, done with this entry */ + + if (xp_HUGE_STRLEN (raw_manifest) >= SZ) + { + /* almost certainly nonsense */ + continue; + } + + + /* Parse out the name/value pair */ + + xp_HUGE_STRCPY (line, raw_manifest); + x_info = line; + + while (*x_info && *x_info != ' ' && *x_info != '\t' && *x_info != ':') + x_info++; + + if (*x_info) *x_info++ = 0; + + while (*x_info == ' ' || *x_info == '\t') + x_info++; + + + if (!PORT_Strcasecmp (line, "Name")) + PORT_Strcpy (x_name, x_info); + + else if (!PORT_Strcasecmp (line, "MD5-Digest")) + PORT_Strcpy (x_md5, x_info); + + else if (!PORT_Strcasecmp (line, "SHA1-Digest") + || !PORT_Strcasecmp (line, "SHA-Digest")) + { + PORT_Strcpy (x_sha, x_info); + } + + /* Algorithm list is meta info we don't care about; keeping it out + of metadata saves significant space for large jar files */ + + else if (!PORT_Strcasecmp (line, "Digest-Algorithms") + || !PORT_Strcasecmp (line, "Hash-Algorithms")) + { + continue; + } + + /* Meta info is only collected for the manifest.mf file, + since the JAR_get_metainfo call does not support identity */ + + else if (type == jarTypeMF) + { + JAR_Metainfo *met; + + /* this is meta-data */ + + met = (JAR_Metainfo*)PORT_ZAlloc (sizeof (JAR_Metainfo)); + + if (met == NULL) + return JAR_ERR_MEMORY; + + /* metainfo (name, value) pair is now (line, x_info) */ + + if ((met->header = PORT_Strdup (line)) == NULL) + return JAR_ERR_MEMORY; + + if ((met->info = PORT_Strdup (x_info)) == NULL) + return JAR_ERR_MEMORY; + + ADDITEM (jar->metainfo, jarTypeMeta, + x_name, met, sizeof (JAR_Metainfo)); + } + } + + if(!x_name || !*x_name) { + /* Whatever that was, it wasn't an entry, because we didn't get a name. + * We don't really have anything, so don't record this. */ + continue; + } + + dig = (JAR_Digest*)PORT_ZAlloc (sizeof (JAR_Digest)); + if (dig == NULL) + return JAR_ERR_MEMORY; + + if (*x_md5 ) + { + unsigned int binary_length; + unsigned char *binary_digest; + + binary_digest = ATOB_AsciiToData (x_md5, &binary_length); + PORT_Assert( binary_length == MD5_LENGTH ); + + if (binary_length != MD5_LENGTH) + return JAR_ERR_CORRUPT; + + memcpy (dig->md5, binary_digest, MD5_LENGTH); + dig->md5_status = jarHashPresent; + } + + if (*x_sha ) + { + unsigned int binary_length; + unsigned char *binary_digest; + + binary_digest = ATOB_AsciiToData (x_sha, &binary_length); + PORT_Assert( binary_length == SHA1_LENGTH ); + + if (binary_length != SHA1_LENGTH) + return JAR_ERR_CORRUPT; + + memcpy (dig->sha1, binary_digest, SHA1_LENGTH); + dig->sha1_status = jarHashPresent; + } + + PORT_Assert( type == jarTypeMF || type == jarTypeSF ); + + + if (type == jarTypeMF) + { + ADDITEM (jar->hashes, jarTypeMF, x_name, dig, sizeof (JAR_Digest)); + } + else if (type == jarTypeSF) + { + ADDITEM (signer->sf, jarTypeSF, x_name, dig, sizeof (JAR_Digest)); + } + else + return JAR_ERR_ORDER; + + /* we're placing these calculated digests of manifest.mf + sections in a list where they can subsequently be forgotten */ + + if (type == jarTypeMF && mfdig) + { + ADDITEM (jar->manifest, jarTypeSect, + x_name, mfdig, sizeof (JAR_Digest)); + + mfdig = NULL; + } + + + /* Retrieve our saved SHA1 digest from saved copy and check digests. + This is just comparing the digest of the MF section as indicated in + the SF file with the one we remembered from parsing the MF file */ + + if (type == jarTypeSF) + { + if ((status = jar_internal_digest (jar, path, x_name, dig)) < 0) + return status; + } + } + + return 0; + } + +static int jar_internal_digest + (JAR *jar, const char *path, char *x_name, JAR_Digest *dig) + { + int cv; + int status; + + JAR_Digest *savdig; + + savdig = jar_get_mf_digest (jar, x_name); + + if (savdig == NULL) + { + /* no .mf digest for this pathname */ + status = jar_signal (JAR_ERR_ENTRY, jar, path, x_name); + if (status < 0) + return 0; /* was continue; */ + else + return status; + } + + /* check for md5 consistency */ + if (dig->md5_status) + { + cv = PORT_Memcmp (savdig->md5, dig->md5, MD5_LENGTH); + /* md5 hash of .mf file is not what expected */ + if (cv) + { + status = jar_signal (JAR_ERR_HASH, jar, path, x_name); + + /* bad hash, man */ + + dig->md5_status = jarHashBad; + savdig->md5_status = jarHashBad; + + if (status < 0) + return 0; /* was continue; */ + else + return status; + } + } + + /* check for sha1 consistency */ + if (dig->sha1_status) + { + cv = PORT_Memcmp (savdig->sha1, dig->sha1, SHA1_LENGTH); + /* sha1 hash of .mf file is not what expected */ + if (cv) + { + status = jar_signal (JAR_ERR_HASH, jar, path, x_name); + + /* bad hash, man */ + + dig->sha1_status = jarHashBad; + savdig->sha1_status = jarHashBad; + + if (status < 0) + return 0; /* was continue; */ + else + return status; + } + } + return 0; + } + +#ifdef DEBUG +/* + * j a r _ i n s a n i t y _ c h e c k + * + * Check for illegal characters (or possibly so) + * in the manifest files, to detect potential memory + * corruption by our neighbors. Debug only, since + * not I18N safe. + * + */ + +static int jar_insanity_check (char ZHUGEP *data, long length) + { + int c; + long off; + + for (off = 0; off < length; off++) + { + c = data [off]; + + if (c == '\n' || c == '\r' || (c >= ' ' && c <= 128)) + continue; + + return JAR_ERR_CORRUPT; + } + + return 0; + } +#endif + +/* + * j a r _ p a r s e _ d i g i t a l _ s i g n a t u r e + * + * Parse an RSA or DSA (or perhaps other) digital signature. + * Right now everything is PKCS7. + * + */ + +static int jar_parse_digital_signature + (char *raw_manifest, JAR_Signer *signer, long length, JAR *jar) + { +#if defined(XP_WIN16) + PORT_Assert( LOWORD(raw_manifest) + length < 0xFFFF ); +#endif + return jar_validate_pkcs7 (jar, signer, raw_manifest, length); + } + +/* + * j a r _ a d d _ c e r t + * + * Add information for the given certificate + * (or whatever) to the JAR linked list. A pointer + * is passed for some relevant reference, say + * for example the original certificate. + * + */ + +static int jar_add_cert + (JAR *jar, JAR_Signer *signer, int type, CERTCertificate *cert) + { + JAR_Cert *fing; + + if (cert == NULL) + return JAR_ERR_ORDER; + + fing = (JAR_Cert*)PORT_ZAlloc (sizeof (JAR_Cert)); + + if (fing == NULL) + goto loser; + +#ifdef USE_MOZ_THREAD + fing->cert = jar_moz_dup (cert); +#else + fing->cert = CERT_DupCertificate (cert); +#endif + + /* get the certkey */ + + fing->length = cert->certKey.len; + + fing->key = (char *) PORT_ZAlloc (fing->length); + + if (fing->key == NULL) + goto loser; + + PORT_Memcpy (fing->key, cert->certKey.data, fing->length); + + ADDITEM (signer->certs, type, + /* pathname */ NULL, fing, sizeof (JAR_Cert)); + + return 0; + +loser: + + if (fing) + { + if (fing->cert) + CERT_DestroyCertificate (fing->cert); + + PORT_Free (fing); + } + + return JAR_ERR_MEMORY; + } + +/* + * e a t _ l i n e + * + * Consume an ascii line from the top of a file kept + * in memory. This destroys the file in place. This function + * handles PC, Mac, and Unix style text files. + * + */ + +static char ZHUGEP *jar_eat_line + (int lines, int eating, char ZHUGEP *data, long *len) + { + char ZHUGEP *ret; + + ret = data; + if (!*len) return ret; + + /* Eat the requisite number of lines, if any; + prior to terminating the current line with a 0. */ + + for (/* yip */ ; lines; lines--) + { + while (*data && *data != '\n') + data++; + + /* After the CR, ok to eat one LF */ + + if (*data == '\n') + data++; + + /* If there are zeros, we put them there */ + + while (*data == 0 && data - ret < *len) + data++; + } + + *len -= data - ret; + ret = data; + + if (eating) + { + /* Terminate this line with a 0 */ + + while (*data && *data != '\n' && *data != '\r') + data++; + + /* In any case we are allowed to eat CR */ + + if (*data == '\r') + *data++ = 0; + + /* After the CR, ok to eat one LF */ + + if (*data == '\n') + *data++ = 0; + } + + return ret; + } + +/* + * j a r _ d i g e s t _ s e c t i o n + * + * Return the digests of the next section of the manifest file. + * Does not damage the manifest file, unlike parse_manifest. + * + */ + +static JAR_Digest *jar_digest_section + (char ZHUGEP *manifest, long length) + { + long global_len; + char ZHUGEP *global_end; + + global_end = manifest; + global_len = length; + + while (global_len) + { + global_end = jar_eat_line (1, PR_FALSE, global_end, &global_len); + if (*global_end == 0 || *global_end == '\n') + break; + } + + return JAR_calculate_digest (manifest, global_end - manifest); + } + +/* + * J A R _ v e r i f y _ d i g e s t + * + * Verifies that a precalculated digest matches the + * expected value in the manifest. + * + */ + +int PR_CALLBACK JAR_verify_digest + (JAR *jar, const char *name, JAR_Digest *dig) + { + JAR_Item *it; + + JAR_Digest *shindig; + + ZZLink *link; + ZZList *list; + + int result1, result2; + + list = jar->hashes; + + result1 = result2 = 0; + + if (jar->valid < 0) + { + /* signature not valid */ + return JAR_ERR_SIG; + } + + if (ZZ_ListEmpty (list)) + { + /* empty list */ + return JAR_ERR_PNF; + } + + for (link = ZZ_ListHead (list); + !ZZ_ListIterDone (list, link); + link = link->next) + { + it = link->thing; + if (it->type == jarTypeMF + && it->pathname && !PORT_Strcmp (it->pathname, name)) + { + shindig = (JAR_Digest *) it->data; + + if (shindig->md5_status) + { + if (shindig->md5_status == jarHashBad) + return JAR_ERR_HASH; + else + result1 = memcmp (dig->md5, shindig->md5, MD5_LENGTH); + } + + if (shindig->sha1_status) + { + if (shindig->sha1_status == jarHashBad) + return JAR_ERR_HASH; + else + result2 = memcmp (dig->sha1, shindig->sha1, SHA1_LENGTH); + } + + return (result1 == 0 && result2 == 0) ? 0 : JAR_ERR_HASH; + } + } + + return JAR_ERR_PNF; + } + +/* + * J A R _ c e r t _ a t t r i b u t e + * + * Return the named certificate attribute from the + * certificate specified by the given key. + * + */ + +int PR_CALLBACK JAR_cert_attribute + (JAR *jar, jarCert attrib, long keylen, void *key, + void **result, unsigned long *length) + { + int status = 0; + char *ret = NULL; + + CERTCertificate *cert; + + CERTCertDBHandle *certdb; + + JAR_Digest *dig; + SECItem hexme; + + *length = 0; + + if (attrib == 0 || key == 0) + return JAR_ERR_GENERAL; + + if (attrib == jarCertJavaHack) + { + cert = (CERTCertificate *) NULL; + certdb = JAR_open_database(); + + if (certdb) + { +#ifdef USE_MOZ_THREAD + cert = jar_moz_nickname (certdb, (char*)key); +#else + cert = CERT_FindCertByNickname (certdb, key); +#endif + + if (cert) + { + *length = cert->certKey.len; + + *result = (void *) PORT_ZAlloc (*length); + + if (*result) + PORT_Memcpy (*result, cert->certKey.data, *length); + else + return JAR_ERR_MEMORY; + } + JAR_close_database (certdb); + } + + return cert ? 0 : JAR_ERR_GENERAL; + } + + if (jar && jar->pkcs7 == 0) + return JAR_ERR_GENERAL; + + cert = jar_get_certificate (jar, keylen, key, &status); + + if (cert == NULL || status < 0) + return JAR_ERR_GENERAL; + +#define SEP " <br> " +#define SEPLEN (PORT_Strlen(SEP)) + + switch (attrib) + { + case jarCertCompany: + + ret = cert->subjectName; + + /* This is pretty ugly looking but only used + here for this one purpose. */ + + if (ret) + { + int retlen = 0; + + char *cer_ou1, *cer_ou2, *cer_ou3; + char *cer_cn, *cer_e, *cer_o, *cer_l; + + cer_cn = CERT_GetCommonName (&cert->subject); + cer_e = CERT_GetCertEmailAddress (&cert->subject); + cer_ou3 = jar_cert_element (ret, "OU=", 3); + cer_ou2 = jar_cert_element (ret, "OU=", 2); + cer_ou1 = jar_cert_element (ret, "OU=", 1); + cer_o = CERT_GetOrgName (&cert->subject); + cer_l = CERT_GetCountryName (&cert->subject); + + if (cer_cn) retlen += SEPLEN + PORT_Strlen (cer_cn); + if (cer_e) retlen += SEPLEN + PORT_Strlen (cer_e); + if (cer_ou1) retlen += SEPLEN + PORT_Strlen (cer_ou1); + if (cer_ou2) retlen += SEPLEN + PORT_Strlen (cer_ou2); + if (cer_ou3) retlen += SEPLEN + PORT_Strlen (cer_ou3); + if (cer_o) retlen += SEPLEN + PORT_Strlen (cer_o); + if (cer_l) retlen += SEPLEN + PORT_Strlen (cer_l); + + ret = (char *) PORT_ZAlloc (1 + retlen); + + if (cer_cn) { PORT_Strcpy (ret, cer_cn); PORT_Strcat (ret, SEP); } + if (cer_e) { PORT_Strcat (ret, cer_e); PORT_Strcat (ret, SEP); } + if (cer_ou1) { PORT_Strcat (ret, cer_ou1); PORT_Strcat (ret, SEP); } + if (cer_ou2) { PORT_Strcat (ret, cer_ou2); PORT_Strcat (ret, SEP); } + if (cer_ou3) { PORT_Strcat (ret, cer_ou3); PORT_Strcat (ret, SEP); } + if (cer_o) { PORT_Strcat (ret, cer_o); PORT_Strcat (ret, SEP); } + if (cer_l) PORT_Strcat (ret, cer_l); + + /* return here to avoid unsightly memory leak */ + + *result = ret; + *length = PORT_Strlen (ret); + + return 0; + } + break; + + case jarCertCA: + + ret = cert->issuerName; + + if (ret) + { + int retlen = 0; + + char *cer_ou1, *cer_ou2, *cer_ou3; + char *cer_cn, *cer_e, *cer_o, *cer_l; + + /* This is pretty ugly looking but only used + here for this one purpose. */ + + cer_cn = CERT_GetCommonName (&cert->issuer); + cer_e = CERT_GetCertEmailAddress (&cert->issuer); + cer_ou3 = jar_cert_element (ret, "OU=", 3); + cer_ou2 = jar_cert_element (ret, "OU=", 2); + cer_ou1 = jar_cert_element (ret, "OU=", 1); + cer_o = CERT_GetOrgName (&cert->issuer); + cer_l = CERT_GetCountryName (&cert->issuer); + + if (cer_cn) retlen += SEPLEN + PORT_Strlen (cer_cn); + if (cer_e) retlen += SEPLEN + PORT_Strlen (cer_e); + if (cer_ou1) retlen += SEPLEN + PORT_Strlen (cer_ou1); + if (cer_ou2) retlen += SEPLEN + PORT_Strlen (cer_ou2); + if (cer_ou3) retlen += SEPLEN + PORT_Strlen (cer_ou3); + if (cer_o) retlen += SEPLEN + PORT_Strlen (cer_o); + if (cer_l) retlen += SEPLEN + PORT_Strlen (cer_l); + + ret = (char *) PORT_ZAlloc (1 + retlen); + + if (cer_cn) { PORT_Strcpy (ret, cer_cn); PORT_Strcat (ret, SEP); } + if (cer_e) { PORT_Strcat (ret, cer_e); PORT_Strcat (ret, SEP); } + if (cer_ou1) { PORT_Strcat (ret, cer_ou1); PORT_Strcat (ret, SEP); } + if (cer_ou2) { PORT_Strcat (ret, cer_ou2); PORT_Strcat (ret, SEP); } + if (cer_ou3) { PORT_Strcat (ret, cer_ou3); PORT_Strcat (ret, SEP); } + if (cer_o) { PORT_Strcat (ret, cer_o); PORT_Strcat (ret, SEP); } + if (cer_l) PORT_Strcat (ret, cer_l); + + /* return here to avoid unsightly memory leak */ + + *result = ret; + *length = PORT_Strlen (ret); + + return 0; + } + + break; + + case jarCertSerial: + + ret = CERT_Hexify (&cert->serialNumber, 1); + break; + + case jarCertExpires: + + ret = DER_UTCDayToAscii (&cert->validity.notAfter); + break; + + case jarCertNickname: + + ret = jar_choose_nickname (cert); + break; + + case jarCertFinger: + + dig = JAR_calculate_digest + ((char *) cert->derCert.data, cert->derCert.len); + + if (dig) + { + hexme.len = sizeof (dig->md5); + hexme.data = dig->md5; + ret = CERT_Hexify (&hexme, 1); + } + break; + + default: + + return JAR_ERR_GENERAL; + } + + *result = ret ? PORT_Strdup (ret) : NULL; + *length = ret ? PORT_Strlen (ret) : 0; + + return 0; + } + +/* + * j a r _ c e r t _ e l e m e n t + * + * Retrieve an element from an x400ish ascii + * designator, in a hackish sort of way. The right + * thing would probably be to sort AVATags. + * + */ + +static char *jar_cert_element (char *name, char *tag, int occ) + { + if (name && tag) + { + char *s; + int found = 0; + + while (occ--) + { + if (PORT_Strstr (name, tag)) + { + name = PORT_Strstr (name, tag) + PORT_Strlen (tag); + found = 1; + } + else + { + name = PORT_Strstr (name, "="); + if (name == NULL) return NULL; + found = 0; + } + } + + if (!found) return NULL; + + /* must mangle only the copy */ + name = PORT_Strdup (name); + + /* advance to next equal */ + for (s = name; *s && *s != '='; s++) + /* yip */ ; + + /* back up to previous comma */ + while (s > name && *s != ',') s--; + + /* zap the whitespace and return */ + *s = 0; + } + + return name; + } + +/* + * j a r _ c h o o s e _ n i c k n a m e + * + * Attempt to determine a suitable nickname for + * a certificate with a computer-generated "tmpcertxxx" + * nickname. It needs to be something a user can + * understand, so try a few things. + * + */ + +static char *jar_choose_nickname (CERTCertificate *cert) + { + char *cert_cn; + char *cert_o; + char *cert_cn_o; + + int cn_o_length; + + /* is the existing name ok */ + + if (cert->nickname && PORT_Strncmp (cert->nickname, "tmpcert", 7)) + return PORT_Strdup (cert->nickname); + + /* we have an ugly name here people */ + + /* Try the CN */ + cert_cn = CERT_GetCommonName (&cert->subject); + + if (cert_cn) + { + /* check for duplicate nickname */ + +#ifdef USE_MOZ_THREAD + if (jar_moz_nickname (CERT_GetDefaultCertDB(), cert_cn) == NULL) +#else + if (CERT_FindCertByNickname (CERT_GetDefaultCertDB(), cert_cn) == NULL) +#endif + return cert_cn; + + /* Try the CN plus O */ + cert_o = CERT_GetOrgName (&cert->subject); + + cn_o_length = PORT_Strlen (cert_cn) + 3 + PORT_Strlen (cert_o) + 20; + cert_cn_o = (char*)PORT_ZAlloc (cn_o_length); + + PR_snprintf (cert_cn_o, cn_o_length, + "%s's %s Certificate", cert_cn, cert_o); + +#ifdef USE_MOZ_THREAD + if (jar_moz_nickname (CERT_GetDefaultCertDB(), cert_cn_o) == NULL) +#else + if (CERT_FindCertByNickname (CERT_GetDefaultCertDB(), cert_cn_o) == NULL) +#endif + return cert_cn; + } + + /* If all that failed, use the ugly nickname */ + return cert->nickname ? PORT_Strdup (cert->nickname) : NULL; + } + +/* + * J A R _ c e r t _ h t m l + * + * Return an HTML representation of the certificate + * designated by the given fingerprint, in specified style. + * + * JAR is optional, but supply it if you can in order + * to optimize. + * + */ + +char *JAR_cert_html + (JAR *jar, int style, long keylen, void *key, int *result) + { + char *html; + CERTCertificate *cert; + + *result = -1; + + if (style != 0) + return NULL; + + cert = jar_get_certificate (jar, keylen, key, result); + + if (cert == NULL || *result < 0) + return NULL; + + *result = 0; + + html = CERT_HTMLCertInfo (cert, /* show images */ PR_TRUE, + /*show issuer*/PR_TRUE); + + if (html == NULL) + *result = -1; + + return html; + } + +/* + * J A R _ s t a s h _ c e r t + * + * Stash the certificate pointed to by this + * fingerprint, in persistent storage somewhere. + * + */ + +extern int PR_CALLBACK JAR_stash_cert + (JAR *jar, long keylen, void *key) + { + int result = 0; + + char *nickname; + CERTCertTrust trust; + + CERTCertDBHandle *certdb; + CERTCertificate *cert, *newcert; + + cert = jar_get_certificate (jar, keylen, key, &result); + + if (result < 0) + return result; + + if (cert == NULL) + return JAR_ERR_GENERAL; + + if ((certdb = JAR_open_database()) == NULL) + return JAR_ERR_GENERAL; + + /* Attempt to give a name to the newish certificate */ + nickname = jar_choose_nickname (cert); + +#ifdef USE_MOZ_THREAD + newcert = jar_moz_nickname (certdb, nickname); +#else + newcert = CERT_FindCertByNickname (certdb, nickname); +#endif + + if (newcert && newcert->isperm) + { + /* already in permanant database */ + return 0; + } + + if (newcert) cert = newcert; + + /* FIX, since FindCert returns a bogus dbhandle + set it ourselves */ + + cert->dbhandle = certdb; + +#if 0 + nickname = cert->subjectName; + if (nickname) + { + /* Not checking for a conflict here. But this should + be a new cert or it would have been found earlier. */ + + nickname = jar_cert_element (nickname, "CN=", 1); + + if (SEC_CertNicknameConflict (nickname, cert->dbhandle)) + { + /* conflict */ + nickname = PORT_Realloc (&nickname, PORT_Strlen (nickname) + 3); + + /* Beyond one copy, there are probably serious problems + so we will stop at two rather than counting.. */ + + PORT_Strcat (nickname, " #2"); + } + } +#endif + + if (nickname != NULL) + { + PORT_Memset ((void *) &trust, 0, sizeof(trust)); + +#ifdef USE_MOZ_THREAD + if (jar_moz_perm (cert, nickname, &trust) != SECSuccess) +#else + if (CERT_AddTempCertToPerm (cert, nickname, &trust) != SECSuccess) +#endif + { + /* XXX might want to call PORT_GetError here */ + result = JAR_ERR_GENERAL; + } + } + + JAR_close_database (certdb); + + return result; + } + +/* + * J A R _ f e t c h _ c e r t + * + * Given an opaque identifier of a certificate, + * return the full certificate. + * + * The new function, which retrieves by key. + * + */ + +void *JAR_fetch_cert (long length, void *key) + { + SECItem seckey; + CERTCertificate *cert = NULL; + + CERTCertDBHandle *certdb; + + certdb = JAR_open_database(); + + if (certdb) + { + seckey.len = length; + seckey.data = (unsigned char*)key; + +#ifdef USE_MOZ_THREAD + cert = jar_moz_certkey (certdb, &seckey); +#else + cert = CERT_FindCertByKey (certdb, &seckey); +#endif + + JAR_close_database (certdb); + } + + return (void *) cert; + } + +/* + * j a r _ g e t _ m f _ d i g e s t + * + * Retrieve a corresponding saved digest over a section + * of the main manifest file. + * + */ + +static JAR_Digest *jar_get_mf_digest (JAR *jar, char *pathname) + { + JAR_Item *it; + + JAR_Digest *dig; + + ZZLink *link; + ZZList *list; + + list = jar->manifest; + + if (ZZ_ListEmpty (list)) + return NULL; + + for (link = ZZ_ListHead (list); + !ZZ_ListIterDone (list, link); + link = link->next) + { + it = link->thing; + if (it->type == jarTypeSect + && it->pathname && !PORT_Strcmp (it->pathname, pathname)) + { + dig = (JAR_Digest *) it->data; + return dig; + } + } + + return NULL; + } + +/* + * j a r _ b a s e n a m e + * + * Return the basename -- leading components of path stripped off, + * extension ripped off -- of a path. + * + */ + +static char *jar_basename (const char *path) + { + char *pith, *e, *basename, *ext; + + if (path == NULL) + return PORT_Strdup (""); + + pith = PORT_Strdup (path); + + basename = pith; + + while (1) + { + for (e = basename; *e && *e != '/' && *e != '\\'; e++) + /* yip */ ; + if (*e) + basename = ++e; + else + break; + } + + if ((ext = PORT_Strrchr (basename, '.')) != NULL) + *ext = 0; + + /* We already have the space allocated */ + PORT_Strcpy (pith, basename); + + return pith; + } + +/* + * + + + + + + + + + + + + + + + + * + * CRYPTO ROUTINES FOR JAR + * + * The following functions are the cryptographic + * interface to PKCS7 for Jarnatures. + * + * + + + + + + + + + + + + + + + + * + */ + +/* + * j a r _ c a t c h _ b y t e s + * + * In the event signatures contain enveloped data, it will show up here. + * But note that the lib/pkcs7 routines aren't ready for it. + * + */ + +static void jar_catch_bytes + (void *arg, const char *buf, unsigned long len) + { + /* Actually this should never be called, since there is + presumably no data in the signature itself. */ + } + +/* + * j a r _ v a l i d a t e _ p k c s 7 + * + * Validate (and decode, if necessary) a binary pkcs7 + * signature in DER format. + * + */ + +static int jar_validate_pkcs7 + (JAR *jar, JAR_Signer *signer, char *data, long length) + { + SECItem detdig; + + SEC_PKCS7ContentInfo *cinfo; + SEC_PKCS7DecoderContext *dcx; + + int status = 0; + char *errstring = NULL; + + PORT_Assert( jar != NULL && signer != NULL ); + + if (jar == NULL || signer == NULL) + return JAR_ERR_ORDER; + + signer->valid = JAR_ERR_SIG; + + /* We need a context if we can get one */ + +#ifdef MOZILLA_CLIENT_OLD + if (jar->mw == NULL) { + JAR_set_context (jar, NULL); + } +#endif + + + dcx = SEC_PKCS7DecoderStart + (jar_catch_bytes, NULL /*cb_arg*/, NULL /*getpassword*/, jar->mw, + NULL, NULL, NULL); + + if (dcx != NULL) + { + SEC_PKCS7DecoderUpdate (dcx, data, length); + cinfo = SEC_PKCS7DecoderFinish (dcx); + } + + if (cinfo == NULL) + { + /* strange pkcs7 failure */ + return JAR_ERR_PK7; + } + + if (SEC_PKCS7ContentIsEncrypted (cinfo)) + { + /* content was encrypted, fail */ + return JAR_ERR_PK7; + } + + if (SEC_PKCS7ContentIsSigned (cinfo) == PR_FALSE) + { + /* content was not signed, fail */ + return JAR_ERR_PK7; + } + + PORT_SetError (0); + + /* use SHA1 only */ + + detdig.len = SHA1_LENGTH; + detdig.data = signer->digest->sha1; + +#ifdef USE_MOZ_THREAD + if (jar_moz_verify + (cinfo, certUsageObjectSigner, &detdig, HASH_AlgSHA1, PR_FALSE)== + SECSuccess) +#else + if (SEC_PKCS7VerifyDetachedSignature + (cinfo, certUsageObjectSigner, &detdig, HASH_AlgSHA1, PR_FALSE)== + PR_TRUE) +#endif + { + /* signature is valid */ + signer->valid = 0; + jar_gather_signers (jar, signer, cinfo); + } + else + { + status = PORT_GetError(); + + PORT_Assert( status < 0 ); + if (status >= 0) status = JAR_ERR_SIG; + + jar->valid = status; + signer->valid = status; + + errstring = JAR_get_error (status); + /*XP_TRACE(("JAR signature invalid (reason %d = %s)", status, errstring));*/ + } + + jar->pkcs7 = PR_TRUE; + signer->pkcs7 = PR_TRUE; + + SEC_PKCS7DestroyContentInfo (cinfo); + + return status; + } + +/* + * j a r _ g a t h e r _ s i g n e r s + * + * Add the single signer of this signature to the + * certificate linked list. + * + */ + +static int jar_gather_signers + (JAR *jar, JAR_Signer *signer, SEC_PKCS7ContentInfo *cinfo) + { + int result; + + CERTCertificate *cert; + CERTCertDBHandle *certdb; + + SEC_PKCS7SignedData *sdp; + SEC_PKCS7SignerInfo **pksigners, *pksigner; + + sdp = cinfo->content.signedData; + + if (sdp == NULL) + return JAR_ERR_PK7; + + pksigners = sdp->signerInfos; + + /* permit exactly one signer */ + + if (pksigners == NULL || pksigners [0] == NULL || pksigners [1] != NULL) + return JAR_ERR_PK7; + + pksigner = *pksigners; + cert = pksigner->cert; + + if (cert == NULL) + return JAR_ERR_PK7; + + certdb = JAR_open_database(); + + if (certdb == NULL) + return JAR_ERR_GENERAL; + + result = jar_add_cert (jar, signer, jarTypeSign, cert); + + JAR_close_database (certdb); + + return result; + } + +/* + * j a r _ o p e n _ d a t a b a s e + * + * Open the certificate database, + * for use by JAR functions. + * + */ + +CERTCertDBHandle *JAR_open_database (void) + { + int keepcerts = 0; + CERTCertDBHandle *certdb; + + /* local_certdb will only be used if calling from a command line tool */ + static CERTCertDBHandle local_certdb; + + certdb = CERT_GetDefaultCertDB(); + + if (certdb == NULL) + { + if (CERT_OpenCertDBFilename (&local_certdb, NULL, (PRBool)!keepcerts) != + SECSuccess) + { + return NULL; + } + certdb = &local_certdb; + } + + return certdb; + } + +/* + * j a r _ c l o s e _ d a t a b a s e + * + * Close the certificate database. + * For use by JAR functions. + * + */ + +int JAR_close_database (CERTCertDBHandle *certdb) + { + CERTCertDBHandle *defaultdb; + + /* This really just retrieves the handle, nothing more */ + defaultdb = CERT_GetDefaultCertDB(); + + /* If there is no default db, it means we opened + the permanent database for some reason */ + + if (defaultdb == NULL && certdb != NULL) + CERT_ClosePermCertDB (certdb); + + return 0; + } + +/* + * j a r _ g e t _ c e r t i f i c a t e + * + * Return the certificate referenced + * by a given fingerprint, or NULL if not found. + * Error code is returned in result. + * + */ + +static CERTCertificate *jar_get_certificate + (JAR *jar, long keylen, void *key, int *result) + { + int found = 0; + + JAR_Item *it; + JAR_Cert *fing; + + JAR_Context *ctx; + + if (jar == NULL) + { + void *cert; + cert = JAR_fetch_cert (keylen, key); + *result = (cert == NULL) ? JAR_ERR_GENERAL : 0; + return (CERTCertificate *) cert; + } + + ctx = JAR_find (jar, NULL, jarTypeSign); + + while (JAR_find_next (ctx, &it) >= 0) + { + fing = (JAR_Cert *) it->data; + + if (keylen != fing->length) + continue; + + PORT_Assert( keylen < 0xFFFF ); + if (!PORT_Memcmp (fing->key, key, keylen)) + { + found = 1; + break; + } + } + + JAR_find_end (ctx); + + if (found == 0) + { + *result = JAR_ERR_GENERAL; + return NULL; + } + + *result = 0; + return fing->cert; + } + +/* + * j a r _ s i g n a l + * + * Nonfatal errors come here to callback Java. + * + */ + +static int jar_signal + (int status, JAR *jar, const char *metafile, char *pathname) + { + char *errstring; + + errstring = JAR_get_error (status); + + if (jar->signal) + { + (*jar->signal) (status, jar, metafile, pathname, errstring); + return 0; + } + + return status; + } + +/* + * j a r _ a p p e n d + * + * Tack on an element to one of a JAR's linked + * lists, with rudimentary error handling. + * + */ + +int jar_append (ZZList *list, int type, + char *pathname, void *data, size_t size) + { + JAR_Item *it; + ZZLink *entity; + + it = (JAR_Item*)PORT_ZAlloc (sizeof (JAR_Item)); + + if (it == NULL) + goto loser; + + if (pathname) + { + it->pathname = PORT_Strdup (pathname); + if (it->pathname == NULL) + goto loser; + } + + it->type = (jarType)type; + it->data = (unsigned char *) data; + it->size = size; + + entity = ZZ_NewLink (it); + + if (entity) + { + ZZ_AppendLink (list, entity); + return 0; + } + +loser: + + if (it) + { + if (it->pathname) PORT_Free (it->pathname); + PORT_Free (it); + } + + return JAR_ERR_MEMORY; + } + +/* + * W I N 1 6 s t u f f + * + * These functions possibly belong in xp_mem.c, they operate + * on huge string pointers for win16. + * + */ + +#if defined(XP_WIN16) +int xp_HUGE_STRNCASECMP (char ZHUGEP *buf, char *key, int len) + { + while (len--) + { + char c1, c2; + + c1 = *buf++; + c2 = *key++; + + if (c1 >= 'a' && c1 <= 'z') c1 -= ('a' - 'A'); + if (c2 >= 'a' && c2 <= 'z') c2 -= ('a' - 'A'); + + if (c1 != c2) + return (c1 < c2) ? -1 : 1; + } + return 0; + } + +size_t xp_HUGE_STRLEN (char ZHUGEP *s) + { + size_t len = 0L; + while (*s++) len++; + return len; + } + +char *xp_HUGE_STRCPY (char *to, char ZHUGEP *from) + { + char *ret = to; + + while (*from) + *to++ = *from++; + *to = 0; + + return ret; + } +#endif diff --git a/security/nss/lib/pk11wrap/pk11skey.c b/security/nss/lib/pk11wrap/pk11skey.c new file mode 100644 index 000000000..1ad09ad48 --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11skey.c @@ -0,0 +1,4878 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ +/* + * This file implements the Symkey wrapper and the PKCS context + * Interfaces. + */ + +#include "seccomon.h" +#include "secmod.h" +#include "prlock.h" +#include "secmodi.h" +#include "pkcs11.h" +#include "pk11func.h" +#include "secitem.h" +#include "key.h" +#include "secoid.h" +#include "secasn1.h" +#include "sechash.h" +#include "cert.h" +#include "secerr.h" + +#define PAIRWISE_SECITEM_TYPE siBuffer +#define PAIRWISE_DIGEST_LENGTH SHA1_LENGTH /* 160-bits */ +#define PAIRWISE_MESSAGE_LENGTH 20 /* 160-bits */ + +/* forward static declarations. */ +static PK11SymKey *pk11_DeriveWithTemplate(PK11SymKey *baseKey, + CK_MECHANISM_TYPE derive, SECItem *param, CK_MECHANISM_TYPE target, + CK_ATTRIBUTE_TYPE operation, int keySize, CK_ATTRIBUTE *userAttr, + unsigned int numAttrs); + + +/* + * strip leading zero's from key material + */ +void +pk11_SignedToUnsigned(CK_ATTRIBUTE *attrib) { + char *ptr = (char *)attrib->pValue; + unsigned long len = attrib->ulValueLen; + + while (len && (*ptr == 0)) { + len--; + ptr++; + } + attrib->pValue = ptr; + attrib->ulValueLen = len; +} + +/* + * get a new session on a slot. If we run out of session, use the slot's + * 'exclusive' session. In this case owner becomes false. + */ +static CK_SESSION_HANDLE +pk11_GetNewSession(PK11SlotInfo *slot,PRBool *owner) +{ + CK_SESSION_HANDLE session; + *owner = PR_TRUE; + if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot); + if ( PK11_GETTAB(slot)->C_OpenSession(slot->slotID,CKF_SERIAL_SESSION, + slot,pk11_notify,&session) != CKR_OK) { + *owner = PR_FALSE; + session = slot->session; + } + if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot); + + return session; +} + +static void +pk11_CloseSession(PK11SlotInfo *slot,CK_SESSION_HANDLE session,PRBool owner) +{ + if (!owner) return; + if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot); + (void) PK11_GETTAB(slot)->C_CloseSession(session); + if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot); +} + + +SECStatus +PK11_CreateNewObject(PK11SlotInfo *slot, CK_SESSION_HANDLE session, + CK_ATTRIBUTE *theTemplate, int count, + PRBool token, CK_OBJECT_HANDLE *objectID) +{ + CK_SESSION_HANDLE rwsession; + CK_RV crv; + SECStatus rv = SECSuccess; + + rwsession = session; + if (rwsession == CK_INVALID_SESSION) { + if (token) { + rwsession = PK11_GetRWSession(slot); + } else { + rwsession = slot->session; + PK11_EnterSlotMonitor(slot); + } + } + crv = PK11_GETTAB(slot)->C_CreateObject(rwsession, theTemplate, + count,objectID); + if(crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + rv = SECFailure; + } + + if (session == CK_INVALID_SESSION) { + if (token) { + PK11_RestoreROSession(slot, rwsession); + } else { + PK11_ExitSlotMonitor(slot); + } + } + + return rv; +} + +static void +pk11_EnterKeyMonitor(PK11SymKey *symKey) { + if (!symKey->sessionOwner || !(symKey->slot->isThreadSafe)) + PK11_EnterSlotMonitor(symKey->slot); +} + +static void +pk11_ExitKeyMonitor(PK11SymKey *symKey) { + if (!symKey->sessionOwner || !(symKey->slot->isThreadSafe)) + PK11_ExitSlotMonitor(symKey->slot); +} + + +static PK11SymKey *pk11SymKeyHead = NULL; +static PK11SymKey * +pk11_getKeyFromList(PK11SlotInfo *slot) { + PK11SymKey *symKey = NULL; + + + PK11_USE_THREADS(PR_Lock(slot->freeListLock);) + if (slot->freeSymKeysHead) { + symKey = slot->freeSymKeysHead; + slot->freeSymKeysHead = symKey->next; + slot->keyCount--; + } + PK11_USE_THREADS(PR_Unlock(slot->freeListLock);) + if (symKey) { + symKey->next = NULL; + if (!symKey->sessionOwner) + symKey->session = pk11_GetNewSession(slot,&symKey->sessionOwner); + return symKey; + } + + symKey = (PK11SymKey *)PORT_ZAlloc(sizeof(PK11SymKey)); + if (symKey == NULL) { + return NULL; + } + symKey->refLock = PR_NewLock(); + if (symKey->refLock == NULL) { + PORT_Free(symKey); + return NULL; + } + symKey->session = pk11_GetNewSession(slot,&symKey->sessionOwner); + symKey->next = NULL; + return symKey; +} + +void +PK11_CleanKeyList(PK11SlotInfo *slot) +{ + PK11SymKey *symKey = NULL; + + while (slot->freeSymKeysHead) { + symKey = slot->freeSymKeysHead; + slot->freeSymKeysHead = symKey->next; + pk11_CloseSession(symKey->slot, symKey->session,symKey->sessionOwner); + PK11_USE_THREADS(PR_DestroyLock(symKey->refLock);) + PORT_Free(symKey); + }; + return; +} + +/* + * create a symetric key: + * Slot is the slot to create the key in. + * type is the mechainism type + */ +PK11SymKey * +PK11_CreateSymKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, void *wincx) +{ + + PK11SymKey *symKey = pk11_getKeyFromList(slot); + + + if (symKey == NULL) { + return NULL; + } + + symKey->type = type; + symKey->data.data = NULL; + symKey->data.len = 0; + symKey->owner = PR_TRUE; + symKey->objectID = CK_INVALID_KEY; + symKey->slot = slot; + symKey->series = slot->series; + symKey->cx = wincx; + symKey->size = 0; + symKey->refCount = 1; + symKey->origin = PK11_OriginNULL; + symKey->origin = PK11_OriginNULL; + PK11_ReferenceSlot(slot); + return symKey; +} + +/* + * destroy a symetric key + */ +void +PK11_FreeSymKey(PK11SymKey *symKey) +{ + PRBool destroy = PR_FALSE; + PK11SlotInfo *slot; + PRBool freeit = PR_TRUE; + + PK11_USE_THREADS(PR_Lock(symKey->refLock);) + if (symKey->refCount-- == 1) { + destroy= PR_TRUE; + } + PK11_USE_THREADS(PR_Unlock(symKey->refLock);) + if (destroy) { + if ((symKey->owner) && symKey->objectID != CK_INVALID_KEY) { + pk11_EnterKeyMonitor(symKey); + (void) PK11_GETTAB(symKey->slot)-> + C_DestroyObject(symKey->session, symKey->objectID); + pk11_ExitKeyMonitor(symKey); + } + if (symKey->data.data) { + PORT_Memset(symKey->data.data, 0, symKey->data.len); + PORT_Free(symKey->data.data); + } + slot = symKey->slot; + PK11_USE_THREADS(PR_Lock(slot->freeListLock);) + if (slot->keyCount < slot->maxKeyCount) { + symKey->next = slot->freeSymKeysHead; + slot->freeSymKeysHead = symKey; + slot->keyCount++; + symKey->slot = NULL; + freeit = PR_FALSE; + } + PK11_USE_THREADS(PR_Unlock(slot->freeListLock);) + if (freeit) { + pk11_CloseSession(symKey->slot, symKey->session, + symKey->sessionOwner); + PK11_USE_THREADS(PR_DestroyLock(symKey->refLock);) + PORT_Free(symKey); + } + PK11_FreeSlot(slot); + } +} + +PK11SymKey * +PK11_ReferenceSymKey(PK11SymKey *symKey) +{ + PK11_USE_THREADS(PR_Lock(symKey->refLock);) + symKey->refCount++; + PK11_USE_THREADS(PR_Unlock(symKey->refLock);) + return symKey; +} + +/* + * turn key handle into an appropriate key object + */ +PK11SymKey * +PK11_SymKeyFromHandle(PK11SlotInfo *slot, PK11SymKey *parent, PK11Origin origin, + CK_MECHANISM_TYPE type, CK_OBJECT_HANDLE keyID, PRBool owner, void *wincx) +{ + PK11SymKey *symKey; + + if (keyID == CK_INVALID_KEY) { + return NULL; + } + + symKey = PK11_CreateSymKey(slot,type,wincx); + if (symKey == NULL) { + return NULL; + } + + symKey->objectID = keyID; + symKey->origin = origin; + symKey->owner = owner; + + /* adopt the parent's session */ + /* This is only used by SSL. What we really want here is a session + * structure with a ref count so the session goes away only after all the + * keys do. */ + if (owner && parent) { + pk11_CloseSession(symKey->slot, symKey->session,symKey->sessionOwner); + symKey->sessionOwner = parent->sessionOwner; + symKey->session = parent->session; + parent->sessionOwner = PR_FALSE; + } + + return symKey; +} + +/* + * turn key handle into an appropriate key object + */ +PK11SymKey * +PK11_GetWrapKey(PK11SlotInfo *slot, int wrap, CK_MECHANISM_TYPE type, + int series, void *wincx) +{ + PK11SymKey *symKey = NULL; + + if (slot->series != series) return NULL; + if (slot->refKeys[wrap] == CK_INVALID_KEY) return NULL; + if (type == CKM_INVALID_MECHANISM) type = slot->wrapMechanism; + + symKey = PK11_SymKeyFromHandle(slot, NULL, PK11_OriginDerive, + slot->wrapMechanism, slot->refKeys[wrap], PR_FALSE, wincx); + return symKey; +} + +void +PK11_SetWrapKey(PK11SlotInfo *slot, int wrap, PK11SymKey *wrapKey) +{ + /* save the handle and mechanism for the wrapping key */ + /* mark the key and session as not owned by us to they don't get freed + * when the key goes way... that lets us reuse the key later */ + slot->refKeys[wrap] = wrapKey->objectID; + wrapKey->owner = PR_FALSE; + wrapKey->sessionOwner = PR_FALSE; + slot->wrapMechanism = wrapKey->type; +} + +CK_MECHANISM_TYPE +PK11_GetMechanism(PK11SymKey *symKey) +{ + return symKey->type; +} + +/* + * figure out if a key is still valid or if it is stale. + */ +PRBool +PK11_VerifyKeyOK(PK11SymKey *key) { + if (!PK11_IsPresent(key->slot)) { + return PR_FALSE; + } + return (PRBool)(key->series == key->slot->series); +} + +static PK11SymKey * +pk11_ImportSymKeyWithTempl(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, + PK11Origin origin, CK_ATTRIBUTE *keyTemplate, + unsigned int templateCount, SECItem *key, void *wincx) +{ + PK11SymKey * symKey; + SECStatus rv; + + symKey = PK11_CreateSymKey(slot,type,wincx); + if (symKey == NULL) { + return NULL; + } + + symKey->size = key->len; + + PK11_SETATTRS(&keyTemplate[templateCount], CKA_VALUE, key->data, key->len); + templateCount++; + + if (SECITEM_CopyItem(NULL,&symKey->data,key) != SECSuccess) { + PK11_FreeSymKey(symKey); + return NULL; + } + + symKey->origin = origin; + + /* import the keys */ + rv = PK11_CreateNewObject(slot, symKey->session, keyTemplate, + templateCount, PR_FALSE, &symKey->objectID); + if ( rv != SECSuccess) { + PK11_FreeSymKey(symKey); + return NULL; + } + + return symKey; +} + +/* + * turn key bits into an appropriate key object + */ +PK11SymKey * +PK11_ImportSymKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, + PK11Origin origin, CK_ATTRIBUTE_TYPE operation, SECItem *key,void *wincx) +{ + PK11SymKey * symKey; + unsigned int templateCount = 0; + CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; + CK_BBOOL cktrue = CK_TRUE; /* sigh */ + CK_ATTRIBUTE keyTemplate[5]; + CK_ATTRIBUTE * attrs = keyTemplate; + + PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass) ); attrs++; + PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType) ); attrs++; + PK11_SETATTRS(attrs, operation, &cktrue, 1); attrs++; + /* PK11_SETATTRS(attrs, CKA_VALUE, key->data, key->len); attrs++; */ + templateCount = attrs - keyTemplate; + PR_ASSERT(templateCount <= sizeof(keyTemplate)/sizeof(CK_ATTRIBUTE)); + + keyType = PK11_GetKeyType(type,key->len); + symKey = pk11_ImportSymKeyWithTempl(slot, type, origin, keyTemplate, + templateCount, key, wincx); + return symKey; +} + +/* + * import a public key into the desired slot + */ +CK_OBJECT_HANDLE +PK11_ImportPublicKey(PK11SlotInfo *slot, SECKEYPublicKey *pubKey, + PRBool isToken) +{ + CK_BBOOL cktrue = CK_TRUE; + CK_BBOOL ckfalse = CK_FALSE; + CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY; + CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; + CK_OBJECT_HANDLE objectID; + CK_ATTRIBUTE theTemplate[10]; + CK_ATTRIBUTE *signedattr = NULL; + CK_ATTRIBUTE *attrs = theTemplate; + int signedcount = 0; + int templateCount = 0; + SECStatus rv; + + /* if we already have an object in the desired slot, use it */ + if (!isToken && pubKey->pkcs11Slot == slot) { + return pubKey->pkcs11ID; + } + + /* free the existing key */ + if (pubKey->pkcs11Slot != NULL) { + PK11SlotInfo *oSlot = pubKey->pkcs11Slot; + PK11_EnterSlotMonitor(oSlot); + (void) PK11_GETTAB(oSlot)->C_DestroyObject(oSlot->session, + pubKey->pkcs11ID); + PK11_ExitSlotMonitor(oSlot); + PK11_FreeSlot(oSlot); + pubKey->pkcs11Slot = NULL; + } + PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass) ); attrs++; + PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType) ); attrs++; + PK11_SETATTRS(attrs, CKA_TOKEN, isToken ? &cktrue : &ckfalse, + sizeof(CK_BBOOL) ); attrs++; + + /* now import the key */ + { + switch (pubKey->keyType) { + case rsaKey: + keyType = CKK_RSA; + PK11_SETATTRS(attrs, CKA_WRAP, &cktrue, sizeof(CK_BBOOL) ); attrs++; + PK11_SETATTRS(attrs, CKA_ENCRYPT, &cktrue, + sizeof(CK_BBOOL) ); attrs++; + PK11_SETATTRS(attrs, CKA_VERIFY, &cktrue, sizeof(CK_BBOOL)); attrs++; + signedattr = attrs; + PK11_SETATTRS(attrs, CKA_MODULUS, pubKey->u.rsa.modulus.data, + pubKey->u.rsa.modulus.len); attrs++; + PK11_SETATTRS(attrs, CKA_PUBLIC_EXPONENT, + pubKey->u.rsa.publicExponent.data, + pubKey->u.rsa.publicExponent.len); attrs++; + break; + case dsaKey: + keyType = CKK_DSA; + PK11_SETATTRS(attrs, CKA_VERIFY, &cktrue, sizeof(CK_BBOOL));attrs++; + signedattr = attrs; + PK11_SETATTRS(attrs, CKA_PRIME, pubKey->u.dsa.params.prime.data, + pubKey->u.dsa.params.prime.len); attrs++; + PK11_SETATTRS(attrs,CKA_SUBPRIME,pubKey->u.dsa.params.subPrime.data, + pubKey->u.dsa.params.subPrime.len); attrs++; + PK11_SETATTRS(attrs, CKA_BASE, pubKey->u.dsa.params.base.data, + pubKey->u.dsa.params.base.len); attrs++; + PK11_SETATTRS(attrs, CKA_VALUE, pubKey->u.dsa.publicValue.data, + pubKey->u.dsa.publicValue.len); attrs++; + break; + case fortezzaKey: + keyType = CKK_DSA; + PK11_SETATTRS(attrs, CKA_VERIFY, &cktrue, sizeof(CK_BBOOL));attrs++; + signedattr = attrs; + PK11_SETATTRS(attrs, CKA_PRIME,pubKey->u.fortezza.params.prime.data, + pubKey->u.fortezza.params.prime.len); attrs++; + PK11_SETATTRS(attrs,CKA_SUBPRIME, + pubKey->u.fortezza.params.subPrime.data, + pubKey->u.fortezza.params.subPrime.len);attrs++; + PK11_SETATTRS(attrs, CKA_BASE, pubKey->u.fortezza.params.base.data, + pubKey->u.fortezza.params.base.len); attrs++; + PK11_SETATTRS(attrs, CKA_VALUE, pubKey->u.fortezza.DSSKey.data, + pubKey->u.fortezza.DSSKey.len); attrs++; + break; + case dhKey: + keyType = CKK_DH; + PK11_SETATTRS(attrs, CKA_DERIVE, &cktrue, sizeof(CK_BBOOL));attrs++; + signedattr = attrs; + PK11_SETATTRS(attrs, CKA_PRIME, pubKey->u.dh.prime.data, + pubKey->u.dh.prime.len); attrs++; + PK11_SETATTRS(attrs, CKA_BASE, pubKey->u.dh.base.data, + pubKey->u.dh.base.len); attrs++; + PK11_SETATTRS(attrs, CKA_VALUE, pubKey->u.dh.publicValue.data, + pubKey->u.dh.publicValue.len); attrs++; + break; + /* what about fortezza??? */ + default: + PORT_SetError( SEC_ERROR_BAD_KEY ); + return CK_INVALID_KEY; + } + + templateCount = attrs - theTemplate; + signedcount = attrs - signedattr; + PORT_Assert(templateCount <= (sizeof(theTemplate)/sizeof(CK_ATTRIBUTE))); + for (attrs=signedattr; signedcount; attrs++, signedcount--) { + pk11_SignedToUnsigned(attrs); + } + rv = PK11_CreateNewObject(slot, CK_INVALID_SESSION, theTemplate, + templateCount, isToken, &objectID); + if ( rv != SECSuccess) { + return CK_INVALID_KEY; + } + } + + pubKey->pkcs11ID = objectID; + pubKey->pkcs11Slot = PK11_ReferenceSlot(slot); + + return objectID; +} + + +/* + * return the slot associated with a symetric key + */ +PK11SlotInfo * +PK11_GetSlotFromKey(PK11SymKey *symKey) +{ + return PK11_ReferenceSlot(symKey->slot); +} + +PK11SymKey * +PK11_FindFixedKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, SECItem *keyID, + void *wincx) +{ + CK_ATTRIBUTE findTemp[4]; + CK_ATTRIBUTE *attrs; + CK_BBOOL ckTrue = CK_TRUE; + CK_OBJECT_CLASS keyclass = CKO_SECRET_KEY; + int tsize = 0; + CK_OBJECT_HANDLE key_id; + + attrs = findTemp; + PK11_SETATTRS(attrs, CKA_CLASS, &keyclass, sizeof(keyclass)); attrs++; + PK11_SETATTRS(attrs, CKA_TOKEN, &ckTrue, sizeof(ckTrue)); attrs++; + if (keyID) { + PK11_SETATTRS(attrs, CKA_ID, keyID->data, keyID->len); attrs++; + } + tsize = attrs - findTemp; + PORT_Assert(tsize <= sizeof(findTemp)/sizeof(CK_ATTRIBUTE)); + + key_id = pk11_FindObjectByTemplate(slot,findTemp,tsize); + if (key_id == CK_INVALID_KEY) { + return NULL; + } + return PK11_SymKeyFromHandle(slot, NULL, PK11_OriginDerive, type, key_id, + PR_FALSE, wincx); +} + +void * +PK11_GetWindow(PK11SymKey *key) +{ + return key->cx; +} + + +/* + * extract a symetric key value. NOTE: if the key is sensitive, we will + * not be able to do this operation. This function is used to move + * keys from one token to another */ +SECStatus +PK11_ExtractKeyValue(PK11SymKey *symKey) +{ + + if (symKey->data.data != NULL) return SECSuccess; + + if (symKey->slot == NULL) { + PORT_SetError( SEC_ERROR_INVALID_KEY ); + return SECFailure; + } + + return PK11_ReadAttribute(symKey->slot,symKey->objectID,CKA_VALUE,NULL, + &symKey->data); +} + +SECItem * +PK11_GetKeyData(PK11SymKey *symKey) +{ + return &symKey->data; +} + +/* + * take an attribute and copy it into a secitem, converting unsigned to signed. + */ +static CK_RV +pk11_Attr2SecItem(PRArenaPool *arena, CK_ATTRIBUTE *attr, SECItem *item) { + unsigned char *dataPtr; + + item->len = attr->ulValueLen; + dataPtr = (unsigned char*) PORT_ArenaAlloc(arena, item->len+1); + if ( dataPtr == NULL) { + return CKR_HOST_MEMORY; + } + *dataPtr = 0; + item->data = dataPtr+1; + PORT_Memcpy(item->data,attr->pValue,item->len); + if (item->data[0] & 0x80) { + item->data = item->data-1; + item->len++; + } + return CKR_OK; +} +/* + * extract a public key from a slot and id + */ +SECKEYPublicKey * +PK11_ExtractPublicKey(PK11SlotInfo *slot,KeyType keyType,CK_OBJECT_HANDLE id) +{ + CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY; + PRArenaPool *arena; + PRArenaPool *tmp_arena; + SECKEYPublicKey *pubKey; + int templateCount = 0; + CK_KEY_TYPE pk11KeyType; + CK_RV crv; + CK_ATTRIBUTE template[8]; + CK_ATTRIBUTE *attrs= template; + CK_ATTRIBUTE *modulus,*exponent,*base,*prime,*subprime,*value; + + /* if we didn't know the key type, get it */ + if (keyType== nullKey) { + + pk11KeyType = PK11_ReadULongAttribute(slot,id,CKA_KEY_TYPE); + if (pk11KeyType == CK_UNAVAILABLE_INFORMATION) { + return NULL; + } + switch (pk11KeyType) { + case CKK_RSA: + keyType = rsaKey; + break; + case CKK_DSA: + keyType = dsaKey; + break; + case CKK_DH: + keyType = dhKey; + break; + default: + PORT_SetError( SEC_ERROR_BAD_KEY ); + return NULL; + } + } + + + /* now we need to create space for the public key */ + arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) return NULL; + tmp_arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE); + if (tmp_arena == NULL) { + PORT_FreeArena (arena, PR_FALSE); + return NULL; + } + + + pubKey = (SECKEYPublicKey *) + PORT_ArenaZAlloc(arena, sizeof(SECKEYPublicKey)); + if (pubKey == NULL) { + PORT_FreeArena (arena, PR_FALSE); + PORT_FreeArena (tmp_arena, PR_FALSE); + return NULL; + } + + pubKey->arena = arena; + pubKey->keyType = keyType; + pubKey->pkcs11Slot = PK11_ReferenceSlot(slot); + pubKey->pkcs11ID = id; + PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, + sizeof(keyClass)); attrs++; + PK11_SETATTRS(attrs, CKA_KEY_TYPE, &pk11KeyType, + sizeof(pk11KeyType) ); attrs++; + switch (pubKey->keyType) { + case rsaKey: + modulus = attrs; + PK11_SETATTRS(attrs, CKA_MODULUS, NULL, 0); attrs++; + exponent = attrs; + PK11_SETATTRS(attrs, CKA_PUBLIC_EXPONENT, NULL, 0); attrs++; + + templateCount = attrs - template; + PR_ASSERT(templateCount <= sizeof(template)/sizeof(CK_ATTRIBUTE)); + crv = PK11_GetAttributes(tmp_arena,slot,id,template,templateCount); + if (crv != CKR_OK) break; + + if ((keyClass != CKO_PUBLIC_KEY) || (pk11KeyType != CKK_RSA)) { + crv = CKR_OBJECT_HANDLE_INVALID; + break; + } + crv = pk11_Attr2SecItem(arena,modulus,&pubKey->u.rsa.modulus); + if (crv != CKR_OK) break; + crv = pk11_Attr2SecItem(arena,exponent,&pubKey->u.rsa.publicExponent); + if (crv != CKR_OK) break; + break; + case dsaKey: + prime = attrs; + PK11_SETATTRS(attrs, CKA_PRIME, NULL, 0); attrs++; + subprime = attrs; + PK11_SETATTRS(attrs, CKA_SUBPRIME, NULL, 0); attrs++; + base = attrs; + PK11_SETATTRS(attrs, CKA_BASE, NULL, 0); attrs++; + value = attrs; + PK11_SETATTRS(attrs, CKA_VALUE, NULL, 0); attrs++; + templateCount = attrs - template; + PR_ASSERT(templateCount <= sizeof(template)/sizeof(CK_ATTRIBUTE)); + crv = PK11_GetAttributes(tmp_arena,slot,id,template,templateCount); + if (crv != CKR_OK) break; + + if ((keyClass != CKO_PUBLIC_KEY) || (pk11KeyType != CKK_DSA)) { + crv = CKR_OBJECT_HANDLE_INVALID; + break; + } + crv = pk11_Attr2SecItem(arena,prime,&pubKey->u.dsa.params.prime); + if (crv != CKR_OK) break; + crv = pk11_Attr2SecItem(arena,subprime,&pubKey->u.dsa.params.subPrime); + if (crv != CKR_OK) break; + crv = pk11_Attr2SecItem(arena,base,&pubKey->u.dsa.params.base); + if (crv != CKR_OK) break; + crv = pk11_Attr2SecItem(arena,value,&pubKey->u.dsa.publicValue); + if (crv != CKR_OK) break; + break; + case dhKey: + prime = attrs; + PK11_SETATTRS(attrs, CKA_PRIME, NULL, 0); attrs++; + base = attrs; + PK11_SETATTRS(attrs, CKA_BASE, NULL, 0); attrs++; + value =attrs; + PK11_SETATTRS(attrs, CKA_VALUE, NULL, 0); attrs++; + templateCount = attrs - template; + PR_ASSERT(templateCount <= sizeof(template)/sizeof(CK_ATTRIBUTE)); + crv = PK11_GetAttributes(tmp_arena,slot,id,template,templateCount); + if (crv != CKR_OK) break; + + if ((keyClass != CKO_PUBLIC_KEY) || (pk11KeyType != CKK_DSA)) { + crv = CKR_OBJECT_HANDLE_INVALID; + break; + } + crv = pk11_Attr2SecItem(arena,prime,&pubKey->u.dh.prime); + if (crv != CKR_OK) break; + crv = pk11_Attr2SecItem(arena,base,&pubKey->u.dh.base); + if (crv != CKR_OK) break; + crv = pk11_Attr2SecItem(arena,value,&pubKey->u.dh.publicValue); + if (crv != CKR_OK) break; + break; + case fortezzaKey: + case nullKey: + default: + crv = CKR_OBJECT_HANDLE_INVALID; + break; + } + + PORT_FreeArena(tmp_arena,PR_FALSE); + + if (crv != CKR_OK) { + PORT_FreeArena(arena,PR_FALSE); + PK11_FreeSlot(slot); + PORT_SetError( PK11_MapError(crv) ); + return NULL; + } + + return pubKey; +} + +/* + * Build a Private Key structure from raw PKCS #11 information. + */ +SECKEYPrivateKey * +PK11_MakePrivKey(PK11SlotInfo *slot, KeyType keyType, + PRBool isTemp, CK_OBJECT_HANDLE privID, void *wincx) +{ + PRArenaPool *arena; + SECKEYPrivateKey *privKey; + + /* don't know? look it up */ + if (keyType == nullKey) { + CK_KEY_TYPE pk11Type = CKK_RSA; + + pk11Type = PK11_ReadULongAttribute(slot,privID,CKA_KEY_TYPE); + isTemp = (PRBool)!PK11_HasAttributeSet(slot,privID,CKA_TOKEN); + switch (pk11Type) { + case CKK_RSA: keyType = rsaKey; break; + case CKK_DSA: keyType = dsaKey; break; + case CKK_DH: keyType = dhKey; break; + case CKK_KEA: keyType = fortezzaKey; break; + default: + break; + } + } + + /* now we need to create space for the private key */ + arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) return NULL; + + privKey = (SECKEYPrivateKey *) + PORT_ArenaZAlloc(arena, sizeof(SECKEYPrivateKey)); + if (privKey == NULL) { + PORT_FreeArena(arena, PR_FALSE); + return NULL; + } + + privKey->arena = arena; + privKey->keyType = keyType; + privKey->pkcs11Slot = PK11_ReferenceSlot(slot); + privKey->pkcs11ID = privID; + privKey->pkcs11IsTemp = isTemp; + privKey->wincx = wincx; + + return privKey; +} + +/* return the keylength if possible. '0' if not */ +unsigned int +PK11_GetKeyLength(PK11SymKey *key) +{ + if (key->size != 0) return key->size ; + if (key->data.data == NULL) { + PK11_ExtractKeyValue(key); + } + /* key is probably secret. Look up it's type and length */ + /* this is new PKCS #11 version 2.0 functionality. */ + if (key->size == 0) { + CK_ULONG keyLength; + + keyLength = PK11_ReadULongAttribute(key->slot,key->objectID,CKA_VALUE_LEN); + /* doesn't have a length field, check the known PKCS #11 key types, + * which don't have this field */ + if (keyLength == CK_UNAVAILABLE_INFORMATION) { + CK_KEY_TYPE keyType; + keyType = PK11_ReadULongAttribute(key->slot,key->objectID,CKA_KEY_TYPE); + switch (keyType) { + case CKK_DES: key->size = 8; break; + case CKK_DES2: key->size = 16; break; + case CKK_DES3: key->size = 24; break; + case CKK_SKIPJACK: key->size = 10; break; + case CKK_BATON: key->size = 20; break; + case CKK_JUNIPER: key->size = 20; break; + case CKK_GENERIC_SECRET: + if (key->type == CKM_SSL3_PRE_MASTER_KEY_GEN) { + key->size=48; + } + break; + default: break; + } + } else { + key->size = (unsigned int)keyLength; + } + } + + return key->size; +} + +/* return the strength of a key. This is different from length in that + * 1) it returns the size in bits, and 2) it returns only the secret portions + * of the key minus any checksums or parity. + */ +unsigned int +PK11_GetKeyStrength(PK11SymKey *key, SECAlgorithmID *algid) +{ + int size=0; + CK_MECHANISM_TYPE mechanism= CKM_INVALID_MECHANISM; /* RC2 only */ + SECItem *param = NULL; /* RC2 only */ + CK_RC2_CBC_PARAMS *rc2_params = NULL; /* RC2 ONLY */ + unsigned int effectiveBits = 0; /* RC2 ONLY */ + + switch (PK11_GetKeyType(key->type,0)) { + case CKK_CDMF: + return 40; + case CKK_DES: + return 56; + case CKK_DES3: + case CKK_DES2: + size = PK11_GetKeyLength(key); + if (size == 16) { + /* double des */ + return 112; /* 16*7 */ + } + return 168; + /* + * RC2 has is different than other ciphers in that it allows the user + * to deprecating keysize while still requiring all the bits for the + * original key. The info + * on what the effective key strength is in the parameter for the key. + * In S/MIME this parameter is stored in the DER encoded algid. In Our + * other uses of RC2, effectiveBits == keyBits, so this code functions + * correctly without an algid. + */ + case CKK_RC2: + /* if no algid was provided, fall through to default */ + if (!algid) { + break; + } + /* verify that the algid is for RC2 */ + mechanism = PK11_AlgtagToMechanism(SECOID_GetAlgorithmTag(algid)); + if ((mechanism != CKM_RC2_CBC) && (mechanism != CKM_RC2_ECB)) { + break; + } + + /* now get effective bits from the algorithm ID. */ + param = PK11_ParamFromAlgid(algid); + /* if we couldn't get memory just use key length */ + if (param == NULL) { + break; + } + + rc2_params = (CK_RC2_CBC_PARAMS *) param->data; + /* paranoia... shouldn't happen */ + PORT_Assert(param->data != NULL); + if (param->data == NULL) { + SECITEM_FreeItem(param,PR_TRUE); + break; + } + effectiveBits = (unsigned int)rc2_params->ulEffectiveBits; + SECITEM_FreeItem(param,PR_TRUE); + param = NULL; rc2_params=NULL; /* paranoia */ + + /* we have effective bits, is and allocated memory is free, now + * we need to return the smaller of effective bits and keysize */ + size = PK11_GetKeyLength(key); + if ((unsigned int)size*8 > effectiveBits) { + return effectiveBits; + } + + return size*8; /* the actual key is smaller, the strength can't be + * greater than the actual key size */ + + default: + break; + } + return PK11_GetKeyLength(key) * 8; +} + +/* Make a Key type to an appropriate signing/verification mechanism */ +static CK_MECHANISM_TYPE +pk11_mapSignKeyType(KeyType keyType) +{ + switch (keyType) { + case rsaKey: + return CKM_RSA_PKCS; + case fortezzaKey: + case dsaKey: + return CKM_DSA; + case dhKey: + default: + break; + } + return CKM_INVALID_MECHANISM; +} + +static CK_MECHANISM_TYPE +pk11_mapWrapKeyType(KeyType keyType) +{ + switch (keyType) { + case rsaKey: + return CKM_RSA_PKCS; + /* Add fortezza?? */ + default: + break; + } + return CKM_INVALID_MECHANISM; +} + +/* + * Some non-compliant PKCS #11 vendors do not give us the modulus, so actually + * set up a signature to get the signaure length. + */ +static int +pk11_backupGetSignLength(SECKEYPrivateKey *key) +{ + PK11SlotInfo *slot = key->pkcs11Slot; + CK_MECHANISM mech = {0, NULL, 0 }; + PRBool owner = PR_TRUE; + CK_SESSION_HANDLE session; + CK_ULONG len; + CK_RV crv; + unsigned char h_data[20] = { 0 }; + unsigned char buf[20]; /* obviously to small */ + CK_ULONG smallLen = sizeof(buf); + + mech.mechanism = pk11_mapSignKeyType(key->keyType); + + session = pk11_GetNewSession(slot,&owner); + if (!owner || !(slot->isThreadSafe)) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_SignInit(session,&mech,key->pkcs11ID); + if (crv != CKR_OK) { + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + PORT_SetError( PK11_MapError(crv) ); + return -1; + } + len = 0; + crv = PK11_GETTAB(slot)->C_Sign(session,h_data,sizeof(h_data), + NULL, &len); + /* now call C_Sign with too small a buffer to clear the session state */ + (void) PK11_GETTAB(slot)-> + C_Sign(session,h_data,sizeof(h_data),buf,&smallLen); + + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return -1; + } + return len; +} +/* + * get the length of a signature object based on the key + */ +int +PK11_SignatureLen(SECKEYPrivateKey *key) +{ + PK11SlotInfo *slot = key->pkcs11Slot; + int val; + + switch (key->keyType) { + case rsaKey: + val = PK11_GetPrivateModulusLen(key); + if (val == -1) { + break; /* failed */ + } + return (unsigned long) val; + + case fortezzaKey: + case dsaKey: + return 40; + + default: + break; + } + PORT_SetError( SEC_ERROR_INVALID_KEY ); + return 0; +} + +PK11SlotInfo * +PK11_GetSlotFromPrivateKey(SECKEYPrivateKey *key) +{ + PK11SlotInfo *slot = key->pkcs11Slot; + slot = PK11_ReferenceSlot(slot); + return slot; +} + +/* + * Get the modulus length for raw parsing + */ +int +PK11_GetPrivateModulusLen(SECKEYPrivateKey *key) +{ + CK_ATTRIBUTE theTemplate = { CKA_MODULUS, NULL, 0 }; + PK11SlotInfo *slot = key->pkcs11Slot; + CK_RV crv; + int length; + + switch (key->keyType) { + case rsaKey: + crv = PK11_GetAttributes(NULL, slot, key->pkcs11ID, &theTemplate, 1); + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return -1; + } + length = theTemplate.ulValueLen; + if ( *(unsigned char *)theTemplate.pValue == 0) { + length--; + } + if (theTemplate.pValue != NULL) + PORT_Free(theTemplate.pValue); + return (int) length; + + case fortezzaKey: + case dsaKey: + case dhKey: + default: + break; + } + if (theTemplate.pValue != NULL) + PORT_Free(theTemplate.pValue); + PORT_SetError( SEC_ERROR_INVALID_KEY ); + return -1; +} + +/* + * copy a key (or any other object) on a token + */ +CK_OBJECT_HANDLE +PK11_CopyKey(PK11SlotInfo *slot, CK_OBJECT_HANDLE srcObject) +{ + CK_OBJECT_HANDLE destObject; + CK_RV crv; + + PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_CopyObject(slot->session,srcObject,NULL,0, + &destObject); + PK11_ExitSlotMonitor(slot); + if (crv == CKR_OK) return destObject; + PORT_SetError( PK11_MapError(crv) ); + return CK_INVALID_KEY; +} + + +PK11SymKey * +pk11_KeyExchange(PK11SlotInfo *slot,CK_MECHANISM_TYPE type, + CK_ATTRIBUTE_TYPE operation, PK11SymKey *symKey); + +/* + * The next two utilities are to deal with the fact that a given operation + * may be a multi-slot affair. This creates a new key object that is copied + * into the new slot. + */ +PK11SymKey * +pk11_CopyToSlot(PK11SlotInfo *slot,CK_MECHANISM_TYPE type, + CK_ATTRIBUTE_TYPE operation, PK11SymKey *symKey) +{ + SECStatus rv; + PK11SymKey *newKey = NULL; + + /* Extract the raw key data if possible */ + if (symKey->data.data == NULL) { + rv = PK11_ExtractKeyValue(symKey); + /* KEY is sensitive, we're try key exchanging it. */ + if (rv != SECSuccess) { + return pk11_KeyExchange(slot, type, operation, symKey); + } + } + newKey = PK11_ImportSymKey(slot, type, symKey->origin, operation, + &symKey->data, symKey->cx); + if (newKey == NULL) newKey = pk11_KeyExchange(slot,type,operation,symKey); + return newKey; +} + +/* + * Make sure the slot we are in the correct slot for the operation + */ +static PK11SymKey * +pk11_ForceSlot(PK11SymKey *symKey,CK_MECHANISM_TYPE type, + CK_ATTRIBUTE_TYPE operation) +{ + PK11SlotInfo *slot = symKey->slot; + PK11SymKey *newKey = NULL; + + if ((slot== NULL) || !PK11_DoesMechanism(slot,type)) { + slot = PK11_GetBestSlot(type,symKey->cx); + if (slot == NULL) { + PORT_SetError( SEC_ERROR_NO_MODULE ); + return NULL; + } + newKey = pk11_CopyToSlot(slot, type, operation, symKey); + PK11_FreeSlot(slot); + } + return newKey; +} + +/* + * Use the token to Generate a key. keySize must be 'zero' for fixed key + * length algorithms. NOTE: this means we can never generate a DES2 key + * from this interface! + */ +PK11SymKey * +PK11_TokenKeyGen(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, SECItem *param, + int keySize, SECItem *keyid, PRBool isToken, void *wincx) +{ + PK11SymKey *symKey; + CK_ATTRIBUTE genTemplate[4]; + CK_ATTRIBUTE *attrs = genTemplate; + int count = sizeof(genTemplate)/sizeof(genTemplate[0]); + CK_SESSION_HANDLE session; + CK_MECHANISM mechanism; + CK_RV crv; + PRBool weird = PR_FALSE; /* hack for fortezza */ + CK_BBOOL ckfalse = CK_FALSE; + CK_BBOOL cktrue = CK_TRUE; + + if ((keySize == -1) && (type == CKM_SKIPJACK_CBC64)) { + weird = PR_TRUE; + keySize = 0; + } + + /* TNH: Isn't this redundant, since "handleKey" will set defaults? */ + PK11_SETATTRS(attrs, (!weird) + ? CKA_ENCRYPT : CKA_DECRYPT, &cktrue, sizeof(CK_BBOOL)); attrs++; + + if (keySize != 0) { + CK_ULONG key_size = keySize; /* Convert to PK11 type */ + + PK11_SETATTRS(attrs, CKA_VALUE_LEN, &key_size, sizeof(key_size)); + attrs++; + } + + /* Include key id value if provided */ + if (keyid) { + PK11_SETATTRS(attrs, CKA_ID, keyid->data, keyid->len); attrs++; + } + + if (isToken) { + PK11_SETATTRS(attrs, CKA_TOKEN, &cktrue, sizeof(cktrue)); attrs++; + } + + count = attrs - genTemplate; + PR_ASSERT(count <= sizeof(genTemplate)/sizeof(CK_ATTRIBUTE)); + + /* find a slot to generate the key into */ + /* Only do slot management if this is not a token key */ + if (!isToken && (slot == NULL || !PK11_DoesMechanism(slot,type))) { + PK11SlotInfo *bestSlot; + + bestSlot = PK11_GetBestSlot(type,wincx); /* TNH: references the slot? */ + if (bestSlot == NULL) { + PORT_SetError( SEC_ERROR_NO_MODULE ); + return NULL; + } + + symKey = PK11_CreateSymKey(bestSlot,type,wincx); + + PK11_FreeSlot(bestSlot); + } else { + symKey = PK11_CreateSymKey(slot, type, wincx); + } + if (symKey == NULL) return NULL; + + symKey->size = keySize; + symKey->origin = (!weird) ? PK11_OriginGenerated : PK11_OriginFortezzaHack; + + /* Initialize the Key Gen Mechanism */ + mechanism.mechanism = PK11_GetKeyGen(type); + if (mechanism.mechanism == CKM_FAKE_RANDOM) { + PORT_SetError( SEC_ERROR_NO_MODULE ); + return NULL; + } + + /* Set the parameters for the key gen if provided */ + mechanism.pParameter = NULL; + mechanism.ulParameterLen = 0; + if (param) { + mechanism.pParameter = param->data; + mechanism.ulParameterLen = param->len; + } + + /* Get session and perform locking */ + if (isToken) { + session = PK11_GetRWSession(symKey->slot); /* Should always be original slot */ + } else { + session = symKey->session; + pk11_EnterKeyMonitor(symKey); + } + + crv = PK11_GETTAB(symKey->slot)->C_GenerateKey(session, + &mechanism, genTemplate, count, &symKey->objectID); + + /* Release lock and session */ + if (isToken) { + PK11_RestoreROSession(symKey->slot, session); + } else { + pk11_ExitKeyMonitor(symKey); + } + + if (crv != CKR_OK) { + PK11_FreeSymKey(symKey); + PORT_SetError( PK11_MapError(crv) ); + return NULL; + } + + return symKey; +} + +PK11SymKey * +PK11_KeyGen(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, SECItem *param, + int keySize, void *wincx) +{ + return PK11_TokenKeyGen(slot, type, param, keySize, 0, PR_FALSE, wincx); +} + +/* --- */ +PK11SymKey * +PK11_GenDES3TokenKey(PK11SlotInfo *slot, SECItem *keyid, void *cx) +{ + return PK11_TokenKeyGen(slot, CKM_DES3_CBC, 0, 0, keyid, PR_TRUE, cx); +} + +/* + * PKCS #11 pairwise consistency check utilized to validate key pair. + */ +static SECStatus +pk11_PairwiseConsistencyCheck(SECKEYPublicKey *pubKey, + SECKEYPrivateKey *privKey, CK_MECHANISM *mech, void* wincx ) +{ + /* Variables used for Encrypt/Decrypt functions. */ + unsigned char *known_message = (unsigned char *)"Known Crypto Message"; + CK_BBOOL isEncryptable = CK_FALSE; + CK_BBOOL canSignVerify = CK_FALSE; + CK_BBOOL isDerivable = CK_FALSE; + unsigned char plaintext[PAIRWISE_MESSAGE_LENGTH]; + CK_ULONG bytes_decrypted; + PK11SlotInfo *slot; + CK_OBJECT_HANDLE id; + unsigned char *ciphertext; + unsigned char *text_compared; + CK_ULONG max_bytes_encrypted; + CK_ULONG bytes_encrypted; + CK_ULONG bytes_compared; + CK_RV crv; + + /* Variables used for Signature/Verification functions. */ + unsigned char *known_digest = (unsigned char *)"Mozilla Rules World!"; + SECItem signature; + SECItem digest; /* always uses SHA-1 digest */ + int signature_length; + SECStatus rv; + + /**************************************************/ + /* Pairwise Consistency Check of Encrypt/Decrypt. */ + /**************************************************/ + + isEncryptable = PK11_HasAttributeSet( privKey->pkcs11Slot, + privKey->pkcs11ID, CKA_DECRYPT ); + + /* If the encryption attribute is set; attempt to encrypt */ + /* with the public key and decrypt with the private key. */ + if( isEncryptable ) { + /* Find a module to encrypt against */ + slot = PK11_GetBestSlot(pk11_mapWrapKeyType(privKey->keyType),wincx); + if (slot == NULL) { + PORT_SetError( SEC_ERROR_NO_MODULE ); + return SECFailure; + } + + id = PK11_ImportPublicKey(slot,pubKey,PR_FALSE); + if (id == CK_INVALID_KEY) { + PK11_FreeSlot(slot); + return SECFailure; + } + + /* Compute max bytes encrypted from modulus length of private key. */ + max_bytes_encrypted = PK11_GetPrivateModulusLen( privKey ); + + + /* Prepare for encryption using the public key. */ + PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB( slot )->C_EncryptInit( slot->session, + mech, id ); + if( crv != CKR_OK ) { + PK11_ExitSlotMonitor(slot); + PORT_SetError( PK11_MapError( crv ) ); + PK11_FreeSlot(slot); + return SECFailure; + } + + /* Allocate space for ciphertext. */ + ciphertext = (unsigned char *) PORT_Alloc( max_bytes_encrypted ); + if( ciphertext == NULL ) { + PK11_ExitSlotMonitor(slot); + PORT_SetError( SEC_ERROR_NO_MEMORY ); + PK11_FreeSlot(slot); + return SECFailure; + } + + /* Initialize bytes encrypted to max bytes encrypted. */ + bytes_encrypted = max_bytes_encrypted; + + /* Encrypt using the public key. */ + crv = PK11_GETTAB( slot )->C_Encrypt( slot->session, + known_message, + PAIRWISE_MESSAGE_LENGTH, + ciphertext, + &bytes_encrypted ); + PK11_ExitSlotMonitor(slot); + PK11_FreeSlot(slot); + if( crv != CKR_OK ) { + PORT_SetError( PK11_MapError( crv ) ); + PORT_Free( ciphertext ); + return SECFailure; + } + + /* Always use the smaller of these two values . . . */ + bytes_compared = ( bytes_encrypted > PAIRWISE_MESSAGE_LENGTH ) + ? PAIRWISE_MESSAGE_LENGTH + : bytes_encrypted; + + /* If there was a failure, the plaintext */ + /* goes at the end, therefore . . . */ + text_compared = ( bytes_encrypted > PAIRWISE_MESSAGE_LENGTH ) + ? (ciphertext + bytes_encrypted - + PAIRWISE_MESSAGE_LENGTH ) + : ciphertext; + + /* Check to ensure that ciphertext does */ + /* NOT EQUAL known input message text */ + /* per FIPS PUB 140-1 directive. */ + if( ( bytes_encrypted != max_bytes_encrypted ) || + ( PORT_Memcmp( text_compared, known_message, + bytes_compared ) == 0 ) ) { + /* Set error to Invalid PRIVATE Key. */ + PORT_SetError( SEC_ERROR_INVALID_KEY ); + PORT_Free( ciphertext ); + return SECFailure; + } + + slot = privKey->pkcs11Slot; + /* Prepare for decryption using the private key. */ + PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB( slot )->C_DecryptInit( slot->session, + mech, + privKey->pkcs11ID ); + if( crv != CKR_OK ) { + PK11_ExitSlotMonitor(slot); + PORT_SetError( PK11_MapError(crv) ); + PORT_Free( ciphertext ); + PK11_FreeSlot(slot); + return SECFailure; + } + + /* Initialize bytes decrypted to be the */ + /* expected PAIRWISE_MESSAGE_LENGTH. */ + bytes_decrypted = PAIRWISE_MESSAGE_LENGTH; + + /* Decrypt using the private key. */ + /* NOTE: No need to reset the */ + /* value of bytes_encrypted. */ + crv = PK11_GETTAB( slot )->C_Decrypt( slot->session, + ciphertext, + bytes_encrypted, + plaintext, + &bytes_decrypted ); + PK11_ExitSlotMonitor(slot); + + /* Finished with ciphertext; free it. */ + PORT_Free( ciphertext ); + + if( crv != CKR_OK ) { + PORT_SetError( PK11_MapError(crv) ); + PK11_FreeSlot(slot); + return SECFailure; + } + + /* Check to ensure that the output plaintext */ + /* does EQUAL known input message text. */ + if( ( bytes_decrypted != PAIRWISE_MESSAGE_LENGTH ) || + ( PORT_Memcmp( plaintext, known_message, + PAIRWISE_MESSAGE_LENGTH ) != 0 ) ) { + /* Set error to Bad PUBLIC Key. */ + PORT_SetError( SEC_ERROR_BAD_KEY ); + PK11_FreeSlot(slot); + return SECFailure; + } + } + + /**********************************************/ + /* Pairwise Consistency Check of Sign/Verify. */ + /**********************************************/ + + canSignVerify = PK11_HasAttributeSet ( privKey->pkcs11Slot, + privKey->pkcs11ID, CKA_VERIFY); + + if (canSignVerify) + { + /* Initialize signature and digest data. */ + signature.data = NULL; + digest.data = NULL; + + /* Determine length of signature. */ + signature_length = PK11_SignatureLen( privKey ); + if( signature_length == 0 ) + goto failure; + + /* Allocate space for signature data. */ + signature.data = (unsigned char *) PORT_Alloc( signature_length ); + if( signature.data == NULL ) { + PORT_SetError( SEC_ERROR_NO_MEMORY ); + goto failure; + } + + /* Allocate space for known digest data. */ + digest.data = (unsigned char *) PORT_Alloc( PAIRWISE_DIGEST_LENGTH ); + if( digest.data == NULL ) { + PORT_SetError( SEC_ERROR_NO_MEMORY ); + goto failure; + } + + /* "Fill" signature type and length. */ + signature.type = PAIRWISE_SECITEM_TYPE; + signature.len = signature_length; + + /* "Fill" digest with known SHA-1 digest parameters. */ + digest.type = PAIRWISE_SECITEM_TYPE; + PORT_Memcpy( digest.data, known_digest, PAIRWISE_DIGEST_LENGTH ); + digest.len = PAIRWISE_DIGEST_LENGTH; + + /* Sign the known hash using the private key. */ + rv = PK11_Sign( privKey, &signature, &digest ); + if( rv != SECSuccess ) + goto failure; + + /* Verify the known hash using the public key. */ + rv = PK11_Verify( pubKey, &signature, &digest, wincx ); + if( rv != SECSuccess ) + goto failure; + + /* Free signature and digest data. */ + PORT_Free( signature.data ); + PORT_Free( digest.data ); + } + + + + /**********************************************/ + /* Pairwise Consistency Check for Derivation */ + /**********************************************/ + + isDerivable = PK11_HasAttributeSet ( privKey->pkcs11Slot, + privKey->pkcs11ID, CKA_DERIVE); + + if (isDerivable) + { + /* + * We are not doing consistency check for Diffie-Hellman Key - + * otherwise it would be here + */ + + } + + return SECSuccess; + +failure: + if( signature.data != NULL ) + PORT_Free( signature.data ); + if( digest.data != NULL ) + PORT_Free( digest.data ); + + return SECFailure; +} + + + +/* + * take a private key in one pkcs11 module and load it into another: + * NOTE: the source private key is a rare animal... it can't be sensitive. + * This is used to do a key gen using one pkcs11 module and storing the + * result into another. + */ +SECKEYPrivateKey * +pk11_loadPrivKey(PK11SlotInfo *slot,SECKEYPrivateKey *privKey, + SECKEYPublicKey *pubKey, PRBool token, PRBool sensitive) +{ + CK_ATTRIBUTE privTemplate[] = { + /* class must be first */ + { CKA_CLASS, NULL, 0 }, + { CKA_KEY_TYPE, NULL, 0 }, + /* these three must be next */ + { CKA_TOKEN, NULL, 0 }, + { CKA_PRIVATE, NULL, 0 }, + { CKA_SENSITIVE, NULL, 0 }, + { CKA_ID, NULL, 0 }, +#ifdef notdef + { CKA_LABEL, NULL, 0 }, + { CKA_SUBJECT, NULL, 0 }, +#endif + /* RSA */ + { CKA_MODULUS, NULL, 0 }, + { CKA_PRIVATE_EXPONENT, NULL, 0 }, + { CKA_PUBLIC_EXPONENT, NULL, 0 }, + { CKA_PRIME_1, NULL, 0 }, + { CKA_PRIME_2, NULL, 0 }, + { CKA_EXPONENT_1, NULL, 0 }, + { CKA_EXPONENT_2, NULL, 0 }, + { CKA_COEFFICIENT, NULL, 0 }, + }; + CK_ATTRIBUTE *attrs = NULL, *ap; + int templateSize = sizeof(privTemplate)/sizeof(privTemplate[0]); + PRArenaPool *arena; + CK_OBJECT_HANDLE objectID; + int i, count = 0; + int extra_count = 0; + CK_RV crv; + SECStatus rv; + + for (i=0; i < templateSize; i++) { + if (privTemplate[i].type == CKA_MODULUS) { + attrs= &privTemplate[i]; + count = i; + break; + } + } + PORT_Assert(attrs != NULL); + if (attrs == NULL) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return NULL; + } + + ap = attrs; + + switch (privKey->keyType) { + case rsaKey: + count = templateSize; + extra_count = templateSize - (attrs - privTemplate); + break; + case dsaKey: + ap->type = CKA_PRIME; ap++; count++; extra_count++; + ap->type = CKA_SUBPRIME; ap++; count++; extra_count++; + ap->type = CKA_BASE; ap++; count++; extra_count++; + ap->type = CKA_VALUE; ap++; count++; extra_count++; + break; + case dhKey: + ap->type = CKA_PRIME; ap++; count++; extra_count++; + ap->type = CKA_BASE; ap++; count++; extra_count++; + ap->type = CKA_VALUE; ap++; count++; extra_count++; + break; + default: + count = 0; + extra_count = 0; + break; + } + + if (count == 0) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return NULL; + } + + arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) return NULL; + /* + * read out the old attributes. + */ + crv = PK11_GetAttributes(arena, privKey->pkcs11Slot, privKey->pkcs11ID, + privTemplate,count); + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + PORT_FreeArena(arena, PR_TRUE); + return NULL; + } + + /* Reset sensitive, token, and private */ + *(CK_BBOOL *)(privTemplate[2].pValue) = token ? CK_TRUE : CK_FALSE; + *(CK_BBOOL *)(privTemplate[3].pValue) = token ? CK_TRUE : CK_FALSE; + *(CK_BBOOL *)(privTemplate[4].pValue) = sensitive ? CK_TRUE : CK_FALSE; + + /* Not everyone can handle zero padded key values, give + * them the raw data as unsigned */ + for (ap=attrs; extra_count; ap++, extra_count--) { + pk11_SignedToUnsigned(ap); + } + + /* now Store the puppies */ + rv = PK11_CreateNewObject(slot, CK_INVALID_SESSION, privTemplate, + count, token, &objectID); + PORT_FreeArena(arena, PR_TRUE); + if (rv != SECSuccess) { + return NULL; + } + + /* try loading the public key as a token object */ + if (pubKey) { + PK11_ImportPublicKey(slot, pubKey, PR_TRUE); + if (pubKey->pkcs11Slot) { + PK11_FreeSlot(pubKey->pkcs11Slot); + pubKey->pkcs11Slot = NULL; + pubKey->pkcs11ID = CK_INVALID_KEY; + } + } + + /* build new key structure */ + return PK11_MakePrivKey(slot, privKey->keyType, (PRBool)!token, + objectID, privKey->wincx); +} + + +/* + * Use the token to Generate a key. keySize must be 'zero' for fixed key + * length algorithms. NOTE: this means we can never generate a DES2 key + * from this interface! + */ +SECKEYPrivateKey * +PK11_GenerateKeyPair(PK11SlotInfo *slot,CK_MECHANISM_TYPE type, + void *param, SECKEYPublicKey **pubKey, PRBool token, + PRBool sensitive, void *wincx) +{ + /* we have to use these native types because when we call PKCS 11 modules + * we have to make sure that we are using the correct sizes for all the + * parameters. */ + CK_BBOOL ckfalse = CK_FALSE; + CK_BBOOL cktrue = CK_TRUE; + CK_ULONG modulusBits; + CK_BYTE publicExponent[4]; + CK_ATTRIBUTE privTemplate[] = { + { CKA_SENSITIVE, NULL, 0}, + { CKA_TOKEN, NULL, 0}, + { CKA_PRIVATE, NULL, 0}, + { CKA_DERIVE, NULL, 0}, + { CKA_UNWRAP, NULL, 0}, + { CKA_SIGN, NULL, 0}, + { CKA_DECRYPT, NULL, 0}, + }; + CK_ATTRIBUTE rsaPubTemplate[] = { + { CKA_MODULUS_BITS, NULL, 0}, + { CKA_PUBLIC_EXPONENT, NULL, 0}, + { CKA_TOKEN, NULL, 0}, + { CKA_DERIVE, NULL, 0}, + { CKA_WRAP, NULL, 0}, + { CKA_VERIFY, NULL, 0}, + { CKA_VERIFY_RECOVER, NULL, 0}, + { CKA_ENCRYPT, NULL, 0}, + }; + CK_ATTRIBUTE dsaPubTemplate[] = { + { CKA_PRIME, NULL, 0 }, + { CKA_SUBPRIME, NULL, 0 }, + { CKA_BASE, NULL, 0 }, + { CKA_TOKEN, NULL, 0}, + { CKA_DERIVE, NULL, 0}, + { CKA_WRAP, NULL, 0}, + { CKA_VERIFY, NULL, 0}, + { CKA_VERIFY_RECOVER, NULL, 0}, + { CKA_ENCRYPT, NULL, 0}, + }; + CK_ATTRIBUTE dhPubTemplate[] = { + { CKA_PRIME, NULL, 0 }, + { CKA_BASE, NULL, 0 }, + { CKA_TOKEN, NULL, 0}, + { CKA_DERIVE, NULL, 0}, + { CKA_WRAP, NULL, 0}, + { CKA_VERIFY, NULL, 0}, + { CKA_VERIFY_RECOVER, NULL, 0}, + { CKA_ENCRYPT, NULL, 0}, + }; + + int dsaPubCount = sizeof(dsaPubTemplate)/sizeof(dsaPubTemplate[0]); + /*CK_ULONG key_size = 0;*/ + CK_ATTRIBUTE *pubTemplate; + int privCount = sizeof(privTemplate)/sizeof(privTemplate[0]); + int rsaPubCount = sizeof(rsaPubTemplate)/sizeof(rsaPubTemplate[0]); + int dhPubCount = sizeof(dhPubTemplate)/sizeof(dhPubTemplate[0]); + int pubCount = 0; + PK11RSAGenParams *rsaParams; + PQGParams *dsaParams; + DHParams * dhParams; + CK_MECHANISM mechanism; + CK_MECHANISM test_mech; + CK_SESSION_HANDLE session_handle; + CK_RV crv; + CK_OBJECT_HANDLE privID,pubID; + SECKEYPrivateKey *privKey; + KeyType keyType; + PRBool restore; + int peCount,i; + CK_ATTRIBUTE *attrs; + CK_ATTRIBUTE *privattrs; + SECItem *pubKeyIndex; + CK_ATTRIBUTE setTemplate; + SECStatus rv; + CK_MECHANISM_INFO mechanism_info; + CK_OBJECT_CLASS keyClass; + SECItem *cka_id; + PRBool haslock = PR_FALSE; + PRBool pubIsToken = PR_FALSE; + + PORT_Assert(slot != NULL); + if (slot == NULL) { + PORT_SetError( SEC_ERROR_NO_MODULE); + return NULL; + } + + /* if our slot really doesn't do this mechanism, Generate the key + * in our internal token and write it out */ + if (!PK11_DoesMechanism(slot,type)) { + PK11SlotInfo *int_slot = PK11_GetInternalSlot(); + + /* don't loop forever looking for a slot */ + if (slot == int_slot) { + PK11_FreeSlot(int_slot); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return NULL; + } + + /* if there isn't a suitable slot, then we can't do the keygen */ + if (int_slot == NULL) { + PORT_SetError( SEC_ERROR_NO_MODULE ); + return NULL; + } + + /* generate the temporary key to load */ + privKey = PK11_GenerateKeyPair(int_slot,type, param, pubKey, PR_FALSE, + PR_FALSE, wincx); + PK11_FreeSlot(int_slot); + + /* if successful, load the temp key into the new token */ + if (privKey != NULL) { + SECKEYPrivateKey *newPrivKey = pk11_loadPrivKey(slot,privKey, + *pubKey,token,sensitive); + SECKEY_DestroyPrivateKey(privKey); + if (newPrivKey == NULL) { + SECKEY_DestroyPublicKey(*pubKey); + *pubKey = NULL; + } + return newPrivKey; + } + return NULL; + } + + + mechanism.mechanism = type; + mechanism.pParameter = NULL; + mechanism.ulParameterLen = 0; + test_mech.pParameter = NULL; + test_mech.ulParameterLen = 0; + + /* set up the private key template */ + privattrs = privTemplate; + PK11_SETATTRS(privattrs, CKA_SENSITIVE, sensitive ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); privattrs++; + PK11_SETATTRS(privattrs, CKA_TOKEN, token ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); privattrs++; + PK11_SETATTRS(privattrs, CKA_PRIVATE, sensitive ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); privattrs++; + + /* set up the mechanism specific info */ + switch (type) { + case CKM_RSA_PKCS_KEY_PAIR_GEN: + rsaParams = (PK11RSAGenParams *)param; + modulusBits = rsaParams->keySizeInBits; + peCount = 0; + + /* convert pe to a PKCS #11 string */ + for (i=0; i < 4; i++) { + if (peCount || (rsaParams->pe & + ((unsigned long)0xff000000L >> (i*8)))) { + publicExponent[peCount] = + (CK_BYTE)((rsaParams->pe >> (3-i)*8) & 0xff); + peCount++; + } + } + PORT_Assert(peCount != 0); + attrs = rsaPubTemplate; + PK11_SETATTRS(attrs, CKA_MODULUS_BITS, + &modulusBits, sizeof(modulusBits)); attrs++; + PK11_SETATTRS(attrs, CKA_PUBLIC_EXPONENT, + publicExponent, peCount);attrs++; + pubTemplate = rsaPubTemplate; + pubCount = rsaPubCount; + keyType = rsaKey; + test_mech.mechanism = CKM_RSA_PKCS; + break; + case CKM_DSA_KEY_PAIR_GEN: + dsaParams = (PQGParams *)param; + attrs = dsaPubTemplate; + PK11_SETATTRS(attrs, CKA_PRIME, dsaParams->prime.data, + dsaParams->prime.len); attrs++; + PK11_SETATTRS(attrs, CKA_SUBPRIME, dsaParams->subPrime.data, + dsaParams->subPrime.len); attrs++; + PK11_SETATTRS(attrs, CKA_BASE, dsaParams->base.data, + dsaParams->base.len); attrs++; + pubTemplate = dsaPubTemplate; + pubCount = dsaPubCount; + keyType = dsaKey; + test_mech.mechanism = CKM_DSA; + break; + case CKM_DH_PKCS_KEY_PAIR_GEN: + dhParams = (DHParams *)param; + attrs = dhPubTemplate; + PK11_SETATTRS(attrs, CKA_PRIME, dhParams->prime.data, + dhParams->prime.len); attrs++; + PK11_SETATTRS(attrs, CKA_BASE, dhParams->base.data, + dhParams->base.len); attrs++; + pubTemplate = dhPubTemplate; + pubCount = dhPubCount; + keyType = dhKey; + test_mech.mechanism = CKM_DH_PKCS_DERIVE; + break; + default: + PORT_SetError( SEC_ERROR_BAD_KEY ); + return NULL; + } + + /* now query the slot to find out how "good" a key we can generate */ + if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_GetMechanismInfo(slot->slotID, + test_mech.mechanism,&mechanism_info); + if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot); + if ((crv != CKR_OK) || (mechanism_info.flags == 0)) { + /* must be old module... guess what it should be... */ + switch (test_mech.mechanism) { + case CKM_RSA_PKCS: + mechanism_info.flags = (CKF_SIGN | CKF_DECRYPT | + CKF_WRAP | CKF_VERIFY_RECOVER | CKF_ENCRYPT | CKF_WRAP);; + break; + case CKM_DSA: + mechanism_info.flags = CKF_SIGN | CKF_VERIFY; + break; + case CKM_DH_PKCS_DERIVE: + mechanism_info.flags = CKF_DERIVE; + break; + default: + break; + } + } + /* set the public key objects */ + PK11_SETATTRS(attrs, CKA_TOKEN, token ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); attrs++; + PK11_SETATTRS(attrs, CKA_DERIVE, + mechanism_info.flags & CKF_DERIVE ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); attrs++; + PK11_SETATTRS(attrs, CKA_WRAP, + mechanism_info.flags & CKF_WRAP ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); attrs++; + PK11_SETATTRS(attrs, CKA_VERIFY, + mechanism_info.flags & CKF_VERIFY ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); attrs++; + PK11_SETATTRS(attrs, CKA_VERIFY_RECOVER, + mechanism_info.flags & CKF_VERIFY_RECOVER ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); attrs++; + PK11_SETATTRS(attrs, CKA_ENCRYPT, + mechanism_info.flags & CKF_ENCRYPT? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); attrs++; + PK11_SETATTRS(privattrs, CKA_DERIVE, + mechanism_info.flags & CKF_DERIVE ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); privattrs++; + PK11_SETATTRS(privattrs, CKA_UNWRAP, + mechanism_info.flags & CKF_UNWRAP ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); privattrs++; + PK11_SETATTRS(privattrs, CKA_SIGN, + mechanism_info.flags & CKF_SIGN ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); privattrs++; + PK11_SETATTRS(privattrs, CKA_DECRYPT, + mechanism_info.flags & CKF_DECRYPT ? &cktrue : &ckfalse, + sizeof(CK_BBOOL)); privattrs++; + + if (token) { + session_handle = PK11_GetRWSession(slot); + haslock = PK11_RWSessionHasLock(slot,session_handle); + restore = PR_TRUE; + } else { + PK11_EnterSlotMonitor(slot); /* gross!! */ + session_handle = slot->session; + restore = PR_FALSE; + haslock = PR_TRUE; + } + + crv = PK11_GETTAB(slot)->C_GenerateKeyPair(session_handle, &mechanism, + pubTemplate,pubCount,privTemplate,privCount,&pubID,&privID); + + + if (crv != CKR_OK) { + if (restore) { + PK11_RestoreROSession(slot,session_handle); + } else PK11_ExitSlotMonitor(slot); + PORT_SetError( PK11_MapError(crv) ); + return NULL; + } + /* This locking code is dangerous and needs to be more thought + * out... the real problem is that we're holding the mutex open this long + */ + if (haslock) { PK11_ExitSlotMonitor(slot); } + + /* swap around the ID's for older PKCS #11 modules */ + keyClass = PK11_ReadULongAttribute(slot,pubID,CKA_CLASS); + if (keyClass != CKO_PUBLIC_KEY) { + CK_OBJECT_HANDLE tmp = pubID; + pubID = privID; + privID = tmp; + } + + *pubKey = PK11_ExtractPublicKey(slot, keyType, pubID); + if (*pubKey == NULL) { + if (restore) { + /* we may have to restore the mutex so it get's exited properly + * in RestoreROSession */ + if (haslock) PK11_EnterSlotMonitor(slot); + PK11_RestoreROSession(slot,session_handle); + } + PK11_DestroyObject(slot,pubID); + PK11_DestroyObject(slot,privID); + return NULL; + } + + /* set the ID to the public key so we can find it again */ + pubKeyIndex = NULL; + switch (type) { + case CKM_RSA_PKCS_KEY_PAIR_GEN: + pubKeyIndex = &(*pubKey)->u.rsa.modulus; + break; + case CKM_DSA_KEY_PAIR_GEN: + pubKeyIndex = &(*pubKey)->u.dsa.publicValue; + break; + case CKM_DH_PKCS_KEY_PAIR_GEN: + pubKeyIndex = &(*pubKey)->u.dh.publicValue; + break; + } + PORT_Assert(pubKeyIndex != NULL); + + cka_id = PK11_MakeIDFromPubKey(pubKeyIndex); + pubIsToken = (PRBool)PK11_HasAttributeSet(slot,pubID, CKA_TOKEN); + + PK11_SETATTRS(&setTemplate, CKA_ID, cka_id->data, cka_id->len); + + if (haslock) { PK11_EnterSlotMonitor(slot); } + crv = PK11_GETTAB(slot)->C_SetAttributeValue(session_handle, privID, + &setTemplate, 1); + + if (crv == CKR_OK && pubIsToken) { + crv = PK11_GETTAB(slot)->C_SetAttributeValue(session_handle, pubID, + &setTemplate, 1); + } + + + if (restore) { + PK11_RestoreROSession(slot,session_handle); + } else { + PK11_ExitSlotMonitor(slot); + } + SECITEM_FreeItem(cka_id,PR_TRUE); + + + if (crv != CKR_OK) { + PK11_DestroyObject(slot,pubID); + PK11_DestroyObject(slot,privID); + PORT_SetError( PK11_MapError(crv) ); + *pubKey = NULL; + return NULL; + } + + privKey = PK11_MakePrivKey(slot,keyType,(PRBool)!token,privID,wincx); + if (privKey == NULL) { + SECKEY_DestroyPublicKey(*pubKey); + PK11_DestroyObject(slot,privID); + *pubKey = NULL; + return NULL; /* due to pairwise consistency check */ + } + + /* Perform PKCS #11 pairwise consistency check. */ + rv = pk11_PairwiseConsistencyCheck( *pubKey, privKey, &test_mech, wincx ); + if( rv != SECSuccess ) { + SECKEY_DestroyPublicKey( *pubKey ); + SECKEY_DestroyPrivateKey( privKey ); + *pubKey = NULL; + privKey = NULL; + return NULL; + } + + return privKey; +} + +/* + * This function does a straight public key wrap (which only RSA can do). + * Use PK11_PubGenKey and PK11_WrapSymKey to implement the FORTEZZA and + * Diffie-Hellman Ciphers. */ +SECStatus +PK11_PubWrapSymKey(CK_MECHANISM_TYPE type, SECKEYPublicKey *pubKey, + PK11SymKey *symKey, SECItem *wrappedKey) +{ + PK11SlotInfo *slot; + CK_ULONG len = wrappedKey->len; + PK11SymKey *newKey = NULL; + CK_OBJECT_HANDLE id; + CK_MECHANISM mechanism; + PRBool owner = PR_TRUE; + CK_SESSION_HANDLE session; + CK_RV crv; + + /* if this slot doesn't support the mechanism, go to a slot that does */ + newKey = pk11_ForceSlot(symKey,type,CKA_ENCRYPT); + if (newKey != NULL) { + symKey = newKey; + } + + if ((symKey == NULL) || (symKey->slot == NULL)) { + PORT_SetError( SEC_ERROR_NO_MODULE ); + return SECFailure; + } + + slot = symKey->slot; + mechanism.mechanism = pk11_mapWrapKeyType(pubKey->keyType); + mechanism.pParameter = NULL; + mechanism.ulParameterLen = 0; + + id = PK11_ImportPublicKey(slot,pubKey,PR_FALSE); + + session = pk11_GetNewSession(slot,&owner); + if (!owner || !(slot->isThreadSafe)) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_WrapKey(session,&mechanism, + id,symKey->objectID,wrappedKey->data,&len); + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + if (newKey) { + PK11_FreeSymKey(newKey); + } + + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + wrappedKey->len = len; + return SECSuccess; +} + +/* + * this little function uses the Encrypt function to wrap a key, just in + * case we have problems with the wrap implementation for a token. + */ +static SECStatus +pk11_HandWrap(PK11SymKey *wrappingKey, SECItem *param, CK_MECHANISM_TYPE type, + SECItem *inKey, SECItem *outKey) +{ + PK11SlotInfo *slot; + CK_ULONG len; + SECItem *data; + CK_MECHANISM mech; + PRBool owner = PR_TRUE; + CK_SESSION_HANDLE session; + CK_RV crv; + + slot = wrappingKey->slot; + /* use NULL IV's for wrapping */ + mech.mechanism = type; + if (param) { + mech.pParameter = param->data; + mech.ulParameterLen = param->len; + } else { + mech.pParameter = NULL; + mech.ulParameterLen = 0; + } + session = pk11_GetNewSession(slot,&owner); + if (!owner || !(slot->isThreadSafe)) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_EncryptInit(session,&mech, + wrappingKey->objectID); + if (crv != CKR_OK) { + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + + /* keys are almost always aligned, but if we get this far, + * we've gone above and beyond anyway... */ + data = PK11_BlockData(inKey,PK11_GetBlockSize(type,param)); + if (data == NULL) { + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + len = outKey->len; + crv = PK11_GETTAB(slot)->C_Encrypt(session,data->data,data->len, + outKey->data, &len); + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + SECITEM_FreeItem(data,PR_TRUE); + outKey->len = len; + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + return SECSuccess; +} + +/* + * This function does a symetric based wrap. + */ +SECStatus +PK11_WrapSymKey(CK_MECHANISM_TYPE type, SECItem *param, + PK11SymKey *wrappingKey, PK11SymKey *symKey, SECItem *wrappedKey) +{ + PK11SlotInfo *slot; + CK_ULONG len = wrappedKey->len; + PK11SymKey *newKey = NULL; + SECItem *param_save = NULL; + CK_MECHANISM mechanism; + PRBool owner = PR_TRUE; + CK_SESSION_HANDLE session; + CK_RV crv; + SECStatus rv; + + /* if this slot doesn't support the mechanism, go to a slot that does */ + /* Force symKey and wrappingKey into the same slot */ + if ((wrappingKey->slot == NULL) || (symKey->slot != wrappingKey->slot)) { + /* first try copying the wrapping Key to the symKey slot */ + if (symKey->slot && PK11_DoesMechanism(symKey->slot,type)) { + newKey = pk11_CopyToSlot(symKey->slot,type,CKA_WRAP,wrappingKey); + } + /* Nope, try it the other way */ + if (newKey == NULL) { + if (wrappingKey->slot) { + newKey = pk11_CopyToSlot(wrappingKey->slot, + symKey->type, CKA_ENCRYPT, symKey); + } + /* just not playing... one last thing, can we get symKey's data? + * If it's possible, we it should already be in the + * symKey->data.data pointer because pk11_CopyToSlot would have + * tried to put it there. */ + if (newKey == NULL) { + /* Can't get symKey's data: Game Over */ + if (symKey->data.data == NULL) { + PORT_SetError( SEC_ERROR_NO_MODULE ); + return SECFailure; + } + if (param == NULL) { + param_save = param = PK11_ParamFromIV(type,NULL); + } + rv = pk11_HandWrap(wrappingKey, param, type, + &symKey->data,wrappedKey); + if (param_save) SECITEM_FreeItem(param_save,PR_TRUE); + return rv; + } + /* we successfully moved the sym Key */ + symKey = newKey; + } else { + /* we successfully moved the wrapping Key */ + wrappingKey = newKey; + } + } + + /* at this point both keys are in the same token */ + slot = wrappingKey->slot; + mechanism.mechanism = type; + /* use NULL IV's for wrapping */ + if (param == NULL) { + param_save = param = PK11_ParamFromIV(type,NULL); + } + if (param) { + mechanism.pParameter = param->data; + mechanism.ulParameterLen = param->len; + } else { + mechanism.pParameter = NULL; + mechanism.ulParameterLen = 0; + } + + len = wrappedKey->len; + + session = pk11_GetNewSession(slot,&owner); + if (!owner || !(slot->isThreadSafe)) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_WrapKey(session, &mechanism, + wrappingKey->objectID, symKey->objectID, + wrappedKey->data, &len); + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + rv = SECSuccess; + if (crv != CKR_OK) { + /* can't wrap it? try hand wrapping it... */ + do { + if (symKey->data.data == NULL) { + rv = PK11_ExtractKeyValue(symKey); + if (rv != SECSuccess) break; + } + rv = pk11_HandWrap(wrappingKey, param, type, &symKey->data, + wrappedKey); + } while (PR_FALSE); + } else { + wrappedKey->len = len; + } + if (newKey) PK11_FreeSymKey(newKey); + if (param_save) SECITEM_FreeItem(param_save,PR_TRUE); + return rv; +} + +/* + * This Generates a new key based on a symetricKey + */ +PK11SymKey * +PK11_Derive( PK11SymKey *baseKey, CK_MECHANISM_TYPE derive, SECItem *param, + CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, + int keySize) +{ + return pk11_DeriveWithTemplate(baseKey, derive, param, target, operation, + keySize, NULL, 0); +} + +#define MAX_TEMPL_ATTRS 16 /* maximum attributes in template */ + +/* This mask includes all CK_FLAGs with an equivalent CKA_ attribute. */ +#define CKF_KEY_OPERATION_FLAGS 0x000e7b00UL + +static unsigned int +pk11_FlagsToAttributes(CK_FLAGS flags, CK_ATTRIBUTE *attrs, CK_BBOOL *ckTrue) +{ + + const static CK_ATTRIBUTE_TYPE attrTypes[12] = { + CKA_ENCRYPT, CKA_DECRYPT, 0 /* DIGEST */, CKA_SIGN, + CKA_SIGN_RECOVER, CKA_VERIFY, CKA_VERIFY_RECOVER, 0 /* GEN */, + 0 /* GEN PAIR */, CKA_WRAP, CKA_UNWRAP, CKA_DERIVE + }; + + const CK_ATTRIBUTE_TYPE *pType = attrTypes; + CK_ATTRIBUTE *attr = attrs; + CK_FLAGS test = CKF_ENCRYPT; + + + PR_ASSERT(!(flags & ~CKF_KEY_OPERATION_FLAGS)); + flags &= CKF_KEY_OPERATION_FLAGS; + + for (; flags && test <= CKF_DERIVE; test <<= 1, ++pType) { + if (test & flags) { + flags ^= test; + PK11_SETATTRS(attr, *pType, ckTrue, sizeof *ckTrue); + ++attr; + } + } + return (attr - attrs); +} + +PK11SymKey * +PK11_DeriveWithFlags( PK11SymKey *baseKey, CK_MECHANISM_TYPE derive, + SECItem *param, CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, + int keySize, CK_FLAGS flags) +{ + CK_BBOOL ckTrue = CK_TRUE; + CK_ATTRIBUTE keyTemplate[MAX_TEMPL_ATTRS]; + unsigned int templateCount; + + templateCount = pk11_FlagsToAttributes(flags, keyTemplate, &ckTrue); + return pk11_DeriveWithTemplate(baseKey, derive, param, target, operation, + keySize, keyTemplate, templateCount); +} + +static PRBool +pk11_FindAttrInTemplate(CK_ATTRIBUTE * attr, + unsigned int numAttrs, + CK_ATTRIBUTE_TYPE target) +{ + for (; numAttrs > 0; ++attr, --numAttrs) { + if (attr->type == target) + return PR_TRUE; + } + return PR_FALSE; +} + +static PK11SymKey * +pk11_DeriveWithTemplate( PK11SymKey *baseKey, CK_MECHANISM_TYPE derive, + SECItem *param, CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, + int keySize, CK_ATTRIBUTE *userAttr, unsigned int numAttrs) +{ + PK11SlotInfo * slot = baseKey->slot; + PK11SymKey * symKey; + PK11SymKey * newBaseKey = NULL; + CK_BBOOL cktrue = CK_TRUE; + CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; + CK_ULONG valueLen = 0; + CK_MECHANISM mechanism; + CK_RV crv; + CK_ATTRIBUTE keyTemplate[MAX_TEMPL_ATTRS]; + CK_ATTRIBUTE * attrs = keyTemplate; + unsigned int templateCount; + + if (numAttrs > MAX_TEMPL_ATTRS) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + /* first copy caller attributes in. */ + for (templateCount = 0; templateCount < numAttrs; ++templateCount) { + *attrs++ = *userAttr++; + } + + /* We only add the following attributes to the template if the caller + ** didn't already supply them. + */ + if (!pk11_FindAttrInTemplate(keyTemplate, numAttrs, CKA_CLASS)) { + PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof keyClass); + attrs++; + } + if (!pk11_FindAttrInTemplate(keyTemplate, numAttrs, CKA_KEY_TYPE)) { + keyType = PK11_GetKeyType(target, keySize); + PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof keyType ); + attrs++; + } + if (keySize > 0 && + !pk11_FindAttrInTemplate(keyTemplate, numAttrs, CKA_VALUE_LEN)) { + valueLen = (CK_ULONG)keySize; + PK11_SETATTRS(attrs, CKA_VALUE_LEN, &valueLen, sizeof valueLen); + attrs++; + } + if (!pk11_FindAttrInTemplate(keyTemplate, numAttrs, operation)) { + PK11_SETATTRS(attrs, operation, &cktrue, sizeof cktrue); attrs++; + } + + templateCount = attrs - keyTemplate; + PR_ASSERT(templateCount <= MAX_TEMPL_ATTRS); + + /* move the key to a slot that can do the function */ + if (!PK11_DoesMechanism(slot,derive)) { + /* get a new base key & slot */ + PK11SlotInfo *newSlot = PK11_GetBestSlot(derive, baseKey->cx); + + if (newSlot == NULL) return NULL; + + newBaseKey = pk11_CopyToSlot (newSlot, derive, CKA_DERIVE, + baseKey); + PK11_FreeSlot(newSlot); + if (newBaseKey == NULL) return NULL; + baseKey = newBaseKey; + slot = baseKey->slot; + } + + + /* get our key Structure */ + symKey = PK11_CreateSymKey(slot,target,baseKey->cx); + if (symKey == NULL) { + return NULL; + } + + symKey->size = keySize; + + mechanism.mechanism = derive; + if (param) { + mechanism.pParameter = param->data; + mechanism.ulParameterLen = param->len; + } else { + mechanism.pParameter = NULL; + mechanism.ulParameterLen = 0; + } + symKey->origin=PK11_OriginDerive; + + pk11_EnterKeyMonitor(symKey); + crv = PK11_GETTAB(slot)->C_DeriveKey(symKey->session, &mechanism, + baseKey->objectID, keyTemplate, templateCount, &symKey->objectID); + pk11_ExitKeyMonitor(symKey); + + if (newBaseKey) PK11_FreeSymKey(newBaseKey); + if (crv != CKR_OK) { + PK11_FreeSymKey(symKey); + return NULL; + } + return symKey; +} + +/* build a public KEA key from the public value */ +SECKEYPublicKey * +PK11_MakeKEAPubKey(unsigned char *keyData,int length) +{ + SECKEYPublicKey *pubk; + SECItem pkData; + SECStatus rv; + PRArenaPool *arena; + + pkData.data = keyData; + pkData.len = length; + + arena = PORT_NewArena (DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) + return NULL; + + pubk = (SECKEYPublicKey *) PORT_ArenaZAlloc(arena, sizeof(SECKEYPublicKey)); + if (pubk == NULL) { + PORT_FreeArena (arena, PR_FALSE); + return NULL; + } + + pubk->arena = arena; + pubk->pkcs11Slot = 0; + pubk->pkcs11ID = CK_INVALID_KEY; + pubk->keyType = fortezzaKey; + rv = SECITEM_CopyItem(arena, &pubk->u.fortezza.KEAKey, &pkData); + if (rv != SECSuccess) { + PORT_FreeArena (arena, PR_FALSE); + return NULL; + } + return pubk; +} + + +/* + * This Generates a wrapping key based on a privateKey, publicKey, and two + * random numbers. For Mail usage RandomB should be NULL. In the Sender's + * case RandomA is generate, outherwize it is passed. + */ +static unsigned char *rb_email = NULL; + +PK11SymKey * +PK11_PubDerive(SECKEYPrivateKey *privKey, SECKEYPublicKey *pubKey, + PRBool isSender, SECItem *randomA, SECItem *randomB, + CK_MECHANISM_TYPE derive, CK_MECHANISM_TYPE target, + CK_ATTRIBUTE_TYPE operation, int keySize,void *wincx) +{ + PK11SlotInfo *slot = privKey->pkcs11Slot; + CK_MECHANISM mechanism; + PK11SymKey *symKey; + CK_RV crv; + + + if (rb_email == NULL) { + rb_email = PORT_ZAlloc(128); + if (rb_email == NULL) { + return NULL; + } + rb_email[127] = 1; + } + + /* get our key Structure */ + symKey = PK11_CreateSymKey(slot,target,wincx); + if (symKey == NULL) { + return NULL; + } + + symKey->origin = PK11_OriginDerive; + + switch (privKey->keyType) { + case rsaKey: + case nullKey: + PORT_SetError(SEC_ERROR_BAD_KEY); + break; + /* case keaKey: */ + case dsaKey: + case fortezzaKey: + { + CK_KEA_DERIVE_PARAMS param; + param.isSender = (CK_BBOOL) isSender; + param.ulRandomLen = randomA->len; + param.pRandomA = randomA->data; + param.pRandomB = rb_email; + if (randomB) + param.pRandomB = randomB->data; + if (pubKey->keyType == fortezzaKey) { + param.ulPublicDataLen = pubKey->u.fortezza.KEAKey.len; + param.pPublicData = pubKey->u.fortezza.KEAKey.data; + } else { + /* assert type == keaKey */ + /* XXX change to match key key types */ + param.ulPublicDataLen = pubKey->u.fortezza.KEAKey.len; + param.pPublicData = pubKey->u.fortezza.KEAKey.data; + } + + mechanism.mechanism = derive; + mechanism.pParameter = ¶m; + mechanism.ulParameterLen = sizeof(param); + + /* get a new symKey structure */ + pk11_EnterKeyMonitor(symKey); + crv=PK11_GETTAB(slot)->C_DeriveKey(symKey->session, &mechanism, + privKey->pkcs11ID, NULL, 0, &symKey->objectID); + pk11_ExitKeyMonitor(symKey); + if (crv == CKR_OK) return symKey; + PORT_SetError( PK11_MapError(crv) ); + } + break; + case dhKey: + { + CK_BBOOL cktrue = CK_TRUE; + CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; + CK_ULONG key_size = 0; + CK_ATTRIBUTE keyTemplate[4]; + int templateCount; + CK_ATTRIBUTE *attrs = keyTemplate; + + if (pubKey->keyType != dhKey) { + PORT_SetError(SEC_ERROR_BAD_KEY); + break; + } + + PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass)); + attrs++; + PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType)); + attrs++; + PK11_SETATTRS(attrs, operation, &cktrue, 1); attrs++; + PK11_SETATTRS(attrs, CKA_VALUE_LEN, &key_size, sizeof(key_size)); + attrs++; + templateCount = attrs - keyTemplate; + PR_ASSERT(templateCount <= sizeof(keyTemplate)/sizeof(CK_ATTRIBUTE)); + + keyType = PK11_GetKeyType(target,keySize); + key_size = keySize; + symKey->size = keySize; + if (key_size == 0) templateCount--; + + mechanism.mechanism = derive; + + /* we can undefine these when we define diffie-helman keys */ + mechanism.pParameter = pubKey->u.dh.publicValue.data; + mechanism.ulParameterLen = pubKey->u.dh.publicValue.len; + + pk11_EnterKeyMonitor(symKey); + crv = PK11_GETTAB(slot)->C_DeriveKey(symKey->session, &mechanism, + privKey->pkcs11ID, keyTemplate, templateCount, &symKey->objectID); + pk11_ExitKeyMonitor(symKey); + if (crv == CKR_OK) return symKey; + PORT_SetError( PK11_MapError(crv) ); + } + break; + } + + PK11_FreeSymKey(symKey); + return NULL; +} + +/* + * this little function uses the Decrypt function to unwrap a key, just in + * case we are having problem with unwrap. NOTE: The key size may + * not be preserved properly for some algorithms! + */ +static PK11SymKey * +pk11_HandUnwrap(PK11SlotInfo *slot, CK_OBJECT_HANDLE wrappingKey, + CK_MECHANISM *mech, SECItem *inKey, CK_MECHANISM_TYPE target, + CK_ATTRIBUTE *keyTemplate, unsigned int templateCount, + int key_size, void * wincx, CK_RV *crvp) +{ + CK_ULONG len; + SECItem outKey; + PK11SymKey *symKey; + CK_RV crv; + PRBool owner = PR_TRUE; + PRBool bool = PR_TRUE; + CK_SESSION_HANDLE session; + + /* remove any VALUE_LEN parameters */ + if (keyTemplate[templateCount-1].type == CKA_VALUE_LEN) { + templateCount--; + } + + /* keys are almost always aligned, but if we get this far, + * we've gone above and beyond anyway... */ + outKey.data = (unsigned char*)PORT_Alloc(inKey->len); + if (outKey.data == NULL) { + PORT_SetError( SEC_ERROR_NO_MEMORY ); + if (crvp) *crvp = CKR_HOST_MEMORY; + return NULL; + } + len = inKey->len; + + /* use NULL IV's for wrapping */ + session = pk11_GetNewSession(slot,&owner); + if (!owner || !(slot->isThreadSafe)) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_DecryptInit(session,mech,wrappingKey); + if (crv != CKR_OK) { + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + PORT_Free(outKey.data); + PORT_SetError( PK11_MapError(crv) ); + if (crvp) *crvp =crv; + return NULL; + } + crv = PK11_GETTAB(slot)->C_Decrypt(session,inKey->data,inKey->len, + outKey.data, &len); + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + if (crv != CKR_OK) { + PORT_Free(outKey.data); + PORT_SetError( PK11_MapError(crv) ); + if (crvp) *crvp =crv; + return NULL; + } + + outKey.len = (key_size == 0) ? len : key_size; + + if (PK11_DoesMechanism(slot,target)) { + symKey = pk11_ImportSymKeyWithTempl(slot, target, PK11_OriginUnwrap, + keyTemplate, templateCount, + &outKey, wincx); + } else { + slot = PK11_GetBestSlot(target,wincx); + if (slot == NULL) { + PORT_SetError( SEC_ERROR_NO_MODULE ); + PORT_Free(outKey.data); + if (crvp) *crvp = CKR_DEVICE_ERROR; + return NULL; + } + symKey = pk11_ImportSymKeyWithTempl(slot, target, PK11_OriginUnwrap, + keyTemplate, templateCount, + &outKey, wincx); + PK11_FreeSlot(slot); + } + PORT_Free(outKey.data); + + if (crvp) *crvp = symKey? CKR_OK : CKR_DEVICE_ERROR; + return symKey; +} + +/* + * The wrap/unwrap function is pretty much the same for private and + * public keys. It's just getting the Object ID and slot right. This is + * the combined unwrap function. + */ +static PK11SymKey * +pk11_AnyUnwrapKey(PK11SlotInfo *slot, CK_OBJECT_HANDLE wrappingKey, + CK_MECHANISM_TYPE wrapType, SECItem *param, SECItem *wrappedKey, + CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, int keySize, + void *wincx, CK_ATTRIBUTE *userAttr, unsigned int numAttrs) +{ + PK11SymKey * symKey; + SECItem * param_free = NULL; + CK_BBOOL ckfalse = CK_FALSE; + CK_BBOOL cktrue = CK_TRUE; + CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; + CK_ULONG valueLen = 0; + CK_MECHANISM mechanism; + CK_RV crv; + CK_MECHANISM_INFO mechanism_info; + CK_ATTRIBUTE keyTemplate[MAX_TEMPL_ATTRS]; + CK_ATTRIBUTE * attrs = keyTemplate; + unsigned int templateCount; + + if (numAttrs > MAX_TEMPL_ATTRS) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + /* first copy caller attributes in. */ + for (templateCount = 0; templateCount < numAttrs; ++templateCount) { + *attrs++ = *userAttr++; + } + + /* We only add the following attributes to the template if the caller + ** didn't already supply them. + */ + if (!pk11_FindAttrInTemplate(keyTemplate, numAttrs, CKA_CLASS)) { + PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof keyClass); + attrs++; + } + if (!pk11_FindAttrInTemplate(keyTemplate, numAttrs, CKA_KEY_TYPE)) { + keyType = PK11_GetKeyType(target, keySize); + PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof keyType ); + attrs++; + } + if (!pk11_FindAttrInTemplate(keyTemplate, numAttrs, operation)) { + PK11_SETATTRS(attrs, operation, &cktrue, 1); attrs++; + } + + /* + * must be last in case we need to use this template to import the key + */ + if (keySize > 0 && + !pk11_FindAttrInTemplate(keyTemplate, numAttrs, CKA_VALUE_LEN)) { + valueLen = (CK_ULONG)keySize; + PK11_SETATTRS(attrs, CKA_VALUE_LEN, &valueLen, sizeof valueLen); + attrs++; + } + + templateCount = attrs - keyTemplate; + PR_ASSERT(templateCount <= sizeof(keyTemplate)/sizeof(CK_ATTRIBUTE)); + + + /* find out if we can do wrap directly. Because the RSA case if *very* + * common, cache the results for it. */ + if ((wrapType == CKM_RSA_PKCS) && (slot->hasRSAInfo)) { + mechanism_info.flags = slot->RSAInfoFlags; + } else { + if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_GetMechanismInfo(slot->slotID,wrapType, + &mechanism_info); + if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot); + if (crv != CKR_OK) { + mechanism_info.flags = 0; + } + if (wrapType == CKM_RSA_PKCS) { + slot->RSAInfoFlags = mechanism_info.flags; + slot->hasRSAInfo = PR_TRUE; + } + } + + /* initialize the mechanism structure */ + mechanism.mechanism = wrapType; + /* use NULL IV's for wrapping */ + if (param == NULL) param = param_free = PK11_ParamFromIV(wrapType,NULL); + if (param) { + mechanism.pParameter = param->data; + mechanism.ulParameterLen = param->len; + } else { + mechanism.pParameter = NULL; + mechanism.ulParameterLen = 0; + } + + if ((mechanism_info.flags & CKF_DECRYPT) + && !PK11_DoesMechanism(slot,target)) { + symKey = pk11_HandUnwrap(slot, wrappingKey, &mechanism, wrappedKey, + target, keyTemplate, templateCount, keySize, + wincx, &crv); + if (symKey) { + if (param_free) SECITEM_FreeItem(param_free,PR_TRUE); + return symKey; + } + /* + * if the RSA OP simply failed, don't try to unwrap again + * with this module. + */ + if (crv == CKR_DEVICE_ERROR){ + return NULL; + } + /* fall through, maybe they incorrectly set CKF_DECRYPT */ + } + + /* get our key Structure */ + symKey = PK11_CreateSymKey(slot,target,wincx); + if (symKey == NULL) { + if (param_free) SECITEM_FreeItem(param_free,PR_TRUE); + return NULL; + } + + symKey->size = keySize; + symKey->origin = PK11_OriginUnwrap; + + pk11_EnterKeyMonitor(symKey); + crv = PK11_GETTAB(slot)->C_UnwrapKey(symKey->session,&mechanism,wrappingKey, + wrappedKey->data, wrappedKey->len, keyTemplate, templateCount, + &symKey->objectID); + pk11_ExitKeyMonitor(symKey); + if (param_free) SECITEM_FreeItem(param_free,PR_TRUE); + if ((crv != CKR_OK) && (crv != CKR_DEVICE_ERROR)) { + /* try hand Unwrapping */ + PK11_FreeSymKey(symKey); + symKey = pk11_HandUnwrap(slot, wrappingKey, &mechanism, wrappedKey, + target, keyTemplate, templateCount, keySize, + wincx, NULL); + } + + return symKey; +} + +/* use a symetric key to unwrap another symetric key */ +PK11SymKey * +PK11_UnwrapSymKey( PK11SymKey *wrappingKey, CK_MECHANISM_TYPE wrapType, + SECItem *param, SECItem *wrappedKey, + CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, + int keySize) +{ + return pk11_AnyUnwrapKey(wrappingKey->slot, wrappingKey->objectID, + wrapType, param, wrappedKey, target, operation, keySize, + wrappingKey->cx, NULL, 0); +} + +/* use a symetric key to unwrap another symetric key */ +PK11SymKey * +PK11_UnwrapSymKeyWithFlags(PK11SymKey *wrappingKey, CK_MECHANISM_TYPE wrapType, + SECItem *param, SECItem *wrappedKey, + CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, + int keySize, CK_FLAGS flags) +{ + CK_BBOOL ckTrue = CK_TRUE; + CK_ATTRIBUTE keyTemplate[MAX_TEMPL_ATTRS]; + unsigned int templateCount; + + templateCount = pk11_FlagsToAttributes(flags, keyTemplate, &ckTrue); + return pk11_AnyUnwrapKey(wrappingKey->slot, wrappingKey->objectID, + wrapType, param, wrappedKey, target, operation, keySize, + wrappingKey->cx, keyTemplate, templateCount); +} + + +/* unwrap a symetric key with a private key. */ +PK11SymKey * +PK11_PubUnwrapSymKey(SECKEYPrivateKey *wrappingKey, SECItem *wrappedKey, + CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, int keySize) +{ + CK_MECHANISM_TYPE wrapType = pk11_mapWrapKeyType(wrappingKey->keyType); + + PK11_HandlePasswordCheck(wrappingKey->pkcs11Slot,wrappingKey->wincx); + + return pk11_AnyUnwrapKey(wrappingKey->pkcs11Slot, wrappingKey->pkcs11ID, + wrapType, NULL, wrappedKey, target, operation, keySize, + wrappingKey->wincx, NULL, 0); +} + +/* + * Recover the Signed data. We need this because our old verify can't + * figure out which hash algorithm to use until we decryptted this. + */ +SECStatus +PK11_VerifyRecover(SECKEYPublicKey *key, + SECItem *sig, SECItem *dsig, void *wincx) +{ + PK11SlotInfo *slot = key->pkcs11Slot; + CK_OBJECT_HANDLE id = key->pkcs11ID; + CK_MECHANISM mech = {0, NULL, 0 }; + PRBool owner = PR_TRUE; + CK_SESSION_HANDLE session; + CK_ULONG len; + CK_RV crv; + + mech.mechanism = pk11_mapSignKeyType(key->keyType); + + if (slot == NULL) { + slot = PK11_GetBestSlot(mech.mechanism,wincx); + if (slot == NULL) { + PORT_SetError( SEC_ERROR_NO_MODULE ); + return SECFailure; + } + id = PK11_ImportPublicKey(slot,key,PR_FALSE); + } + + session = pk11_GetNewSession(slot,&owner); + if (!owner || !(slot->isThreadSafe)) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_VerifyRecoverInit(session,&mech,id); + if (crv != CKR_OK) { + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + len = dsig->len; + crv = PK11_GETTAB(slot)->C_VerifyRecover(session,sig->data, + sig->len, dsig->data, &len); + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + dsig->len = len; + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + return SECSuccess; +} + +/* + * verify a signature from its hash. + */ +SECStatus +PK11_Verify(SECKEYPublicKey *key, SECItem *sig, SECItem *hash, void *wincx) +{ + PK11SlotInfo *slot = key->pkcs11Slot; + PK11SlotInfo *tmpslot = key->pkcs11Slot; + CK_OBJECT_HANDLE id = key->pkcs11ID; + CK_MECHANISM mech = {0, NULL, 0 }; + PRBool owner = PR_TRUE; + CK_SESSION_HANDLE session; + CK_RV crv; + + mech.mechanism = pk11_mapSignKeyType(key->keyType); + + if (slot == NULL) { + if (mech.mechanism == CKM_DSA) { + slot = PK11_GetInternalSlot(); /* use internal slot for + DSA verify */ + } else { + slot = PK11_GetBestSlot(mech.mechanism,wincx); + }; + + if (slot == NULL) { + PORT_SetError( SEC_ERROR_NO_MODULE ); + return SECFailure; + } + id = PK11_ImportPublicKey(slot,key,PR_FALSE); + + } + + session = pk11_GetNewSession(slot,&owner); + if (!owner || !(slot->isThreadSafe)) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_VerifyInit(session,&mech,id); + if (crv != CKR_OK) { + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + crv = PK11_GETTAB(slot)->C_Verify(session,hash->data, + hash->len, sig->data, sig->len); + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + return SECSuccess; +} + +/* + * sign a hash. The algorithm is determined by the key. + */ +SECStatus +PK11_Sign(SECKEYPrivateKey *key, SECItem *sig, SECItem *hash) +{ + PK11SlotInfo *slot = key->pkcs11Slot; + CK_MECHANISM mech = {0, NULL, 0 }; + PRBool owner = PR_TRUE; + CK_SESSION_HANDLE session; + CK_ULONG len; + CK_RV crv; + + mech.mechanism = pk11_mapSignKeyType(key->keyType); + + PK11_HandlePasswordCheck(slot, key->wincx); + + session = pk11_GetNewSession(slot,&owner); + if (!owner || !(slot->isThreadSafe)) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_SignInit(session,&mech,key->pkcs11ID); + if (crv != CKR_OK) { + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + len = sig->len; + crv = PK11_GETTAB(slot)->C_Sign(session,hash->data, + hash->len, sig->data, &len); + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + sig->len = len; + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + return SECSuccess; +} + +/* + * Now SSL 2.0 uses raw RSA stuff. These next to functions *must* use + * RSA keys, or they'll fail. We do the checks up front. If anyone comes + * up with a meaning for rawdecrypt for any other public key operation, + * then we need to move this check into some of PK11_PubDecrypt callers, + * (namely SSL 2.0). + */ +SECStatus +PK11_PubDecryptRaw(SECKEYPrivateKey *key, unsigned char *data, + unsigned *outLen, unsigned int maxLen, unsigned char *enc, + unsigned encLen) +{ + PK11SlotInfo *slot = key->pkcs11Slot; + CK_MECHANISM mech = {CKM_RSA_X_509, NULL, 0 }; + CK_ULONG out = maxLen; + PRBool owner = PR_TRUE; + CK_SESSION_HANDLE session; + CK_RV crv; + + if (key->keyType != rsaKey) { + PORT_SetError( SEC_ERROR_INVALID_KEY ); + return SECFailure; + } + + /* Why do we do a PK11_handle check here? for simple + * decryption? .. because the user may have asked for 'ask always' + * and this is a private key operation. In practice, thought, it's mute + * since only servers wind up using this function */ + PK11_HandlePasswordCheck(slot, key->wincx); + session = pk11_GetNewSession(slot,&owner); + if (!owner || !(slot->isThreadSafe)) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_DecryptInit(session,&mech,key->pkcs11ID); + if (crv != CKR_OK) { + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + crv = PK11_GETTAB(slot)->C_Decrypt(session,enc, encLen, + data, &out); + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + *outLen = out; + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + return SECSuccess; +} + +/* The encrypt version of the above function */ +SECStatus +PK11_PubEncryptRaw(SECKEYPublicKey *key, unsigned char *enc, + unsigned char *data, unsigned dataLen, void *wincx) +{ + PK11SlotInfo *slot; + CK_MECHANISM mech = {CKM_RSA_X_509, NULL, 0 }; + CK_OBJECT_HANDLE id; + CK_ULONG out = dataLen; + PRBool owner = PR_TRUE; + CK_SESSION_HANDLE session; + CK_RV crv; + + if (key->keyType != rsaKey) { + PORT_SetError( SEC_ERROR_BAD_KEY ); + return SECFailure; + } + + slot = PK11_GetBestSlot(mech.mechanism, wincx); + if (slot == NULL) { + PORT_SetError( SEC_ERROR_NO_MODULE ); + return SECFailure; + } + + id = PK11_ImportPublicKey(slot,key,PR_FALSE); + + session = pk11_GetNewSession(slot,&owner); + if (!owner || !(slot->isThreadSafe)) PK11_EnterSlotMonitor(slot); + crv = PK11_GETTAB(slot)->C_EncryptInit(session,&mech,id); + if (crv != CKR_OK) { + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + crv = PK11_GETTAB(slot)->C_Encrypt(session,data,dataLen,enc,&out); + if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot); + pk11_CloseSession(slot,session,owner); + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + return SECSuccess; +} + + +/********************************************************************** + * + * Now Deal with Crypto Contexts + * + **********************************************************************/ + +/* + * the monitors... + */ +void +PK11_EnterContextMonitor(PK11Context *cx) { + /* if we own the session and our slot is ThreadSafe, only monitor + * the Context */ + if ((cx->ownSession) && (cx->slot->isThreadSafe)) { + /* Should this use monitors instead? */ + PR_Lock(cx->sessionLock); + } else { + PK11_EnterSlotMonitor(cx->slot); + } +} + +void +PK11_ExitContextMonitor(PK11Context *cx) { + /* if we own the session and our slot is ThreadSafe, only monitor + * the Context */ + if ((cx->ownSession) && (cx->slot->isThreadSafe)) { + /* Should this use monitors instead? */ + PR_Unlock(cx->sessionLock); + } else { + PK11_ExitSlotMonitor(cx->slot); + } +} + +/* + * Free up a Cipher Context + */ +void +PK11_DestroyContext(PK11Context *context, PRBool freeit) +{ + pk11_CloseSession(context->slot,context->session,context->ownSession); + /* initialize the critical fields of the context */ + if (context->savedData != NULL ) PORT_Free(context->savedData); + if (context->key) PK11_FreeSymKey(context->key); + if (context->param) SECITEM_FreeItem(context->param, PR_TRUE); + if (context->sessionLock) PR_DestroyLock(context->sessionLock); + PK11_FreeSlot(context->slot); + if (freeit) PORT_Free(context); +} + +/* + * save the current context. Allocate Space if necessary. + */ +static void * +pk11_saveContextHelper(PK11Context *context, void *space, + unsigned long *savedLength, PRBool staticBuffer, PRBool recurse) +{ + CK_ULONG length; + CK_RV crv; + + if (staticBuffer) PORT_Assert(space != NULL); + + if (space == NULL) { + crv =PK11_GETTAB(context->slot)->C_GetOperationState(context->session, + NULL,&length); + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return NULL; + } + space = PORT_Alloc(length); + if (space == NULL) return NULL; + *savedLength = length; + } + crv = PK11_GETTAB(context->slot)->C_GetOperationState(context->session, + (CK_BYTE_PTR)space,savedLength); + if (!staticBuffer && !recurse && (crv == CKR_BUFFER_TOO_SMALL)) { + if (!staticBuffer) PORT_Free(space); + return pk11_saveContextHelper(context, NULL, + savedLength, PR_FALSE, PR_TRUE); + } + if (crv != CKR_OK) { + if (!staticBuffer) PORT_Free(space); + PORT_SetError( PK11_MapError(crv) ); + return NULL; + } + return space; +} + +void * +pk11_saveContext(PK11Context *context, void *space, unsigned long *savedLength) +{ + return pk11_saveContextHelper(context, space, + savedLength, PR_FALSE, PR_FALSE); +} + +/* + * restore the current context + */ +SECStatus +pk11_restoreContext(PK11Context *context,void *space, unsigned long savedLength) +{ + CK_RV crv; + CK_OBJECT_HANDLE objectID = (context->key) ? context->key->objectID: + CK_INVALID_KEY; + + PORT_Assert(space != NULL); + if (space == NULL) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + crv = PK11_GETTAB(context->slot)->C_SetOperationState(context->session, + (CK_BYTE_PTR)space, savedLength, objectID, 0); + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv)); + return SECFailure; + } + return SECSuccess; +} + +SECStatus pk11_Finalize(PK11Context *context); + +/* + * Context initialization. Used by all flavors of CreateContext + */ +static SECStatus +pk11_context_init(PK11Context *context, CK_MECHANISM *mech_info) +{ + CK_RV crv; + PK11SymKey *symKey = context->key; + SECStatus rv = SECSuccess; + + switch (context->operation) { + case CKA_ENCRYPT: + crv=PK11_GETTAB(context->slot)->C_EncryptInit(context->session, + mech_info, symKey->objectID); + break; + case CKA_DECRYPT: + if (context->fortezzaHack) { + CK_ULONG count = 0;; + /* generate the IV for fortezza */ + crv=PK11_GETTAB(context->slot)->C_EncryptInit(context->session, + mech_info, symKey->objectID); + if (crv != CKR_OK) break; + PK11_GETTAB(context->slot)->C_EncryptFinal(context->session, + NULL, &count); + } + crv=PK11_GETTAB(context->slot)->C_DecryptInit(context->session, + mech_info, symKey->objectID); + break; + case CKA_SIGN: + crv=PK11_GETTAB(context->slot)->C_SignInit(context->session, + mech_info, symKey->objectID); + break; + case CKA_VERIFY: + crv=PK11_GETTAB(context->slot)->C_SignInit(context->session, + mech_info, symKey->objectID); + break; + case CKA_DIGEST: + crv=PK11_GETTAB(context->slot)->C_DigestInit(context->session, + mech_info); + break; + default: + crv = CKR_OPERATION_NOT_INITIALIZED; + break; + } + + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + + /* + * handle session starvation case.. use our last session to multiplex + */ + if (!context->ownSession) { + context->savedData = pk11_saveContext(context,context->savedData, + &context->savedLength); + if (context->savedData == NULL) rv = SECFailure; + /* clear out out session for others to use */ + pk11_Finalize(context); + } + return rv; +} + + +/* + * Common Helper Function do come up with a new context. + */ +static PK11Context *pk11_CreateNewContextInSlot(CK_MECHANISM_TYPE type, + PK11SlotInfo *slot, CK_ATTRIBUTE_TYPE operation, PK11SymKey *symKey, + SECItem *param) +{ + CK_MECHANISM mech_info; + PK11Context *context; + SECStatus rv; + + context = (PK11Context *) PORT_Alloc(sizeof(PK11Context)); + if (context == NULL) { + return NULL; + } + + /* now deal with the fortezza hack... the fortezza hack is an attempt + * to get around the issue of the card not allowing you to do a FORTEZZA + * LoadIV/Encrypt, which was added because such a combination could be + * use to circumvent the key escrow system. Unfortunately SSL needs to + * do this kind of operation, so in SSL we do a loadIV (to verify it), + * Then GenerateIV, and through away the first 8 bytes on either side + * of the connection.*/ + context->fortezzaHack = PR_FALSE; + if (type == CKM_SKIPJACK_CBC64) { + if (symKey->origin == PK11_OriginFortezzaHack) { + context->fortezzaHack = PR_TRUE; + } + } + + /* initialize the critical fields of the context */ + context->operation = operation; + context->key = symKey ? PK11_ReferenceSymKey(symKey) : NULL; + context->slot = PK11_ReferenceSlot(slot); + context->session = pk11_GetNewSession(slot,&context->ownSession); + context->cx = symKey ? symKey->cx : NULL; + /* get our session */ + context->savedData = NULL; + + /* save the parameters so that some digesting stuff can do multiple + * begins on a single context */ + context->type = type; + context->param = SECITEM_DupItem(param); + context->init = PR_FALSE; + context->sessionLock = PR_NewLock(); + if ((context->param == NULL) || (context->sessionLock == NULL)) { + PK11_DestroyContext(context,PR_TRUE); + return NULL; + } + + mech_info.mechanism = type; + mech_info.pParameter = param->data; + mech_info.ulParameterLen = param->len; + PK11_EnterContextMonitor(context); + rv = pk11_context_init(context,&mech_info); + PK11_ExitContextMonitor(context); + + if (rv != SECSuccess) { + PK11_DestroyContext(context,PR_TRUE); + return NULL; + } + context->init = PR_TRUE; + return context; +} + + +/* + * put together the various PK11_Create_Context calls used by different + * parts of libsec. + */ +PK11Context * +PK11_CreateContextByRawKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, + PK11Origin origin, CK_ATTRIBUTE_TYPE operation, SECItem *key, + SECItem *param, void *wincx) +{ + PK11SymKey *symKey; + PK11Context *context; + + /* first get a slot */ + if (slot == NULL) { + slot = PK11_GetBestSlot(type,wincx); + if (slot == NULL) { + PORT_SetError( SEC_ERROR_NO_MODULE ); + return NULL; + } + } else { + PK11_ReferenceSlot(slot); + } + + /* now import the key */ + symKey = PK11_ImportSymKey(slot, type, origin, operation, key, wincx); + if (symKey == NULL) return NULL; + + context = PK11_CreateContextBySymKey(type, operation, symKey, param); + + PK11_FreeSymKey(symKey); + PK11_FreeSlot(slot); + + return context; +} + + +/* + * Create a context from a key. We really should make sure we aren't using + * the same key in multiple session! + */ +PK11Context * +PK11_CreateContextBySymKey(CK_MECHANISM_TYPE type,CK_ATTRIBUTE_TYPE operation, + PK11SymKey *symKey, SECItem *param) +{ + PK11SymKey *newKey; + PK11Context *context; + + /* if this slot doesn't support the mechanism, go to a slot that does */ + newKey = pk11_ForceSlot(symKey,type,operation); + if (newKey == NULL) { + PK11_ReferenceSymKey(symKey); + } else { + symKey = newKey; + } + + + /* Context Adopts the symKey.... */ + context = pk11_CreateNewContextInSlot(type, symKey->slot, operation, symKey, + param); + PK11_FreeSymKey(symKey); + return context; +} + +/* + * Digest contexts don't need keys, but the do need to find a slot. + * Macing should use PK11_CreateContextBySymKey. + */ +PK11Context * +PK11_CreateDigestContext(SECOidTag hashAlg) +{ + /* digesting has to work without authentication to the slot */ + CK_MECHANISM_TYPE type; + PK11SlotInfo *slot; + PK11Context *context; + SECItem param; + + type = PK11_AlgtagToMechanism(hashAlg); + slot = PK11_GetBestSlot(type, NULL); + if (slot == NULL) { + PORT_SetError( SEC_ERROR_NO_MODULE ); + return NULL; + } + + /* maybe should really be PK11_GenerateNewParam?? */ + param.data = NULL; + param.len = 0; + + context = pk11_CreateNewContextInSlot(type, slot, CKA_DIGEST, NULL, ¶m); + PK11_FreeSlot(slot); + return context; +} + +/* + * create a new context which is the clone of the state of old context. + */ +PK11Context * PK11_CloneContext(PK11Context *old) +{ + PK11Context *newcx; + PRBool needFree = PR_FALSE; + SECStatus rv = SECSuccess; + void *data; + unsigned long len; + + newcx = pk11_CreateNewContextInSlot(old->type, old->slot, old->operation, + old->key, old->param); + if (newcx == NULL) return NULL; + + /* now clone the save state. First we need to find the save state + * of the old session. If the old context owns it's session, + * the state needs to be saved, otherwise the state is in saveData. */ + if (old->ownSession) { + PK11_EnterContextMonitor(old); + data=pk11_saveContext(old,NULL,&len); + PK11_ExitContextMonitor(old); + needFree = PR_TRUE; + } else { + data = old->savedData; + len = old->savedLength; + } + + if (data == NULL) { + PK11_DestroyContext(newcx,PR_TRUE); + return NULL; + } + + /* now copy that state into our new context. Again we have different + * work if the new context owns it's own session. If it does, we + * restore the state gathered above. If it doesn't, we copy the + * saveData pointer... */ + if (newcx->ownSession) { + PK11_EnterContextMonitor(newcx); + rv = pk11_restoreContext(newcx,data,len); + PK11_ExitContextMonitor(newcx); + } else { + PORT_Assert(newcx->savedData != NULL); + if ((newcx->savedData == NULL) || (newcx->savedLength < len)) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + rv = SECFailure; + } else { + PORT_Memcpy(newcx->savedData,data,len); + newcx->savedLength = len; + } + } + + if (needFree) PORT_Free(data); + + if (rv != SECSuccess) { + PK11_DestroyContext(newcx,PR_TRUE); + return NULL; + } + return newcx; +} + +/* + * save the current context state into a variable. Required to make FORTEZZA + * work. + */ +SECStatus +PK11_SaveContext(PK11Context *cx,unsigned char *save,int *len, int saveLength) +{ + unsigned char * data = NULL; + CK_ULONG length = saveLength; + + if (cx->ownSession) { + PK11_EnterContextMonitor(cx); + data = (unsigned char*)pk11_saveContextHelper(cx,save,&length, + PR_FALSE,PR_FALSE); + PK11_ExitContextMonitor(cx); + if (data) *len = length; + } else if (saveLength >= cx->savedLength) { + data = (unsigned char*)cx->savedData; + if (cx->savedData) { + PORT_Memcpy(save,cx->savedData,cx->savedLength); + } + *len = cx->savedLength; + } + return (data != NULL) ? SECSuccess : SECFailure; +} + +/* + * restore the context state into a new running context. Also required for + * FORTEZZA . + */ +SECStatus +PK11_RestoreContext(PK11Context *cx,unsigned char *save,int len) +{ + SECStatus rv = SECSuccess; + if (cx->ownSession) { + PK11_EnterContextMonitor(cx); + pk11_Finalize(cx); + rv = pk11_restoreContext(cx,save,len); + PK11_ExitContextMonitor(cx); + } else { + PORT_Assert(cx->savedData != NULL); + if ((cx->savedData == NULL) || (cx->savedLength < (unsigned) len)) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + rv = SECFailure; + } else { + PORT_Memcpy(cx->savedData,save,len); + cx->savedLength = len; + } + } + return rv; +} + +/* + * This is to get FIPS compliance until we can convert + * libjar to use PK11_ hashing functions. It returns PR_FALSE + * if we can't get a PK11 Context. + */ +PRBool +PK11_HashOK(SECOidTag algID) { + PK11Context *cx; + + cx = PK11_CreateDigestContext(algID); + if (cx == NULL) return PR_FALSE; + PK11_DestroyContext(cx, PR_TRUE); + return PR_TRUE; +} + + + +/* + * start a new digesting or Mac'ing operation on this context + */ +SECStatus PK11_DigestBegin(PK11Context *cx) +{ + CK_MECHANISM mech_info; + SECStatus rv; + + if (cx->init == PR_TRUE) { + return SECSuccess; + } + + /* + * make sure the old context is clear first + */ + PK11_EnterContextMonitor(cx); + pk11_Finalize(cx); + + mech_info.mechanism = cx->type; + mech_info.pParameter = cx->param->data; + mech_info.ulParameterLen = cx->param->len; + rv = pk11_context_init(cx,&mech_info); + PK11_ExitContextMonitor(cx); + + if (rv != SECSuccess) { + return SECFailure; + } + cx->init = PR_TRUE; + return SECSuccess; +} + +SECStatus +PK11_HashBuf(SECOidTag hashAlg, unsigned char *out, unsigned char *in, + int32 len) { + PK11Context *context; + unsigned int max_length; + unsigned int out_length; + SECStatus rv; + + context = PK11_CreateDigestContext(hashAlg); + if (context == NULL) return SECFailure; + + rv = PK11_DigestBegin(context); + if (rv != SECSuccess) { + PK11_DestroyContext(context, PR_TRUE); + return rv; + } + + rv = PK11_DigestOp(context, in, len); + if (rv != SECSuccess) { + PK11_DestroyContext(context, PR_TRUE); + return rv; + } + + /* we need the output length ... maybe this should be table driven...*/ + switch (hashAlg) { + case SEC_OID_SHA1: max_length = SHA1_LENGTH; break; + case SEC_OID_MD2: max_length = MD2_LENGTH; break; + case SEC_OID_MD5: max_length = MD5_LENGTH; break; + default: max_length = 16; break; + } + + rv = PK11_DigestFinal(context,out,&out_length,max_length); + PK11_DestroyContext(context, PR_TRUE); + return rv; +} + + +/* + * execute a bulk encryption operation + */ +SECStatus +PK11_CipherOp(PK11Context *context, unsigned char * out, int *outlen, + int maxout, unsigned char *in, int inlen) +{ + CK_RV crv = CKR_OK; + CK_ULONG length = maxout; + CK_ULONG offset =0; + PK11SymKey *symKey = context->key; + SECStatus rv = SECSuccess; + unsigned char *saveOut = out; + unsigned char *allocOut = NULL; + + /* if we ran out of session, we need to restore our previously stored + * state. + */ + PK11_EnterContextMonitor(context); + if (!context->ownSession) { + rv = pk11_restoreContext(context,context->savedData, + context->savedLength); + if (rv != SECSuccess) { + PK11_ExitContextMonitor(context); + return rv; + } + } + + /* + * The fortezza hack is to send 8 extra bytes on the first encrypted and + * loose them on the first decrypt. + */ + if (context->fortezzaHack) { + unsigned char random[8]; + if (context->operation == CKA_ENCRYPT) { + PK11_ExitContextMonitor(context); + rv = PK11_GenerateRandom(random,sizeof(random)); + PK11_EnterContextMonitor(context); + + /* since we are offseting the output, we can't encrypt back into + * the same buffer... allocate a temporary buffer just for this + * call. */ + allocOut = out = (unsigned char*)PORT_Alloc(maxout); + if (out == NULL) { + PK11_ExitContextMonitor(context); + return SECFailure; + } + crv = PK11_GETTAB(context->slot)->C_EncryptUpdate(context->session, + random,sizeof(random),out,&length); + + out += length; + maxout -= length; + offset = length; + } else if (context->operation == CKA_DECRYPT) { + length = sizeof(random); + crv = PK11_GETTAB(context->slot)->C_DecryptUpdate(context->session, + in,sizeof(random),random,&length); + inlen -= length; + in += length; + context->fortezzaHack = PR_FALSE; + } + } + + switch (context->operation) { + case CKA_ENCRYPT: + length = maxout; + crv=PK11_GETTAB(context->slot)->C_EncryptUpdate(context->session, + in, inlen, out, &length); + length += offset; + break; + case CKA_DECRYPT: + length = maxout; + crv=PK11_GETTAB(context->slot)->C_DecryptUpdate(context->session, + in, inlen, out, &length); + break; + default: + crv = CKR_OPERATION_NOT_INITIALIZED; + break; + } + + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + *outlen = 0; + rv = SECFailure; + } else { + *outlen = length; + } + + if (context->fortezzaHack) { + if (context->operation == CKA_ENCRYPT) { + PORT_Assert(allocOut); + PORT_Memcpy(saveOut, allocOut, length); + PORT_Free(allocOut); + } + context->fortezzaHack = PR_FALSE; + } + + /* + * handle session starvation case.. use our last session to multiplex + */ + if (!context->ownSession) { + context->savedData = pk11_saveContext(context,context->savedData, + &context->savedLength); + if (context->savedData == NULL) rv = SECFailure; + + /* clear out out session for others to use */ + pk11_Finalize(context); + } + PK11_ExitContextMonitor(context); + return rv; +} + +/* + * execute a digest/signature operation + */ +SECStatus +PK11_DigestOp(PK11Context *context, const unsigned char * in, unsigned inLen) +{ + CK_RV crv = CKR_OK; + SECStatus rv = SECSuccess; + + /* if we ran out of session, we need to restore our previously stored + * state. + */ + context->init = PR_FALSE; + PK11_EnterContextMonitor(context); + if (!context->ownSession) { + rv = pk11_restoreContext(context,context->savedData, + context->savedLength); + if (rv != SECSuccess) { + PK11_ExitContextMonitor(context); + return rv; + } + } + + switch (context->operation) { + /* also for MAC'ing */ + case CKA_SIGN: + crv=PK11_GETTAB(context->slot)->C_SignUpdate(context->session, + (unsigned char *)in, + inLen); + break; + case CKA_VERIFY: + crv=PK11_GETTAB(context->slot)->C_VerifyUpdate(context->session, + (unsigned char *)in, + inLen); + break; + case CKA_DIGEST: + crv=PK11_GETTAB(context->slot)->C_DigestUpdate(context->session, + (unsigned char *)in, + inLen); + break; + default: + crv = CKR_OPERATION_NOT_INITIALIZED; + break; + } + + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + rv = SECFailure; + } + + /* + * handle session starvation case.. use our last session to multiplex + */ + if (!context->ownSession) { + context->savedData = pk11_saveContext(context,context->savedData, + &context->savedLength); + if (context->savedData == NULL) rv = SECFailure; + + /* clear out out session for others to use */ + pk11_Finalize(context); + } + PK11_ExitContextMonitor(context); + return rv; +} + +/* + * Digest a key if possible./ + */ +SECStatus +PK11_DigestKey(PK11Context *context, PK11SymKey *key) +{ + CK_RV crv = CKR_OK; + SECStatus rv = SECSuccess; + PK11SymKey *newKey = NULL; + + /* if we ran out of session, we need to restore our previously stored + * state. + */ + if (context->slot != key->slot) { + newKey = pk11_CopyToSlot(context->slot,CKM_SSL3_SHA1_MAC,CKA_SIGN,key); + } else { + newKey = PK11_ReferenceSymKey(key); + } + + context->init = PR_FALSE; + PK11_EnterContextMonitor(context); + if (!context->ownSession) { + rv = pk11_restoreContext(context,context->savedData, + context->savedLength); + if (rv != SECSuccess) { + PK11_ExitContextMonitor(context); + PK11_FreeSymKey(newKey); + return rv; + } + } + + + if (newKey == NULL) { + crv = CKR_KEY_TYPE_INCONSISTENT; + if (key->data.data) { + crv=PK11_GETTAB(context->slot)->C_DigestUpdate(context->session, + key->data.data,key->data.len); + } + } else { + crv=PK11_GETTAB(context->slot)->C_DigestKey(context->session, + newKey->objectID); + } + + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + rv = SECFailure; + } + + /* + * handle session starvation case.. use our last session to multiplex + */ + if (!context->ownSession) { + context->savedData = pk11_saveContext(context,context->savedData, + &context->savedLength); + if (context->savedData == NULL) rv = SECFailure; + + /* clear out out session for others to use */ + pk11_Finalize(context); + } + PK11_ExitContextMonitor(context); + if (newKey) PK11_FreeSymKey(newKey); + return rv; +} + +/* + * externally callable version of the lowercase pk11_finalize(). + */ +SECStatus +PK11_Finalize(PK11Context *context) { + SECStatus rv; + + PK11_EnterContextMonitor(context); + rv = pk11_Finalize(context); + PK11_ExitContextMonitor(context); + return rv; +} + +/* + * clean up a cipher operation, so the session can be used by + * someone new. + */ +SECStatus +pk11_Finalize(PK11Context *context) +{ + CK_ULONG count = 0; + CK_RV crv; + + if (!context->ownSession) { + return SECSuccess; + } + + switch (context->operation) { + case CKA_ENCRYPT: + crv=PK11_GETTAB(context->slot)->C_EncryptFinal(context->session, + NULL,&count); + break; + case CKA_DECRYPT: + crv = PK11_GETTAB(context->slot)->C_DecryptFinal(context->session, + NULL,&count); + break; + case CKA_SIGN: + crv=PK11_GETTAB(context->slot)->C_SignFinal(context->session, + NULL,&count); + break; + case CKA_VERIFY: + crv=PK11_GETTAB(context->slot)->C_VerifyFinal(context->session, + NULL,count); + break; + case CKA_DIGEST: + crv=PK11_GETTAB(context->slot)->C_DigestFinal(context->session, + NULL,&count); + break; + default: + crv = CKR_OPERATION_NOT_INITIALIZED; + break; + } + + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + return SECSuccess; +} + +/* + * Return the final digested or signed data... + * this routine can either take pre initialized data, or allocate data + * either out of an arena or out of the standard heap. + */ +SECStatus +PK11_DigestFinal(PK11Context *context,unsigned char *data, + unsigned int *outLen, unsigned int length) +{ + CK_ULONG len; + CK_RV crv; + SECStatus rv; + + + /* if we ran out of session, we need to restore our previously stored + * state. + */ + PK11_EnterContextMonitor(context); + if (!context->ownSession) { + rv = pk11_restoreContext(context,context->savedData, + context->savedLength); + if (rv != SECSuccess) { + PK11_ExitContextMonitor(context); + return rv; + } + } + + len = length; + switch (context->operation) { + case CKA_SIGN: + crv=PK11_GETTAB(context->slot)->C_SignFinal(context->session, + data,&len); + break; + case CKA_VERIFY: + crv=PK11_GETTAB(context->slot)->C_VerifyFinal(context->session, + data,len); + break; + case CKA_DIGEST: + crv=PK11_GETTAB(context->slot)->C_DigestFinal(context->session, + data,&len); + break; + case CKA_ENCRYPT: + crv=PK11_GETTAB(context->slot)->C_EncryptFinal(context->session, + data, &len); + break; + case CKA_DECRYPT: + crv = PK11_GETTAB(context->slot)->C_DecryptFinal(context->session, + data, &len); + break; + default: + crv = CKR_OPERATION_NOT_INITIALIZED; + break; + } + PK11_ExitContextMonitor(context); + + *outLen = (unsigned int) len; + context->init = PR_FALSE; /* allow Begin to start up again */ + + + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + return SECSuccess; +} + +/**************************************************************************** + * + * Now Do The PBE Functions Here... + * + ****************************************************************************/ + +SECAlgorithmID * +PK11_CreatePBEAlgorithmID(SECOidTag algorithm, int iteration, SECItem *salt) +{ + SECAlgorithmID *algid; + + algid = SEC_PKCS5CreateAlgorithmID(algorithm, salt, iteration); + return algid; +} + +PK11SymKey * +PK11_PBEKeyGen(PK11SlotInfo *slot, SECAlgorithmID *algid, SECItem *pwitem, + PRBool faulty3DES, void *wincx) +{ + /* pbe stuff */ + CK_PBE_PARAMS *pbe_params; + CK_MECHANISM_TYPE type; + SECItem *mech; + PK11SymKey *symKey; + + mech = PK11_ParamFromAlgid(algid); + type = PK11_AlgtagToMechanism(SECOID_FindOIDTag(&algid->algorithm)); + if(faulty3DES && (type == CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC)) { + type = CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC; + } + if(mech == NULL) { + return NULL; + } + + pbe_params = (CK_PBE_PARAMS *)mech->data; + pbe_params->pPassword = (CK_CHAR_PTR)PORT_ZAlloc(pwitem->len); + if(pbe_params->pPassword != NULL) { + PORT_Memcpy(pbe_params->pPassword, pwitem->data, pwitem->len); + pbe_params->ulPasswordLen = pwitem->len; + } else { + SECITEM_ZfreeItem(mech, PR_TRUE); + return NULL; + } + + symKey = PK11_KeyGen(slot, type, mech, 0, wincx); + + PORT_ZFree(pbe_params->pPassword, pwitem->len); + SECITEM_ZfreeItem(mech, PR_TRUE); + return symKey; +} + + +SECStatus +PK11_ImportEncryptedPrivateKeyInfo(PK11SlotInfo *slot, + SECKEYEncryptedPrivateKeyInfo *epki, SECItem *pwitem, + SECItem *nickname, SECItem *publicValue, PRBool isPerm, + PRBool isPrivate, KeyType keyType, unsigned int keyUsage, + void *wincx) +{ + CK_MECHANISM_TYPE mechanism; + SECItem *pbe_param, crypto_param; + PK11SymKey *key = NULL; + SECStatus rv = SECSuccess; + CK_MECHANISM cryptoMech, pbeMech; + CK_RV crv; + SECKEYPrivateKey *privKey = NULL; + PRBool faulty3DES = PR_FALSE; + int usageCount; + CK_KEY_TYPE key_type; + CK_ATTRIBUTE_TYPE *usage; + CK_ATTRIBUTE_TYPE rsaUsage[] = { + CKA_UNWRAP, CKA_DECRYPT, CKA_SIGN, CKA_SIGN_RECOVER }; + CK_ATTRIBUTE_TYPE dsaUsage[] = { CKA_SIGN }; + CK_ATTRIBUTE_TYPE dhUsage[] = { CKA_DERIVE }; + + if((epki == NULL) || (pwitem == NULL)) + return SECFailure; + + crypto_param.data = NULL; + + mechanism = PK11_AlgtagToMechanism(SECOID_FindOIDTag( + &epki->algorithm.algorithm)); + + switch (keyType) { + default: + case rsaKey: + key_type = CKK_RSA; + switch (keyUsage & (KU_KEY_ENCIPHERMENT|KU_DIGITAL_SIGNATURE)) { + case KU_KEY_ENCIPHERMENT: + usage = rsaUsage; + usageCount = 2; + break; + case KU_DIGITAL_SIGNATURE: + usage = &rsaUsage[2]; + usageCount = 2; + break; + case KU_KEY_ENCIPHERMENT|KU_DIGITAL_SIGNATURE: + case 0: /* default to everything */ + usage = rsaUsage; + usageCount = 4; + break; + } + break; + case dhKey: + key_type = CKK_DH; + usage = dhUsage; + usageCount = sizeof(dhUsage)/sizeof(dhUsage[0]); + break; + case dsaKey: + key_type = CKK_DSA; + usage = dsaUsage; + usageCount = sizeof(dsaUsage)/sizeof(dsaUsage[0]); + break; + } + +try_faulty_3des: + pbe_param = PK11_ParamFromAlgid(&epki->algorithm); + + key = PK11_PBEKeyGen(slot, &epki->algorithm, pwitem, faulty3DES, wincx); + if((key == NULL) || (pbe_param == NULL)) { + rv = SECFailure; + goto done; + } + + pbeMech.mechanism = mechanism; + pbeMech.pParameter = pbe_param->data; + pbeMech.ulParameterLen = pbe_param->len; + + crv = PK11_MapPBEMechanismToCryptoMechanism(&pbeMech, &cryptoMech, + pwitem, faulty3DES); + if(crv != CKR_OK) { + rv = SECFailure; + goto done; + } + + cryptoMech.mechanism = PK11_GetPadMechanism(cryptoMech.mechanism); + crypto_param.data = (unsigned char*)cryptoMech.pParameter; + crypto_param.len = cryptoMech.ulParameterLen; + + privKey = PK11_UnwrapPrivKey(slot, key, cryptoMech.mechanism, + &crypto_param, &epki->encryptedData, + nickname, publicValue, isPerm, isPrivate, + key_type, usage, usageCount, wincx); + if(privKey) { + SECKEY_DestroyPrivateKey(privKey); + privKey = NULL; + rv = SECSuccess; + goto done; + } + + /* if we are unable to import the key and the mechanism is + * CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC, then it is possible that + * the encrypted blob was created with a buggy key generation method + * which is described in the PKCS 12 implementation notes. So we + * need to try importing via that method. + */ + if((mechanism == CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC) && (!faulty3DES)) { + /* clean up after ourselves before redoing the key generation. */ + + PK11_FreeSymKey(key); + key = NULL; + + if(pbe_param) { + SECITEM_ZfreeItem(pbe_param, PR_TRUE); + pbe_param = NULL; + } + + if(crypto_param.data) { + SECITEM_ZfreeItem(&crypto_param, PR_FALSE); + crypto_param.data = NULL; + cryptoMech.pParameter = NULL; + crypto_param.len = cryptoMech.ulParameterLen = 0; + } + + faulty3DES = PR_TRUE; + goto try_faulty_3des; + } + + /* key import really did fail */ + rv = SECFailure; + +done: + if(pbe_param != NULL) { + SECITEM_ZfreeItem(pbe_param, PR_TRUE); + pbe_param = NULL; + } + + if(crypto_param.data != NULL) { + SECITEM_ZfreeItem(&crypto_param, PR_FALSE); + } + + if(key != NULL) { + PK11_FreeSymKey(key); + } + + return rv; +} + +/* + * import a private key info into the desired slot + */ +SECStatus +PK11_ImportPrivateKeyInfo(PK11SlotInfo *slot, SECKEYPrivateKeyInfo *pki, + SECItem *nickname, SECItem *publicValue, PRBool isPerm, + PRBool isPrivate, unsigned int keyUsage, void *wincx) +{ + CK_BBOOL cktrue = CK_TRUE; + CK_BBOOL ckfalse = CK_FALSE; + CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY; + CK_KEY_TYPE keyType = CKK_RSA; + CK_OBJECT_HANDLE objectID; + CK_ATTRIBUTE theTemplate[20]; + int templateCount = 0; + SECStatus rv = SECFailure; + SECKEYLowPrivateKey *lpk = NULL; + const SEC_ASN1Template *keyTemplate, *paramTemplate; + void *paramDest = NULL; + PRArenaPool *arena; + CK_ATTRIBUTE *attrs; + CK_ATTRIBUTE *signedattr = NULL; + int signedcount = 0; + CK_ATTRIBUTE *ap; + SECItem *ck_id = NULL; + + arena = PORT_NewArena(2048); + if(!arena) { + return SECFailure; + } + + /* need to change this to use RSA/DSA keys */ + lpk = (SECKEYLowPrivateKey *)PORT_ArenaZAlloc(arena, + sizeof(SECKEYLowPrivateKey)); + if(lpk == NULL) { + goto loser; + } + lpk->arena = arena; + + attrs = theTemplate; + switch(SECOID_GetAlgorithmTag(&pki->algorithm)) { + case SEC_OID_PKCS1_RSA_ENCRYPTION: + keyTemplate = SECKEY_RSAPrivateKeyTemplate; + paramTemplate = NULL; + paramDest = NULL; + lpk->keyType = rsaKey; + keyType = CKK_RSA; + break; + case SEC_OID_ANSIX9_DSA_SIGNATURE: + if(!publicValue) { + goto loser; + } + keyTemplate = SECKEY_DSAPrivateKeyExportTemplate; + paramTemplate = SECKEY_PQGParamsTemplate; + paramDest = &(lpk->u.dsa.params); + lpk->keyType = dsaKey; + keyType = CKK_DSA; + break; + case SEC_OID_X942_DIFFIE_HELMAN_KEY: + if(!publicValue) { + goto loser; + } + keyTemplate = SECKEY_DHPrivateKeyExportTemplate; + paramTemplate = NULL; + paramDest = NULL; + lpk->keyType = dhKey; + keyType = CKK_DH; + break; + + default: + keyTemplate = NULL; + paramTemplate = NULL; + paramDest = NULL; + break; + } + + if(!keyTemplate) { + goto loser; + } + + /* decode the private key and any algorithm parameters */ + rv = SEC_ASN1DecodeItem(arena, lpk, keyTemplate, &pki->privateKey); + if(rv != SECSuccess) { + goto loser; + } + if(paramDest && paramTemplate) { + rv = SEC_ASN1DecodeItem(arena, paramDest, paramTemplate, + &(pki->algorithm.parameters)); + if(rv != SECSuccess) { + goto loser; + } + } + + PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass) ); attrs++; + PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType) ); attrs++; + PK11_SETATTRS(attrs, CKA_TOKEN, isPerm ? &cktrue : &ckfalse, + sizeof(CK_BBOOL) ); attrs++; + PK11_SETATTRS(attrs, CKA_SENSITIVE, isPrivate ? &cktrue : &ckfalse, + sizeof(CK_BBOOL) ); attrs++; + PK11_SETATTRS(attrs, CKA_PRIVATE, isPrivate ? &cktrue : &ckfalse, + sizeof(CK_BBOOL) ); attrs++; + + switch (lpk->keyType) { + case rsaKey: + PK11_SETATTRS(attrs, CKA_UNWRAP, (keyUsage & KU_KEY_ENCIPHERMENT) ? + &cktrue : &ckfalse, sizeof(CK_BBOOL) ); attrs++; + PK11_SETATTRS(attrs, CKA_DECRYPT, (keyUsage & KU_DATA_ENCIPHERMENT) ? + &cktrue : &ckfalse, sizeof(CK_BBOOL) ); attrs++; + PK11_SETATTRS(attrs, CKA_SIGN, (keyUsage & KU_DIGITAL_SIGNATURE) ? + &cktrue : &ckfalse, sizeof(CK_BBOOL) ); attrs++; + PK11_SETATTRS(attrs, CKA_SIGN_RECOVER, + (keyUsage & KU_DIGITAL_SIGNATURE) ? + &cktrue : &ckfalse, sizeof(CK_BBOOL) ); attrs++; + ck_id = PK11_MakeIDFromPubKey(&lpk->u.rsa.modulus); + if (ck_id == NULL) { + goto loser; + } + PK11_SETATTRS(attrs, CKA_ID, ck_id->data,ck_id->len); attrs++; + if (nickname) { + PK11_SETATTRS(attrs, CKA_LABEL, nickname->data, nickname->len); attrs++; + } + signedattr = attrs; + PK11_SETATTRS(attrs, CKA_MODULUS, lpk->u.rsa.modulus.data, + lpk->u.rsa.modulus.len); attrs++; + PK11_SETATTRS(attrs, CKA_PUBLIC_EXPONENT, + lpk->u.rsa.publicExponent.data, + lpk->u.rsa.publicExponent.len); attrs++; + PK11_SETATTRS(attrs, CKA_PRIVATE_EXPONENT, + lpk->u.rsa.privateExponent.data, + lpk->u.rsa.privateExponent.len); attrs++; + PK11_SETATTRS(attrs, CKA_PRIME_1, + lpk->u.rsa.prime1.data, + lpk->u.rsa.prime1.len); attrs++; + PK11_SETATTRS(attrs, CKA_PRIME_2, + lpk->u.rsa.prime2.data, + lpk->u.rsa.prime2.len); attrs++; + PK11_SETATTRS(attrs, CKA_EXPONENT_1, + lpk->u.rsa.exponent1.data, + lpk->u.rsa.exponent1.len); attrs++; + PK11_SETATTRS(attrs, CKA_EXPONENT_2, + lpk->u.rsa.exponent2.data, + lpk->u.rsa.exponent2.len); attrs++; + PK11_SETATTRS(attrs, CKA_COEFFICIENT, + lpk->u.rsa.coefficient.data, + lpk->u.rsa.coefficient.len); attrs++; + break; + case dsaKey: + /* To make our intenal PKCS #11 module work correctly with + * our database, we need to pass in the public key value for + * this dsa key. We have a netscape only CKA_ value to do this. + * Only send it to internal slots */ + if (PK11_IsInternal(slot)) { + PK11_SETATTRS(attrs, CKA_NETSCAPE_DB, + publicValue->data, publicValue->len); attrs++; + } + PK11_SETATTRS(attrs, CKA_SIGN, &cktrue, sizeof(CK_BBOOL)); attrs++; + PK11_SETATTRS(attrs, CKA_SIGN_RECOVER, &cktrue, sizeof(CK_BBOOL)); attrs++; + if(nickname) { + PK11_SETATTRS(attrs, CKA_LABEL, nickname->data, nickname->len); + attrs++; + } + ck_id = PK11_MakeIDFromPubKey(publicValue); + if (ck_id == NULL) { + goto loser; + } + PK11_SETATTRS(attrs, CKA_ID, ck_id->data,ck_id->len); attrs++; + signedattr = attrs; + PK11_SETATTRS(attrs, CKA_PRIME, lpk->u.dsa.params.prime.data, + lpk->u.dsa.params.prime.len); attrs++; + PK11_SETATTRS(attrs,CKA_SUBPRIME,lpk->u.dsa.params.subPrime.data, + lpk->u.dsa.params.subPrime.len); attrs++; + PK11_SETATTRS(attrs, CKA_BASE, lpk->u.dsa.params.base.data, + lpk->u.dsa.params.base.len); attrs++; + PK11_SETATTRS(attrs, CKA_VALUE, lpk->u.dsa.privateValue.data, + lpk->u.dsa.privateValue.len); attrs++; + break; + case dhKey: + /* To make our intenal PKCS #11 module work correctly with + * our database, we need to pass in the public key value for + * this dh key. We have a netscape only CKA_ value to do this. + * Only send it to internal slots */ + if (PK11_IsInternal(slot)) { + PK11_SETATTRS(attrs, CKA_NETSCAPE_DB, + publicValue->data, publicValue->len); attrs++; + } + PK11_SETATTRS(attrs, CKA_DERIVE, &cktrue, sizeof(CK_BBOOL)); attrs++; + if(nickname) { + PK11_SETATTRS(attrs, CKA_LABEL, nickname->data, nickname->len); + attrs++; + } + ck_id = PK11_MakeIDFromPubKey(publicValue); + if (ck_id == NULL) { + goto loser; + } + PK11_SETATTRS(attrs, CKA_ID, ck_id->data,ck_id->len); attrs++; + signedattr = attrs; + PK11_SETATTRS(attrs, CKA_PRIME, lpk->u.dh.prime.data, + lpk->u.dh.prime.len); attrs++; + PK11_SETATTRS(attrs, CKA_BASE, lpk->u.dh.base.data, + lpk->u.dh.base.len); attrs++; + PK11_SETATTRS(attrs, CKA_VALUE, lpk->u.dh.privateValue.data, + lpk->u.dh.privateValue.len); attrs++; + break; + /* what about fortezza??? */ + default: + PORT_SetError(SEC_ERROR_BAD_KEY); + goto loser; + } + templateCount = attrs - theTemplate; + PR_ASSERT(templateCount <= sizeof(theTemplate)/sizeof(CK_ATTRIBUTE)); + signedcount = attrs - signedattr; + + for (ap=signedattr; signedcount; ap++, signedcount--) { + pk11_SignedToUnsigned(ap); + } + + rv = PK11_CreateNewObject(slot, CK_INVALID_SESSION, + theTemplate, templateCount, isPerm, &objectID); + + if (ck_id) { + SECITEM_ZfreeItem(ck_id, PR_TRUE); + } + +loser: + if (lpk!= NULL) { + SECKEY_LowDestroyPrivateKey(lpk); + } + + return rv; +} + +SECKEYPrivateKeyInfo * +PK11_ExportPrivateKeyInfo(CERTCertificate *cert, void *wincx) +{ + return NULL; +} + +static int +pk11_private_key_encrypt_buffer_length(SECKEYPrivateKey *key) + +{ + CK_ATTRIBUTE rsaTemplate = { CKA_MODULUS, NULL, 0 }; + CK_ATTRIBUTE dsaTemplate = { CKA_PRIME, NULL, 0 }; + CK_ATTRIBUTE_PTR pTemplate; + CK_RV crv; + int length; + + if(!key) { + return -1; + } + + switch (key->keyType) { + case rsaKey: + pTemplate = &rsaTemplate; + break; + case dsaKey: + case dhKey: + pTemplate = &dsaTemplate; + break; + case fortezzaKey: + default: + pTemplate = NULL; + } + + if(!pTemplate) { + return -1; + } + + crv = PK11_GetAttributes(NULL, key->pkcs11Slot, key->pkcs11ID, + pTemplate, 1); + if(crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return -1; + } + + length = pTemplate->ulValueLen; + length *= 10; + + if(pTemplate->pValue != NULL) { + PORT_Free(pTemplate->pValue); + } + + return length; +} + +SECKEYEncryptedPrivateKeyInfo * +PK11_ExportEncryptedPrivateKeyInfo(PK11SlotInfo *slot, SECOidTag algTag, + SECItem *pwitem, CERTCertificate *cert, int iteration, void *wincx) +{ + SECKEYEncryptedPrivateKeyInfo *epki = NULL; + SECKEYPrivateKey *pk; + PRArenaPool *arena = NULL; + SECAlgorithmID *algid; + CK_MECHANISM_TYPE mechanism; + SECItem *pbe_param = NULL, crypto_param; + PK11SymKey *key = NULL; + SECStatus rv = SECSuccess; + CK_MECHANISM pbeMech, cryptoMech; + CK_RV crv; + SECItem encryptedKey = {siBuffer,NULL,0}; + int encryptBufLen; + + if(!pwitem) + return NULL; + + crypto_param.data = NULL; + + arena = PORT_NewArena(2048); + epki = (SECKEYEncryptedPrivateKeyInfo *)PORT_ArenaZAlloc(arena, + sizeof(SECKEYEncryptedPrivateKeyInfo)); + if(epki == NULL) { + rv = SECFailure; + goto loser; + } + epki->arena = arena; + algid = SEC_PKCS5CreateAlgorithmID(algTag, NULL, iteration); + if(algid == NULL) { + rv = SECFailure; + goto loser; + } + + mechanism = PK11_AlgtagToMechanism(SECOID_FindOIDTag(&algid->algorithm)); + pbe_param = PK11_ParamFromAlgid(algid); + pbeMech.mechanism = mechanism; + pbeMech.pParameter = pbe_param->data; + pbeMech.ulParameterLen = pbe_param->len; + key = PK11_PBEKeyGen(slot, algid, pwitem, PR_FALSE, wincx); + + if((key == NULL) || (pbe_param == NULL)) { + rv = SECFailure; + goto loser; + } + + crv = PK11_MapPBEMechanismToCryptoMechanism(&pbeMech, &cryptoMech, + pwitem, PR_FALSE); + if(crv != CKR_OK) { + rv = SECFailure; + goto loser; + } + cryptoMech.mechanism = PK11_GetPadMechanism(cryptoMech.mechanism); + crypto_param.data = (unsigned char *)cryptoMech.pParameter; + crypto_param.len = cryptoMech.ulParameterLen; + + pk = PK11_FindKeyByAnyCert(cert, wincx); + if(pk == NULL) { + rv = SECFailure; + goto loser; + } + + encryptBufLen = pk11_private_key_encrypt_buffer_length(pk); + if(encryptBufLen == -1) { + rv = SECFailure; + goto loser; + } + encryptedKey.len = (unsigned int)encryptBufLen; + encryptedKey.data = (unsigned char *)PORT_ZAlloc(encryptedKey.len); + if(!encryptedKey.data) { + rv = SECFailure; + goto loser; + } + + /* we are extracting an encrypted privateKey structure. + * which needs to be freed along with the buffer into which it is + * returned. eventually, we should retrieve an encrypted key using + * pkcs8/pkcs5. + */ + PK11_EnterSlotMonitor(pk->pkcs11Slot); + crv = PK11_GETTAB(pk->pkcs11Slot)->C_WrapKey(pk->pkcs11Slot->session, + &cryptoMech, key->objectID, pk->pkcs11ID, encryptedKey.data, + (CK_ULONG_PTR)(&encryptedKey.len)); + PK11_ExitSlotMonitor(pk->pkcs11Slot); + if(crv != CKR_OK) { + rv = SECFailure; + goto loser; + } + + if(!encryptedKey.len) { + rv = SECFailure; + goto loser; + } + + rv = SECITEM_CopyItem(arena, &epki->encryptedData, &encryptedKey); + if(rv != SECSuccess) { + goto loser; + } + + rv = SECOID_CopyAlgorithmID(arena, &epki->algorithm, algid); + +loser: + if(pbe_param != NULL) { + SECITEM_ZfreeItem(pbe_param, PR_TRUE); + pbe_param = NULL; + } + + if(crypto_param.data != NULL) { + SECITEM_ZfreeItem(&crypto_param, PR_FALSE); + crypto_param.data = NULL; + } + + if(key != NULL) { + PK11_FreeSymKey(key); + } + + if(rv == SECFailure) { + if(arena != NULL) { + PORT_FreeArena(arena, PR_TRUE); + } + epki = NULL; + } + + return epki; +} + + +/* + * This is required to allow FORTEZZA_NULL and FORTEZZA_RC4 + * working. This function simply gets a valid IV for the keys. + */ +SECStatus +PK11_GenerateFortezzaIV(PK11SymKey *symKey,unsigned char *iv,int len) +{ + CK_MECHANISM mech_info; + CK_ULONG count = 0; + CK_RV crv; + SECStatus rv = SECFailure; + + mech_info.mechanism = CKM_SKIPJACK_CBC64; + mech_info.pParameter = iv; + mech_info.ulParameterLen = len; + + /* generate the IV for fortezza */ + PK11_EnterSlotMonitor(symKey->slot); + crv=PK11_GETTAB(symKey->slot)->C_EncryptInit(symKey->slot->session, + &mech_info, symKey->objectID); + if (crv == CKR_OK) { + PK11_GETTAB(symKey->slot)->C_EncryptFinal(symKey->slot->session, + NULL, &count); + rv = SECSuccess; + } + PK11_ExitSlotMonitor(symKey->slot); + return rv; +} + +SECKEYPrivateKey * +PK11_UnwrapPrivKey(PK11SlotInfo *slot, PK11SymKey *wrappingKey, + CK_MECHANISM_TYPE wrapType, SECItem *param, + SECItem *wrappedKey, SECItem *label, + SECItem *idValue, PRBool perm, PRBool sensitive, + CK_KEY_TYPE keyType, CK_ATTRIBUTE_TYPE *usage, int usageCount, + void *wincx) +{ + CK_BBOOL cktrue = CK_TRUE; + CK_BBOOL ckfalse = CK_FALSE; + CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY; + CK_ATTRIBUTE keyTemplate[15] ; + int templateCount = 0; + CK_OBJECT_HANDLE privKeyID; + CK_MECHANISM mechanism; + CK_ATTRIBUTE *attrs = keyTemplate; + SECItem *param_free = NULL, *ck_id; + CK_RV crv; + CK_SESSION_HANDLE rwsession; + PK11SymKey *newKey = NULL; + int i; + + if(!slot || !wrappedKey || !idValue) { + /* SET AN ERROR!!! */ + return NULL; + } + + ck_id = PK11_MakeIDFromPubKey(idValue); + if(!ck_id) { + return NULL; + } + + PK11_SETATTRS(attrs, CKA_TOKEN, perm ? &cktrue : &ckfalse, + sizeof(cktrue)); attrs++; + PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass)); attrs++; + PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType)); attrs++; + PK11_SETATTRS(attrs, CKA_PRIVATE, sensitive ? &cktrue : &ckfalse, + sizeof(cktrue)); attrs++; + PK11_SETATTRS(attrs, CKA_SENSITIVE, sensitive ? &cktrue : &ckfalse, + sizeof(cktrue)); attrs++; + PK11_SETATTRS(attrs, CKA_LABEL, label->data, label->len); attrs++; + PK11_SETATTRS(attrs, CKA_ID, ck_id->data, ck_id->len); attrs++; + for (i=0; i < usageCount; i++) { + PK11_SETATTRS(attrs, usage[i], &cktrue, sizeof(cktrue)); attrs++; + } + + if (PK11_IsInternal(slot)) { + PK11_SETATTRS(attrs, CKA_NETSCAPE_DB, idValue->data, + idValue->len); attrs++; + } + + templateCount = attrs - keyTemplate; + PR_ASSERT(templateCount <= (sizeof(keyTemplate) / sizeof(CK_ATTRIBUTE)) ); + + mechanism.mechanism = wrapType; + if(!param) param = param_free= PK11_ParamFromIV(wrapType, NULL); + if(param) { + mechanism.pParameter = param->data; + mechanism.ulParameterLen = param->len; + } else { + mechanism.pParameter = NULL; + mechanism.ulParameterLen = 0; + } + + if (wrappingKey->slot != slot) { + newKey = pk11_CopyToSlot(slot,wrapType,CKA_WRAP,wrappingKey); + } else { + newKey = PK11_ReferenceSymKey(wrappingKey); + } + + if (newKey) { + if (perm) { + rwsession = PK11_GetRWSession(slot); + } else { + rwsession = slot->session; + } + crv = PK11_GETTAB(slot)->C_UnwrapKey(rwsession, &mechanism, + newKey->objectID, + wrappedKey->data, + wrappedKey->len, keyTemplate, + templateCount, &privKeyID); + + if (perm) PK11_RestoreROSession(slot, rwsession); + PK11_FreeSymKey(newKey); + } else { + crv = CKR_FUNCTION_NOT_SUPPORTED; + } + + if(ck_id) { + SECITEM_FreeItem(ck_id, PR_TRUE); + ck_id = NULL; + } + + if (crv != CKR_OK) { + /* we couldn't unwrap the key, use the internal module to do the + * unwrap, then load the new key into the token */ + PK11SlotInfo *int_slot = PK11_GetInternalSlot(); + + if (int_slot && (slot != int_slot)) { + SECKEYPrivateKey *privKey = PK11_UnwrapPrivKey(int_slot, + wrappingKey, wrapType, param, wrappedKey, label, + idValue, PR_FALSE, PR_FALSE, + keyType, usage, usageCount, wincx); + if (privKey) { + SECKEYPrivateKey *newPrivKey = pk11_loadPrivKey(slot,privKey, + NULL,perm,sensitive); + SECKEY_DestroyPrivateKey(privKey); + PK11_FreeSlot(int_slot); + return newPrivKey; + } + } + if (int_slot) PK11_FreeSlot(int_slot); + PORT_SetError( PK11_MapError(crv) ); + return NULL; + } + return PK11_MakePrivKey(slot, nullKey, PR_FALSE, privKeyID, wincx); +} + +#define ALLOC_BLOCK 10 + +/* + * Now we're going to wrap a SECKEYPrivateKey with a PK11SymKey + * The strategy is to get both keys to reside in the same slot, + * one that can perform the desired crypto mechanism and then + * call C_WrapKey after all the setup has taken place. + */ +SECStatus +PK11_WrapPrivKey(PK11SlotInfo *slot, PK11SymKey *wrappingKey, + SECKEYPrivateKey *privKey, CK_MECHANISM_TYPE wrapType, + SECItem *param, SECItem *wrappedKey, void *wincx) +{ + PK11SlotInfo *privSlot = privKey->pkcs11Slot; /* The slot where + * the private key + * we are going to + * wrap lives. + */ + PK11SymKey *newSymKey = NULL; + SECKEYPrivateKey *newPrivKey = NULL; + SECItem *param_free = NULL; + CK_ULONG len = wrappedKey->len; + CK_MECHANISM mech; + CK_RV crv; + + if (!privSlot || !PK11_DoesMechanism(privSlot, wrapType)) { + /* Figure out a slot that does the mechanism and try to import + * the private key onto that slot. + */ + PK11SlotInfo *int_slot = PK11_GetInternalSlot(); + + privSlot = int_slot; /* The private key has a new home */ + newPrivKey = pk11_loadPrivKey(privSlot,privKey,NULL,PR_FALSE,PR_FALSE); + if (newPrivKey == NULL) { + PK11_FreeSlot (int_slot); + return SECFailure; + } + privKey = newPrivKey; + } + + if (privSlot != wrappingKey->slot) { + newSymKey = pk11_CopyToSlot (privSlot, wrapType, CKA_WRAP, + wrappingKey); + wrappingKey = newSymKey; + } + + if (wrappingKey == NULL) { + if (newPrivKey) { + SECKEY_DestroyPrivateKey(newPrivKey); + } + return SECFailure; + } + mech.mechanism = wrapType; + if (!param) { + param = param_free = PK11_ParamFromIV(wrapType, NULL); + } + if (param) { + mech.pParameter = param->data; + mech.ulParameterLen = param->len; + } else { + mech.pParameter = NULL; + mech.ulParameterLen = 0; + } + + PK11_EnterSlotMonitor(privSlot); + crv = PK11_GETTAB(privSlot)->C_WrapKey(privSlot->session, &mech, + wrappingKey->objectID, + privKey->pkcs11ID, + wrappedKey->data, &len); + PK11_ExitSlotMonitor(privSlot); + + if (newSymKey) { + PK11_FreeSymKey(newSymKey); + } + if (newPrivKey) { + SECKEY_DestroyPrivateKey(newPrivKey); + } + + if (crv != CKR_OK) { + PORT_SetError( PK11_MapError(crv) ); + return SECFailure; + } + + wrappedKey->len = len; + return SECSuccess; +} + +void +PK11_SetFortezzaHack(PK11SymKey *symKey) { + symKey->origin = PK11_OriginFortezzaHack; +} + diff --git a/security/nss/tests/ssl/ssl.sh b/security/nss/tests/ssl/ssl.sh new file mode 100755 index 000000000..2838861d8 --- /dev/null +++ b/security/nss/tests/ssl/ssl.sh @@ -0,0 +1,329 @@ +#! /bin/ksh +# +# This is just a quick script so we can still run our testcases. +# Longer term we need a scriptable test environment.. +# +. ../common/init.sh +CURDIR=`pwd` +PORT=${PORT-8443} + +# Test case files +SSLCOV=${CURDIR}/sslcov.txt +SSLAUTH=${CURDIR}/sslauth.txt +SSLSTRESS=${CURDIR}/sslstress.txt +REQUEST_FILE=${CURDIR}/sslreq.txt + +#temparary files +TMP=${TMP-/tmp} +PWFILE=${TMP}/tests.pw.$$ +CERTSCRIPT=${TMP}/tests_certs.$$ +NOISE_FILE=${TMP}/tests_noise.$$ +SERVEROUTFILE=${TMP}/tests_server.$$ +SERVERPID=${TMP}/tests_pid.$$ + +TEMPFILES="${PWFILE} ${CERTSCRIPT} ${SERVEROUTFILE} ${NOISE_FILE} ${SERVERPID}" + +none=1 +coverage=0 +auth=0 +stress=0 +certs=1 +fileout=0 + +for i in $* +do + case $i in + [aA][lL]*) + none=0; coverage=1; auth=1; stress=1;; + [aA][uU]*) + none=0; auth=1;; + [Nn][Oo][aA][uU]*) + auth=0;; + [Cc][Oo]*) + none=0; coverage=1;; + [Nn][Oo][Cc][Oo]*) + coverage=0;; + [Cc][Ee]*) + none=0; certs=1;; + [Nn][Oo][Cc][Ee]*) + certs=0;; + [Ss]*) + none=0; stress=1;; + [Nn][Oo][Ss]*) + stress=0;; + [Vv][Ee][Rr][Bb]*) + verbose=-v;; + f) + fileout=1; + esac +done + +if [ $none -eq 1 ]; then + coverage=1 + auth=1 + stress=1 +fi + + +# +# should also try to kill any running server +# +trap "rm -f ${TEMPFILES}; exit" 2 3 + +CADIR=${HOSTDIR}/CA +SERVERDIR=${HOSTDIR}/server +CLIENTDIR=${HOSTDIR}/client + +if [ $certs -eq 1 ]; then +# Generate noise for our CA cert. +# +# NOTE: these keys are only suitable for testing, as this whole thing bypasses +# the entropy gathering. Don't use this method to generate keys and certs for +# product use or deployment. +# +ps -efl > ${NOISE_FILE} 2>&1 +ps aux >> ${NOISE_FILE} 2>&1 +netstat >> ${NOISE_FILE} 2>&1 +date >> ${NOISE_FILE} 2>&1 + +# +# build the TEMP CA used for testing purposes +# +echo "<TABLE BORDER=1><TR><TH COLSPAN=3>Certutil Tests</TH></TR>" >> ${RESULTS} +echo "<TR><TH width=500>Test Case</TH><TH width=50>Result</TH></TR>" >> ${RESULTS} +echo "********************** Creating a CA Certificate **********************" +if [ ! -d ${CADIR} ]; then + mkdir -p ${CADIR} +fi +cd ${CADIR} +echo nss > ${PWFILE} +echo " certutil -N -d . -f ${PWFILE}" +certutil -N -d . -f ${PWFILE} + +echo initialized +echo 5 > ${CERTSCRIPT} +echo 9 >> ${CERTSCRIPT} +echo n >> ${CERTSCRIPT} +echo y >> ${CERTSCRIPT} +echo 3 >> ${CERTSCRIPT} +echo n >> ${CERTSCRIPT} +echo 5 >> ${CERTSCRIPT} +echo 6 >> ${CERTSCRIPT} +echo 7 >> ${CERTSCRIPT} +echo 9 >> ${CERTSCRIPT} +echo n >> ${CERTSCRIPT} +echo "certutil -S -n \"TestCA\" -s \"CN=NSS Test CA, O=BOGUS NSS, L=Mountain View, ST=California, C=US\" -t \"CTu,CTu,CTu\" -v 60 -x -d . -1 -2 -5 -f ${PWFILE} -z ${NOISE_FILE}" +certutil -S -n "TestCA" -s "CN=NSS Test CA, O=BOGUS NSS, L=Mountain View, ST=California, C=US" -t "CTu,CTu,CTu" -v 60 -x -d . -1 -2 -5 -f ${PWFILE} -z ${NOISE_FILE} < ${CERTSCRIPT} + +if [ $? -ne 0 ]; then + echo "<TR><TD>Creating CA Cert</TD><TD bgcolor=red>Failed</TD><TR>" >> ${RESULTS} +else + echo "<TR><TD>Creating CA Cert</TD><TD bgcolor=lightGreen>Passed</TD><TR>" >> ${RESULTS} +fi + +echo "**************** Creating Client CA Issued Certificate ****************" +netstat >> ${NOISE_FILE} 2>&1 +date >> ${NOISE_FILE} 2>&1 +if [ ! -d ${CLIENTDIR} ]; then + mkdir -p ${CLIENTDIR} +fi +cd ${CLIENTDIR} +echo " certutil -N -d . -f ${PWFILE}" +certutil -N -d . -f ${PWFILE} +if [ $? -ne 0 ]; then + CERTFAILED=${CERTFAILED-"Init DB"} +fi +echo "Import the root CA" +echo " certutil -L -n \"TestCA\" -r -d ../CA > root.cert" +certutil -L -n "TestCA" -r -d ../CA > root.cert +if [ $? -ne 0 ]; then + CERTFAILED=${CERTFAILED-"Export Root"} +fi +echo " certutil -A -n \"TestCA\" -t \"TC,TC,TC\" -f ${PWFILE} -d . -i root.cert" +certutil -A -n "TestCA" -t "TC,TC,TC" -f ${PWFILE} -d . -i root.cert +if [ $? -ne 0 ]; then + CERTFAILED=${CERTFAILED-"Import Root"} +fi +echo "Generate a Certificate request" +echo " certutil -R -s \"CN=Test User, O=BOGUS Netscape, L=Mountain View, ST=California, C=US\" -d . -f ${PWFILE} -z ${NOISE_FILE} -o req" +certutil -R -s "CN=Test User, O=BOGUS NSS, L=Mountain View, ST=California, C=US" -d . -f ${PWFILE} -z ${NOISE_FILE} -o req +if [ $? -ne 0 ]; then + CERTFAILED=${CERTFAILED-"Generate Request"} +fi +echo "Sign the Certificate request" +echo "certutil -C -c "TestCA" -m 3 -v 60 -d ../CA -f ${PWFILE} -i req -o user.cert" +certutil -C -c "TestCA" -m 3 -v 60 -d ../CA -i req -o user.cert -f ${PWFILE} +if [ $? -ne 0 ]; then + CERTFAILED=${CERTFAILED-"Sign User Cert"} +fi +echo "Import the new Cert" +echo "certutil -A -n \"TestUser\" -t \"u,u,u\" -d . -f ${PWFILE} -i user.cert" +certutil -A -n "TestUser" -t "u,u,u" -d . -f ${PWFILE} -i user.cert +if [ $? -ne 0 ]; then + CERTFAILED=${CERTFAILED-"Import User"} +fi +if [ -n "${CERTFAILED}" ]; then + echo "<TR><TD>Creating User Cert</TD><TD bgcolor=red>Failed ($CERTFAILED)</TD><TR>" >> ${RESULTS} +else + echo "<TR><TD>Creating User Cert</TD><TD bgcolor=lightGreen>Passed</TD><TR>" >> ${RESULTS} +fi + +echo "***** Creating Server CA Issued Certificate for ${HOST}.${DOMSUF} *****" +netstat >> ${NOISE_FILE} 2>&1 +date >> ${NOISE_FILE} 2>&1 +if [ ! -d ${SERVERDIR} ]; then + mkdir -p ${SERVERDIR} +fi +cd ${SERVERDIR} +cp ../CA/*.db . +echo "certutil -S -n \"${HOST}.${DOMSUF}\" -s \"CN=${HOST}.${DOMSUF}, O=BOGUS Netscape, L=Mountain View, ST=California, C=US\" -t \"Pu,Pu,Pu\" -c "TestCA" -v 60 -d . -f ${PWFILE} -z ${NOISE_FILE}" +certutil -S -n "${HOST}.${DOMSUF}" -s "CN=${HOST}.${DOMSUF}, O=BOGUS Netscape, L=Mountain View, ST=California, C=US" -t "Pu,Pu,Pu" -c "TestCA" -m 1 -v 60 -d . -f ${PWFILE} -z ${NOISE_FILE} +if [ $? -ne 0 ]; then + echo "<TR><TD>Creating Server Cert</TD><TD bgcolor=red>Failed</TD><TR>" >> ${RESULTS} +else + echo "<TR><TD>Creating Server Cert</TD><TD bgcolor=lightGreen>Passed</TD><TR>" >> ${RESULTS} +fi +echo "</TABLE><BR>" >> ${RESULTS} + +rm -f ${TEMPFILES} +fi + + +# OK now lets run the tests.... +if [ $coverage -eq 1 ]; then +echo "********************* SSL Cipher Coverage ****************************" +echo "<TABLE BORDER=1><TR><TH COLSPAN=3>SSL Cipher Coverage</TH></TR>" >> ${RESULTS} +echo "<TR><TH width=500>Test Case</TH><TH width=50>Result</TH></TR>" >> ${RESULTS} +cd ${CLIENTDIR} + cat ${SSLCOV} | while read tls param testname +do + if [ $tls != "#" ]; then + echo "********************* $testname ****************************" + TLS_FLAG=-T + if [ $tls = "TLS" ]; then + TLS_FLAG="" + fi + sparam="" + if [ ${param} = "i" ]; then + sparam='-c i' + fi + echo "selfserv -v -p ${PORT} -d ${SERVERDIR} -n ${HOST}.${DOMSUF} -i ${SERVERPID} -w nss ${sparam} & " + if [ ${fileout} -eq 1 ]; then + selfserv -v -p ${PORT} -d ${SERVERDIR} -n ${HOST}.${DOMSUF} -i ${SERVERPID} -w nss ${sparam} > ${SERVEROUTFILE} 2>&1 & + else + selfserv -v -p ${PORT} -d ${SERVERDIR} -n ${HOST}.${DOMSUF} -w nss ${sparam} -i ${SERVERPID} & + fi + sleep 20 + + tstclnt -p ${PORT} -h ${HOST} -c ${param} ${TLS_FLAG} -f -d . < ${REQUEST_FILE} + if [ $? -ne 0 ]; then + echo "<TR><TD>"${testname}"</TD><TD bgcolor=red>Failed</TD><TR>" >> ${RESULTS} + else + echo "<TR><TD>"${testname}"</TD><TD bgcolor=lightGreen>Passed</TD><TR>" >> ${RESULTS} + fi + ${KILL} `cat ${SERVERPID}` + wait `cat ${SERVERPID}` + if [ ${fileout} -eq 1 ]; then + cat ${SERVEROUTFILE} + fi + ${SLEEP} + fi +done + +echo "</TABLE><BR>" >> ${RESULTS} +fi + +if [ $auth -eq 1 ]; then +echo "********************* SSL Client Auth ****************************" +cd ${CLIENTDIR} +echo "<TABLE BORDER=1><TR><TH COLSPAN=3>SSL Client Authentication</TH></TR>" >> ${RESULTS} +echo "<TR><TH width=500>Test Case</TH><TH width=50>Result</TH></TR>" >> ${RESULTS} + +cat ${SSLAUTH} | while read value sparam cparam testname +do + if [ $value != "#" ]; then + echo "***** $testname ****" + sparam=`echo $sparam | sed -e 's;_; ;g'` + cparam=`echo $cparam | sed -e 's;_; ;g'` + echo "selfserv -v -p ${PORT} -d ${SERVERDIR} -n ${HOST}.${DOMSUF} -w nss ${sparam} -i ${SERVERPID} &" + if [ ${fileout} -eq 1 ]; then + selfserv -v -p ${PORT} -d ${SERVERDIR} -n ${HOST}.${DOMSUF} -w nss ${sparam} -i ${SERVERPID} > ${SERVEROUTFILE} 2>&1 & + else + selfserv -v -p ${PORT} -d ${SERVERDIR} -n ${HOST}.${DOMSUF} -w nss ${sparam} -i ${SERVERPID} & + fi + sleep 20 + pwd + echo "tstclnt -p ${PORT} -h ${HOST} -f -d ${CLIENTDIR} ${cparam}" + tstclnt -p ${PORT} -h ${HOST} -f -d ${CLIENTDIR} ${cparam} < ${REQUEST_FILE} + ret=$? + +# +# for some reason the NT client does not return the same error code as Unix +# (sigh). +# + if [ ${OS_ARCH} = "WINNT" ]; then + if [ $value -ne 0 ]; then + if [ $ret -ne 0 ]; then + value=$ret + fi + fi + fi + + if [ $ret -ne $value ]; then + echo "<TR><TD>"${testname}"</TD><TD bgcolor=red>Failed</TD><TR>" >> ${RESULTS} + else + echo "<TR><TD>"${testname}"</TD><TD bgcolor=lightGreen>Passed</TD><TR>" >> ${RESULTS} + fi + ${KILL} `cat ${SERVERPID}` + wait `cat ${SERVERPID}` + if [ ${fileout} -eq 1 ]; then + cat ${SERVEROUTFILE} + fi + ${SLEEP} + fi +done + +echo "</TABLE><BR>" >> ${RESULTS} +fi + + +if [ $stress -eq 1 ]; then +echo "********************* Stress Test ****************************" +cd ${CLIENTDIR} +echo "<TABLE BORDER=1><TR><TH COLSPAN=3>SSL Stress Test</TH></TR>" >> ${RESULTS} +echo "<TR><TH width=500>Test Case</TH><TH width=50>Result</TH></TR>" >> ${RESULTS} + +cat ${SSLSTRESS} | while read value sparam cparam testname +do + if [ $value != "#" ]; then + echo "********************* $testname ****************************" + sparam=`echo $sparam | sed -e 's;_; ;g'` + cparam=`echo $cparam | sed -e 's;_; ;g'` + echo "selfserv -p ${PORT} -d ${SERVERDIR} -n ${HOST}.${DOMSUF} -w nss ${sparam} -i ${SERVERPID} $verbose &" + if [ ${fileout} -eq 1 ]; then + selfserv -p ${PORT} -d ${SERVERDIR} -n ${HOST}.${DOMSUF} -w nss ${sparam} -i ${SERVERPID} $verbose > ${SERVEROUTFILE} 2>&1 & + else + selfserv -p ${PORT} -d ${SERVERDIR} -n ${HOST}.${DOMSUF} -w nss ${sparam} -i ${SERVERPID} $verbose & + fi + sleep 20 + + echo "strsclnt -p ${PORT} -d . -w nss $cparam $verbose ${HOST}.${DOMSUF} " + strsclnt -p ${PORT} -d . -w nss $cparam $verbose ${HOST}.${DOMSUF} + if [ $? -ne $value ]; then + echo "<TR><TD>"${testname}"</TD><TD bgcolor=red>Failed</TD><TR>" >> ${RESULTS} + else + echo "<TR><TD>"${testname}"</TD><TD bgcolor=lightGreen>Passed</TD><TR>" >> ${RESULTS} + fi + ${KILL} `cat ${SERVERPID}` + wait `cat ${SERVERPID}` + if [ ${fileout} -eq 1 ]; then + cat ${SERVEROUTFILE} + fi + ${SLEEP} + fi +done + +echo "</TABLE><BR>" >> ${RESULTS} +fi + +rm -f ${TEMPFILES} diff --git a/security/nss/tests/ssl/sslstress.txt b/security/nss/tests/ssl/sslstress.txt new file mode 100644 index 000000000..c4dce350f --- /dev/null +++ b/security/nss/tests/ssl/sslstress.txt @@ -0,0 +1,14 @@ +# +# This file defines the tests for client auth. +# +# expected +# return server client Test Case name +# value params params +# ------ ------ ------ --------------- + 0 _ -c_1000_-C_A Stress SSL2 RC4 128 with MD5 + 0 _ -c_1000_-C_c Stress SSL3 RC4 128 with MD5 +# 0 _ -c_1000_-C_c Stress TLS RC4 128 with MD5 +# +# add client auth versions here... +# +# 0 -r -n_"Test_User"_-w_bogus TLS Request don't require client auth (bad password) |