From 61c1a106bd81794f48e4cd85bae129f9270279e8 Mon Sep 17 00:00:00 2001 From: Andrew Shadura Date: Sun, 1 Nov 2015 19:41:28 +0100 Subject: libdigidoc (3.10.1.1208-1) unstable; urgency=medium * Initial upload (Closes: #658300). # imported from the archive --- libdigidoc/cdigidoc.c | 2559 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2559 insertions(+) create mode 100644 libdigidoc/cdigidoc.c (limited to 'libdigidoc/cdigidoc.c') diff --git a/libdigidoc/cdigidoc.c b/libdigidoc/cdigidoc.c new file mode 100644 index 0000000..49e0988 --- /dev/null +++ b/libdigidoc/cdigidoc.c @@ -0,0 +1,2559 @@ +//================================================== +// FILE: digidoc.c +// PROJECT: Digi Doc +// DESCRIPTION: Utility program to demonstrate the +// functionality of DigiDocLib +// AUTHOR: Veiko Sinivee, S|E|B IT Partner Estonia +//================================================== +// Copyright (C) AS Sertifitseerimiskeskus +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// GNU Lesser General Public Licence is available at +// http://www.gnu.org/copyleft/lesser.html +//==========< HISTORY >============================= +// 12.01.2004 Veiko Sinivee +// Creation +//================================================== + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef WIN32 + #define snprintf _snprintf + #include +#endif + +#include +#include +#include +#include +#include +#include + +//==========< global constants >==================== + +// programm arguments +char* p_szInFile = 0; +char* p_szInEncFile = 0; +char* p_szInDecFile = 0; +char* p_szOutFile = 0; +char* p_szConfigFile = 0; +int p_parseMode = 1, g_nLibErrors = 0; + +int g_cgiMode = 1; // 1=output in CGI mode, 0=normal e.g. human readable mode +char* g_szOutputSeparator = 0; +char *errorClass[] = {"NO_ERRORS", "TECHNICAL", "USER", "LIBRARY"}; +char* g_szProgNameVer = "cdigidoc/"DIGIDOC_VERSION; + +//==========< forward defs >======================== + +void printErrorsAndWarnings(SignedDoc* pSigDoc); + +//==========< helper functions for argument handling >==================== + + + +//-------------------------------------------------- +// Handles one argument +//-------------------------------------------------- +int checkArguments(int argc, char** argv, int* pCounter, char** dest) +{ + int nLen = 0; + + if(argc > (*pCounter)+1 && argv[(*pCounter)+1][0] != '-') { + nLen = strlen(argv[(*pCounter)+1]) * 2; + ddocConvertInput((const char*)argv[(*pCounter)+1], dest); + (*pCounter)++; + } + return nLen; +} + +//-------------------------------------------------- +// checks the existence of one command line argument +//-------------------------------------------------- +int hasCmdLineArg(int argc, char** argv, const char* argName) +{ + int i; + + for(i = 1; i < argc; i++) { + if(argv[i] != NULL && argv[i][0] == '-' && + !strcmp(argv[i], argName)) { + return i; + } + } + return 0; +} + +//-------------------------------------------------- +// Returns the value of one command line argument +//-------------------------------------------------- +int checkCmdLineArg(int argc, char** argv, const char* argName, char** dest) +{ + int i; + + *dest = 0; // mark as not found + for(i = 1; i < argc; i++) { + if(argv[i] != NULL && argv[i][0] == '-' && + !strcmp(argv[i], argName)) { + if((i + 1 < argc) && (argv[i+1][0] != '-')) { + ddocConvertInput((const char*)argv[i+1], dest); + return 0; + } + else + return ERR_BAD_PARAM; + } + } + return 0; // not found but no error +} + +//-------------------------------------------------- +// prints usage statement +//-------------------------------------------------- +void printUsage() +{ + fprintf(stderr, "USAGE: cdigidoc [-in ] [-out ] [-config ]\n"); + fprintf(stderr, "COMMANDS:\n"); + fprintf(stderr, "\t[-new] \n"); + fprintf(stderr, "\t-check-cert <-cerificate-file-name (PEM format)>\n"); + fprintf(stderr, "\t-add [] []\n"); + fprintf(stderr, "\t-verify \n"); + fprintf(stderr, "\t-sign [[[] [ ]] [slot(0)] [ocsp(1)] [PKCS11/CNG/PKCS12] [pkcs12-file-name]]\n"); + fprintf(stderr, "\t-extract [] []\n"); + fprintf(stderr, "\t-encrypt \n"); + fprintf(stderr, "\t-encrypt-sk \n"); + fprintf(stderr, "\t-encrecv [] [] []\n"); + fprintf(stderr, "\t-decrypt [pkcs12-file] [slot(0)]\n"); + fprintf(stderr, "\t-decrypt-sk [pkcs12-file] [slot(0)]\n"); + fprintf(stderr, "\t-denc-list \n"); + fprintf(stderr, "\t-encrypt-file []\n"); + fprintf(stderr, "\t-decrypt-file [pkcs12-file]\n"); + + fprintf(stderr, "\t-calc-sign [] [ ]\n"); + fprintf(stderr, "\t-add-sign-value \n"); + fprintf(stderr, "\t-del-sign \n"); + fprintf(stderr, "\t-get-confirmation \n"); + + fprintf(stderr, "\t-mid-sign [[(EE)] [(EST)] [(Testing)] [] [ ]]\n"); + fprintf(stderr, "\t-mid-test [ (Testimine)]\n"); + + fprintf(stderr, "\t-in-mem \n"); + fprintf(stderr, "\t-add-mem []\n"); + fprintf(stderr, "\t-out-mem \n"); + fprintf(stderr, "\t-extract-mem \n"); + + fprintf(stderr, "OPTIONS:\n"); + fprintf(stderr, "\t-cgimode [ print usage + if(!err && (hasCmdLineArg(argc, argv, "-?") || + hasCmdLineArg(argc, argv, "-help"))) + printUsage(); + // -in + if(!err) + if((err = checkCmdLineArg(argc, argv, "-in", &p_szInFile)) != ERR_OK) + addError(err, __FILE__, __LINE__, "Missing or invalid input file name"); + // -in-mem + if(!err && hasCmdLineArg(argc, argv, "-in-mem")) { + if((err = checkCmdLineArg(argc, argv, "-in-mem", &p_szInFile)) != ERR_OK) + addError(err, __FILE__, __LINE__, "Missing or invalid input file name"); + p_parseMode = 3; + } + // -SAX -> use Sax parser (default) + if(!err) + if(hasCmdLineArg(argc, argv, "-SAX")) + p_parseMode = 1; + // -SAX -> use Sax parser (default) + if(!err) + if(hasCmdLineArg(argc, argv, "-XRDR")) + p_parseMode = 2; + // -libraryerrors + if(!err) + if(hasCmdLineArg(argc, argv, "-libraryerrors")) + g_nLibErrors = 1; + // -out + if(!err) + if((err = checkCmdLineArg(argc, argv, "-out", &p_szOutFile)) != ERR_OK) + addError(err, __FILE__, __LINE__, "Missing or invalid output file name"); + if(!err && hasCmdLineArg(argc, argv, "-out-mem")) { + if((err = checkCmdLineArg(argc, argv, "-out-mem", &p_szOutFile)) != ERR_OK) + addError(err, __FILE__, __LINE__, "Missing or invalid output file name"); + p_parseMode = 3; + } + // -encrypt + if(!err && hasCmdLineArg(argc, argv, "-encrypt")) + if((err = checkCmdLineArg(argc, argv, "-encrypt", &p_szInEncFile)) != ERR_OK) + addError(err, __FILE__, __LINE__, "Missing or invalid encrypt input file name"); + // -encrypt-sk + if(!err && hasCmdLineArg(argc, argv, "-encrypt-sk")) + if((err = checkCmdLineArg(argc, argv, "-encrypt-sk", &p_szInEncFile)) != ERR_OK) + addError(err, __FILE__, __LINE__, "Missing or invalid encrypt input file name"); + // -config + if(!err) + if((err = checkCmdLineArg(argc, argv, "-config", &p_szConfigFile)) != ERR_OK) + addError(err, __FILE__, __LINE__, "Missing or invalid configuration file name"); + // -CGI -> use CGI output mode + if(!err) + if(hasCmdLineArg(argc, argv, "-cgimode")) { + g_cgiMode = 1; + if((err = checkCmdLineArg(argc, argv, "-cgimode", &g_szOutputSeparator)) != ERR_OK) + addError(err, __FILE__, __LINE__, "Missing or invalid cgi output separator"); + if(!g_szOutputSeparator || !g_szOutputSeparator[0] || err) { + g_szOutputSeparator = strdup("|"); + err = ERR_OK; + } + } + if(hasCmdLineArg(argc, argv, "-consolemode")) { + g_cgiMode = 0; + } + return err; +} + +//-------------------------------------------------- +// reads various statusflags from config file +//-------------------------------------------------- +void readConfigParams() +{ + int n = 0; + const char* s = 0; + + // check if we are in CGI mode + n = ConfigItem_lookup_bool("DIGIDOC_CGI_MODE", 0); + if(!g_cgiMode && n) + g_cgiMode = n; + + s = ConfigItem_lookup("DIGIDOC_CGI_SEPARATOR"); + if(!g_szOutputSeparator && s) + g_szOutputSeparator = (char*)strdup(s); + if(!g_szOutputSeparator || !g_szOutputSeparator[0]) { + if(g_szOutputSeparator) free(g_szOutputSeparator); + g_szOutputSeparator = strdup("|"); + } +} + + +//==========< command handlers >==================== + +//-------------------------------------------------- +// Creates a new signed doc +//-------------------------------------------------- +int cmdNew(SignedDoc** ppSigDoc, const char* pFormat, const char* pVersion) +{ + int err = ERR_OK; + const char* format = pFormat, *version = pVersion; + + if(!p_szOutFile) { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "No output file specified"); + return err; + } + if(!format) + format = "DIGIDOC-XML"; + if(!version) + version = "1.3"; + if(format && version) { + err = SignedDoc_new(ppSigDoc, format, version); + RETURN_IF_NOT(err == ERR_OK, err); + } else { + err = ERR_UNSUPPORTED_FORMAT; + addError(err, __FILE__, __LINE__, "Error finding new document format or version"); + } + return err; +} + + + +//-------------------------------------------------- +// Adds a DataFile to signed doc +//-------------------------------------------------- +int cmdAddDataFile(SignedDoc** ppSigDoc, const char* file, + const char* mime, const char* content, const char* charset) +{ + int err = ERR_OK, l1; + DataFile *pDataFile; + char *p = 0, buf1[300]; + + // if there was no new command then implicitly create a new document + if(!(*ppSigDoc)) { + err = cmdNew(ppSigDoc, NULL, NULL); + RETURN_IF_NOT(err == ERR_OK, err); + } + // convert to UTF-8 + err = ddocConvertInput(file, &p); + l1 = sizeof(buf1); + getFullFileName(p, buf1, l1); + freeLibMem(p); + // add a file + err = DataFile_new(&pDataFile, *ppSigDoc, NULL, buf1, + content, mime, 0, NULL, 0, NULL, charset); + if(!err) + err = calculateDataFileSizeAndDigest(*ppSigDoc, pDataFile->szId, buf1, DIGEST_SHA1); + + RETURN_IF_NOT(err == ERR_OK, err); + return err; +} + +//-------------------------------------------------- +// Create digidoc and adds datafiles +//-------------------------------------------------- +int runAddCmds(int argc, char** argv, SignedDoc** ppSigDoc) +{ + int err = ERR_OK, i; + + for(i = 1; (err == ERR_OK) && (i < argc); i++) { + if(argv[i][0] == '-') { // all commands and options must start with - + // create new digidoc + if(!strcmp(argv[i], "-new")) { + char* format = NULL; + char* version = NULL; + // optional content and charset + checkArguments(argc, argv, &i, &format); + checkArguments(argc, argv, &i, &version); + //printf("format: %s version: %s\n", format, version); + err = cmdNew(ppSigDoc, format, version); + freeLibMem(format); + freeLibMem(version); + RETURN_IF_NOT(err == ERR_OK, err); + } + // add a DataFile + if(!strcmp(argv[i], "-add")) { + if(argc > i+2 && argv[i+1][0] != '-' && argv[i+2][0] != '-') { + char* file = argv[i+1]; + char* mime = argv[i+2]; + char* content = NULL; + char* charset = NULL; + i += 2; + // optional content and charset + checkArguments(argc, argv, &i, &content); + checkArguments(argc, argv, &i, &charset); + err = cmdAddDataFile(ppSigDoc, (const char*)file, (const char*)mime, + (const char*)(content ? content : CONTENT_EMBEDDED_BASE64), + (const char*)(charset ? charset : CHARSET_UTF_8)); + freeLibMem(content); + freeLibMem(charset); + } else { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "Missing and arguments of -add command"); + } + } + + } + } + return err; +} + +//-------------------------------------------------- +// Adds a DataFile to signed doc +//-------------------------------------------------- +int cmdAddDataFileFromMem(SignedDoc** ppSigDoc, const char* file, + const char* mime, const char* content) +{ + int err = ERR_OK; + DataFile *pDf = NULL; + DigiDocMemBuf mbuf1; + + mbuf1.pMem = 0; + mbuf1.nLen = 0; + + // if there was no new command then implicitly create a new document + if(!(*ppSigDoc)) { + err = cmdNew(ppSigDoc, NULL, NULL); + RETURN_IF_NOT(err == ERR_OK, err); + } + err = ddocReadFile(file, &mbuf1); + if(!err) + err = createDataFileInMemory(&pDf, *ppSigDoc, NULL, file, content, mime, mbuf1.pMem, mbuf1.nLen); + ddocMemBuf_free(&mbuf1); + return err; +} + +//-------------------------------------------------- +// Create digidoc and adds datafiles +//-------------------------------------------------- +int runAddMemCmds(int argc, char** argv, SignedDoc** ppSigDoc) +{ + int err = ERR_OK, i; + + for(i = 1; (err == ERR_OK) && (i < argc); i++) { + if(argv[i][0] == '-') { // all commands and options must start with - + // add a DataFile + if(!strcmp(argv[i], "-add-mem")) { + if(argc > i+2 && argv[i+1][0] != '-' && argv[i+2][0] != '-') { + char* file = argv[i+1]; + char* mime = argv[i+2]; + char* content = NULL; + i += 2; + // optional content and charset + checkArguments(argc, argv, &i, &content); + err = cmdAddDataFileFromMem(ppSigDoc, (const char*)file, (const char*)mime, + (const char*)(content ? content : CONTENT_EMBEDDED_BASE64)); + freeLibMem(content); + } else { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "Missing and arguments of -add-mem command"); + } + } + + } + } + return err; +} + +//-------------------------------------------------- +// Create digidoc and adds datafiles +//-------------------------------------------------- +/*int runAddSignValue(int argc, char** argv, SignedDoc** ppSigDoc) +{ + int err = ERR_OK, i; + + for(i = 1; (err == ERR_OK) && (i < argc); i++) { + if(argv[i][0] == '-') { // all commands and options must start with - + // create new digidoc + if(!strcmp(argv[i], "-addsignvalue")) { + char* infile = NULL; + char* sigid = NULL; + char* sigvalf = NULL; + checkArguments(argc, argv, &i, &infile); + checkArguments(argc, argv, &i, &sigid); + checkArguments(argc, argv, &i, &sigvalf); + err = cmdReadDigiDoc(ppSigDoc, 0, 1); + + } + // add a DataFile + if(!strcmp(argv[i], "-addsignvalue")) { + if(argc > i+2 && argv[i+1][0] != '-' && argv[i+2][0] != '-') { + char* file = argv[i+1]; + char* mime = argv[i+2]; + char* content = CONTENT_EMBEDDED_BASE64; + char* charset = CHARSET_UTF_8; + i += 2; + // optional content and charset + checkArguments(argc, argv, &i, &content); + checkArguments(argc, argv, &i, &charset); + err = cmdAddDataFile(ppSigDoc, (const char*)file, (const char*)mime, + (const char*)content, (const char*)charset); + } else { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "Missing and or arguments of -addsignvalue command"); + } + + } + } + return err; +}*/ + +//-------------------------------------------------- +// Add ecryption recipients +//-------------------------------------------------- +int runRecipientCmds(int argc, char** argv, DEncEncryptedData** ppEncData) +{ + int err = ERR_OK, i, rb = 0; + DEncEncryptedKey* pEncKey = 0; + X509* pCert = 0; + DigiDocMemBuf mbuf1; + + mbuf1.pMem = 0; + mbuf1.nLen = 0; + for(i = 1; (err == ERR_OK) && (i < argc); i++) { + if(argv[i][0] == '-') { // all commands and options must start with - + // add a recipients key + if(!strcmp(argv[i], "-encrecv")) { + if(argc > i+1 && argv[i+1][0] != '-') { + char* certfile = argv[i+1]; + char* recipient = NULL; + char* keyname = NULL; + char* carriedkeyname = NULL; + char* id = NULL; + i++; + if(!(*ppEncData)) + err = dencEncryptedData_new(ppEncData, DENC_XMLNS_XMLENC, DENC_ENC_METHOD_AES128, 0, 0, 0); + if(!err) { + err = dencMetaInfo_SetLibVersion(*ppEncData); + if(!err) { + err = dencMetaInfo_SetFormatVersion(*ppEncData); + if(!err) { + // optional arguments + checkArguments(argc, argv, &i, &recipient); + checkArguments(argc, argv, &i, &keyname); + checkArguments(argc, argv, &i, &carriedkeyname); + err = ReadCertificate(&pCert, certfile); + if(!err) { + if(!recipient) { + ddocCertGetSubjectCN(pCert, &mbuf1); + recipient = (char*)mbuf1.pMem; + rb = 1; + } + err = dencEncryptedKey_new(*ppEncData, &pEncKey, pCert, + DENC_ENC_METHOD_RSA1_5, + id, recipient, keyname, carriedkeyname); + if(rb) recipient = NULL; // was not malloc separately from mbuf1 + } + } + } + } + ddocMemBuf_free(&mbuf1); + freeLibMem(recipient); + freeLibMem(keyname); + freeLibMem(carriedkeyname); + } else { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "Missing argument of -encrecv command"); + } + } + } + } + return err; +} + + +unsigned char* findFileExt(unsigned char* szFileName) +{ + int i = 0; + + if(szFileName && strlen(szFileName) > 0) { + for(i = strlen(szFileName)-1; (i > 0) && (szFileName[i] != '.'); i--); + if(szFileName[i] == '.') + return szFileName + i + 1; + } + return 0; +} + +int checkOldFormatVer(SignedDoc* pSigDoc) +{ + if(!pSigDoc || !pSigDoc->szFormat || !pSigDoc->szFormatVer) { + addError(ERR_BAD_PARAM, __FILE__, __LINE__, "Format or version not specified"); + return ERR_BAD_PARAM; + } + if(!strcmp(pSigDoc->szFormat, SK_XML_1_NAME) || + (!strcmp(pSigDoc->szFormat, DIGIDOC_XML_1_1_NAME) && + (!strcmp(pSigDoc->szFormatVer, DIGIDOC_XML_1_1_VER) || + !strcmp(pSigDoc->szFormatVer, DIGIDOC_XML_1_2_VER)))) { + SET_LAST_ERROR(ERR_OLD_VERSION); + return ERR_OLD_VERSION; + } + return ERR_OK; +} + +//-------------------------------------------------- +// reads in a signed doc +//-------------------------------------------------- +int cmdReadDigiDoc(SignedDoc** ppSigDoc, DEncEncryptedData** ppEncData, int nMode) +{ + int err = ERR_OK, e; + int nMaxDfLen; + unsigned char* pExt = 0; + if(!p_szInFile) { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "No input file specified"); + return err; + } + nMaxDfLen = ConfigItem_lookup_int("DATAFILE_MAX_CACHE", 0); + pExt = findFileExt(p_szInFile); + //printf("Read file: %s - ext: %s", p_szInFile, pExt); + if(pExt && strcmp(pExt, "cdoc") == 0) { + err = dencSaxReadEncryptedData(ppEncData, p_szInFile); + } else if(pExt && strcmp(pExt, "ddoc") == 0) { + switch(nMode) { + case 2: // new XMLReader interface + //err = ddocXRdrReadSignedDocFromFile(p_szInFile, CHARSET_ISO_8859_1, ppSigDoc, nMaxDfLen); + break; + case 1: + default: + err = ddocSaxReadSignedDocFromFile(ppSigDoc, p_szInFile, 0, nMaxDfLen); + } + } else { + err = ERR_FILE_READ; + } + e = checkOldFormatVer(*ppSigDoc); + if(!err && e) err = e; + printErrorsAndWarnings(*ppSigDoc); + return err; +} + +//-------------------------------------------------- +// reads in a signed doc +//-------------------------------------------------- +int cmdReadDigiDocFromMem(SignedDoc** ppSigDoc, DEncEncryptedData** ppEncData) +{ + int err = ERR_OK, e; + DigiDocMemBuf mbuf1; + + mbuf1.pMem = 0; + mbuf1.nLen = 0; + if(!p_szInFile) { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "No input file specified"); + return err; + } + //nMaxDfLen = ConfigItem_lookup_int("DATAFILE_MAX_CACHE", 0); + + if(strstr(p_szInFile, ".cdoc")) { + err = ddocReadFile(p_szInFile, &mbuf1); + if(!err) + err = dencSaxReadEncryptedDataFromMemory(ppEncData, &mbuf1); + } else { + err = ddocReadFile(p_szInFile, &mbuf1); + if(!err) + err = ddocSaxReadSignedDocFromMemory(ppSigDoc, mbuf1.pMem, mbuf1.nLen, mbuf1.nLen + 1); + } + ddocMemBuf_free(&mbuf1); + e = checkOldFormatVer(*ppSigDoc); + printErrorsAndWarnings(*ppSigDoc); + if(!err && e) err = e; + return err; +} + +//-------------------------------------------------- +// writes a digidoc in file +//-------------------------------------------------- +int cmdWrite(SignedDoc* pSigDoc, DEncEncryptedData* pEncData) +{ + int err = ERR_OK; + FILE* hFile; +#ifdef WIN32 + wchar_t *convFileName = 0; + int l1 = 0; +#endif + + if(!p_szOutFile) { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "No output file specified"); + return err; + } + if(pSigDoc) + err = createSignedDoc(pSigDoc, p_szInFile, p_szOutFile); + if(pEncData) { + if(pEncData->nDataStatus == DENC_DATA_STATUS_ENCRYPTED_AND_NOT_COMPRESSED || + pEncData->nDataStatus == DENC_DATA_STATUS_ENCRYPTED_AND_COMPRESSED) { + err = dencGenEncryptedData_writeToFile(pEncData, p_szOutFile); + } else { +#ifdef WIN32 + err = utf82unicode((const char*)p_szOutFile, (char**)&convFileName, &l1); + ddocDebug(3, "cmdWrite", "Writing file: %s, conv-file: %s len: %d", p_szOutFile, convFileName, l1); + if((hFile = _wfopen(convFileName, L"wb")) != NULL) { +#else + if((hFile = fopen(p_szOutFile, "wb")) != NULL) { +#endif + fwrite((pEncData)->mbufEncryptedData.pMem, 1, + (pEncData)->mbufEncryptedData.nLen, hFile); + fclose(hFile); + } + else + err = ERR_FILE_WRITE; + } + } + if (err) + addError(err, __FILE__, __LINE__, "Error writing file\n"); + return err; +} + +//-------------------------------------------------- +// writes a digidoc in file +//-------------------------------------------------- +int cmdWriteMem(SignedDoc* pSigDoc, DEncEncryptedData* pEncData) +{ + int err = ERR_OK; + DigiDocMemBuf mbuf1; + + mbuf1.pMem = 0; + mbuf1.nLen = 0; + if(!p_szOutFile) { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "No output file specified"); + return err; + } + if(pSigDoc) { + err = createSignedDocInMemory(pSigDoc, NULL, &mbuf1); + if(!err) + err = ddocWriteFile(p_szOutFile, &mbuf1); + } + if(pEncData) { + if(pEncData->nDataStatus == DENC_DATA_STATUS_ENCRYPTED_AND_NOT_COMPRESSED || + pEncData->nDataStatus == DENC_DATA_STATUS_ENCRYPTED_AND_COMPRESSED) { + err = dencGenEncryptedData_toXML(pEncData, &mbuf1); + if(!err) + err = ddocWriteFile(p_szOutFile, &mbuf1); + } else { + err = ddocWriteFile(p_szOutFile, &(pEncData->mbufEncryptedData)); + } + } + ddocMemBuf_free(&mbuf1); + if (err) + addError(err, __FILE__, __LINE__, "Error writing file\n"); + return err; +} + + +//----------------------------------------- +// prints certificate info +//----------------------------------------- +void printCertificateInfo(SignedDoc* pSigDoc, X509* pCert) +{ + int err, p; + char buf1[500]; + PolicyIdentifier* pPolicies; + DigiDocMemBuf mbuf; + int nPols; + + mbuf.pMem = 0; + mbuf.nLen = 0; + // serial number + err = ReadCertSerialNumber(buf1, sizeof(buf1), pCert); + if(g_cgiMode) + fprintf(stdout, "\nX509Certificate%s%s", g_szOutputSeparator, buf1); + else + fprintf(stdout, "\nX509Certificate nr: %s", buf1); + // issuer + err = ddocCertGetIssuerDN(pCert, &mbuf); + if(g_cgiMode) + fprintf(stdout, "%s%s", g_szOutputSeparator, (char*)mbuf.pMem); + else + fprintf(stdout, "\n\tIssuer: %s", (char*)mbuf.pMem); + ddocMemBuf_free(&mbuf); + // subject + err = ddocCertGetSubjectDN(pCert, &mbuf); + if(g_cgiMode) + fprintf(stdout, "%s%s", g_szOutputSeparator, (char*)mbuf.pMem); + else + fprintf(stdout, "\n\tSubject: %s", (char*)mbuf.pMem); + ddocMemBuf_free(&mbuf); + // ValidFrom + memset(buf1, 0, sizeof(buf1)); + err = getCertNotBefore(pSigDoc, pCert, buf1, sizeof(buf1)); + if(g_cgiMode) + fprintf(stdout, "%s%s", g_szOutputSeparator, buf1); + else + fprintf(stdout, "\n\tNotBefore: %s", buf1); + // ValidTo + memset(buf1, 0, sizeof(buf1)); + err = getCertNotAfter(pSigDoc, pCert, buf1, sizeof(buf1)); + if(g_cgiMode) + fprintf(stdout, "%s%s", g_szOutputSeparator, buf1); + else + fprintf(stdout, "\n\tNotAfter: %s", buf1); + // policy URL + err = readCertPolicies(pCert, &pPolicies, &nPols); + if(nPols) { + for(p = 0; p < nPols; p++) { + if(g_cgiMode) + fprintf(stdout, "\nSignaturePolicy%s%s%s%s%s%s", + g_szOutputSeparator, pPolicies[p].szOID, + g_szOutputSeparator, pPolicies[p].szCPS, + g_szOutputSeparator, pPolicies[p].szUserNotice); + else + fprintf(stdout, "\n\tnSignaturePolicy oid: %s cps: %s desc: %s", pPolicies[p].szOID, + pPolicies[p].szCPS, pPolicies[p].szUserNotice); + } // for p < nPols + } + PolicyIdentifiers_free(pPolicies, nPols); + +} + +char* TEST_OIDS_PREFS[] = { + "1.3.6.1.4.1.10015.3.7", "1.3.6.1.4.1.10015.7", // tempel test + "1.3.6.1.4.1.10015.3.3", "1.3.6.1.4.1.10015.3.11", // mid test + "1.3.6.1.4.1.10015.3.2", // digi-id test + "1.3.6.1.4.1.10015.3.1" // est-eid test +}; + +int checkTestCert(X509* pCert) +{ + int err = ERR_OK, i, p; + PolicyIdentifier* pPolicies = NULL; + DigiDocMemBuf mbuf; + int nPols = 0; + + mbuf.pMem = 0; + mbuf.nLen = 0; + ddocCertGetSubjectCN(pCert, &mbuf); + /*if(mbuf.pMem && strstr(mbuf.pMem, "TEST")) { + printf("\n Test CN: %s", (char*)(mbuf.pMem)); + err = ERR_TEST_SIGNATURE; + }*/ + err = readCertPolicies(pCert, &pPolicies, &nPols); + if(!err && nPols && !err) { + for(p = 0; p < nPols; p++) { + //printf("\nOID: %s", pPolicies[p].szOID); + for(i = 0; i < 6 && !err; i++) { + if(i == 1 && // tempel - test + strstr(pPolicies[p].szOID, TEST_OIDS_PREFS[1]) && + strstr(mbuf.pMem, "TEST")) { + //printf("\nTempel-Test OID: %s", TEST_OIDS_PREFS[i]); + err = ERR_TEST_SIGNATURE; + } else if(i != 1) { + if(strstr(pPolicies[p].szOID, TEST_OIDS_PREFS[i])) { + //printf("\nTest OID: %s", TEST_OIDS_PREFS[i]); + err = ERR_TEST_SIGNATURE; + } + } + } + } + } + ddocMemBuf_free(&mbuf); + PolicyIdentifiers_free(pPolicies, nPols); + if(err == ERR_TEST_SIGNATURE) + SET_LAST_ERROR(ERR_TEST_SIGNATURE); + return err; +} + + +int isError(SignedDoc* pSigDoc, int nErrCd) +{ + return (nErrCd != ERR_OK && !isWarning(pSigDoc, nErrCd)); +} + +int isWarning(SignedDoc* pSigDoc, int nErrCd) +{ + if(pSigDoc && pSigDoc->szFormat && !strcmp(pSigDoc->szFormat, SK_XML_1_NAME)) { + // currently no warnings for ddoc 1.0 + return (nErrCd == ERR_OLD_VERSION) || (nErrCd == ERR_TEST_SIGNATURE); + } else { + return ((nErrCd == ERR_ISSUER_XMLNS) || + (nErrCd == ERR_OLD_VERSION) || + (nErrCd == ERR_TEST_SIGNATURE) || + (nErrCd == ERR_DF_WRONG_DIG) + ); + } +} + +int isLibraryError(int nErrCd) +{ + return (nErrCd != ERR_OK && + nErrCd != ERR_OLD_VERSION && + nErrCd != ERR_TEST_SIGNATURE && + nErrCd != ERR_UNSUPPORTED_FORMAT); +} + +int hasWarnings(SignedDoc* pSigDoc) +{ + int n; + ErrorInfo* pErr; + + for(n = getLastErrorsIdx(); n >= 0; n--) { + pErr = getErrorsInfo(n); + if(isWarning(pSigDoc, pErr->code)) + return pErr->code; + } + return ERR_OK; +} + +int hasErrors(SignedDoc* pSigDoc) +{ + int n; + ErrorInfo* pErr; + + for(n = getLastErrorsIdx(); n >= 0; n--) { + pErr = getErrorsInfo(n); + if(isError(pSigDoc, pErr->code)) + return pErr->code; + } + return ERR_OK; +} + +void printErrorsAndWarnings(SignedDoc* pSigDoc) +{ + int n, m = getLastErrorsIdx(); + ErrorInfo* pErr; + char* pErrStr; + + // list all errors + if(g_cgiMode) { + /*for(n = 0; n < m; n++) { + pErr = getErrorsInfo(n); + pErrStr = getErrorString(pErr->code); + printf("\nErr: %d cd: %d msg: %s", n, pErr->code, pErrStr); + }*/ + for(n = m; n >= 0; n--) { + pErr = getErrorsInfo(n); + pErrStr = getErrorString(pErr->code); + if(isError(pSigDoc, pErr->code)) + fprintf(stdout, "\nERROR%s%d%s%s%s%s%s%d%s%s%s%s", + g_szOutputSeparator, pErr->code, + g_szOutputSeparator, pErrStr, + g_szOutputSeparator, pErr->fileName, + g_szOutputSeparator, pErr->line, + g_szOutputSeparator, pErr->assertion, + g_szOutputSeparator, errorClass[getErrorClass(pErr->code)]); + } + for(n = m; n >= 0; n--) { + pErr = getErrorsInfo(n); + pErrStr = getErrorString(pErr->code); + if(isWarning(pSigDoc, pErr->code)) + fprintf(stdout, "\nWARNING%s%d%s%s%s%s%s%d%s%s%s%s", + g_szOutputSeparator, pErr->code, + g_szOutputSeparator, pErrStr, + g_szOutputSeparator, pErr->fileName, + g_szOutputSeparator, pErr->line, + g_szOutputSeparator, pErr->assertion, + g_szOutputSeparator, errorClass[getErrorClass(pErr->code)]); + } + if(g_nLibErrors) { + for(n = m; n >= 0; n--) { + pErr = getErrorsInfo(n); + if(isLibraryError(pErr->code)) { + pErrStr = getErrorString(pErr->code); + fprintf(stdout, "\nLIBRARY-ERROR%s%d%s%s%s%s%s%d%s%s%s%s", + g_szOutputSeparator, pErr->code, + g_szOutputSeparator, pErrStr, + g_szOutputSeparator, pErr->fileName, + g_szOutputSeparator, pErr->line, + g_szOutputSeparator, pErr->assertion, + g_szOutputSeparator, errorClass[getErrorClass(pErr->code)]); + } + } + } + } else { + for(n = m; n >= 0; n--) { + pErr = getErrorsInfo(n); + pErrStr = getErrorString(pErr->code); + if(isError(pSigDoc, pErr->code)) + fprintf(stdout, "\nERROR: %d - %s - %s", + pErr->code, pErrStr, pErr->assertion); + if(isWarning(pSigDoc, pErr->code)) + fprintf(stdout, "\nWARNING: %d - %s - %s", + pErr->code, pErrStr, pErr->assertion); + } + } +} + + +//-------------------------------------------------- +// Verifys sigantures and notaries +//-------------------------------------------------- +int cmdVerify(SignedDoc* pSigDoc) +{ + int err = ERR_OK, s, d, l, m, l1, e, e2, n1, e3; + SignatureInfo* pSigInfo = 0; + NotaryInfo* pNot; + DataFile *pDf = 0; + DigiDocMemBuf mbuf; + X509* pRcert = 0; + const DigiDocMemBuf* pMBuf = 0; + char buf1[100], *p1 = 0; + + mbuf.pMem = 0; + mbuf.nLen = 0; + if(!pSigDoc) { + SET_LAST_ERROR(ERR_UNSUPPORTED_FORMAT); + return ERR_UNSUPPORTED_FORMAT; + } + // print signed doc format and version + if(g_cgiMode) { + fprintf(stdout, "\nSignedDoc%s%s%s%s", + g_szOutputSeparator, + pSigDoc->szFormat, g_szOutputSeparator, + pSigDoc->szFormatVer); + } else { + fprintf(stdout, "\nSignedDoc format: %s version: %s\n", + pSigDoc->szFormat, pSigDoc->szFormatVer); + } + // display DataFile-s + d = getCountOfDataFiles(pSigDoc); + for(l = 0; l < d; l++) { + pDf = getDataFile(pSigDoc, l); + ddocGetDataFileFilename(pSigDoc, pDf->szId, (void**)&p1, &n1); + if(g_cgiMode) + fprintf(stdout, "\n\nDataFile%s%s%s%s%s%ld%s%s%s%s", + g_szOutputSeparator, pDf->szId, g_szOutputSeparator, + p1, g_szOutputSeparator, + pDf->nSize, g_szOutputSeparator, + pDf->szMimeType, g_szOutputSeparator, + pDf->szContentType); + else + fprintf(stdout, "\n\nDataFile: %s, file: %s, size: %ld, mime-type: %s content-type: %s", + pDf->szId, p1, pDf->nSize, pDf->szMimeType, pDf->szContentType); + if(p1) + freeLibMem(p1); + } + + + // verify signatures + d = getCountOfSignatures(pSigDoc); + for(s = 0; s < d; s++) { + pSigInfo = getSignature(pSigDoc, s); + e = ddocCertGetSubjectCN(ddocSigInfo_GetSignersCert(pSigInfo), &mbuf); + clearErrors(); + e = verifySignatureAndNotary(pSigDoc, pSigInfo, p_szInFile); + if(!e) + e = hasErrors(pSigDoc); + if(!err && e) err = e; + e2 = checkTestCert(ddocSigInfo_GetSignersCert(pSigInfo)); + if(!err && e2) err = e2; + if(!e && e2) e = e2; + if(g_cgiMode) { + fprintf(stdout, "\n\nSignature%s%s%s%s%s%d%s%s", + g_szOutputSeparator, + pSigInfo->szId, + g_szOutputSeparator, + (const char*)mbuf.pMem, + g_szOutputSeparator, + e, + g_szOutputSeparator, + (e ? getErrorString(e) : "")); + } else { + fprintf(stdout, "\n\nSignature: %s - %s - %s", pSigInfo->szId, + (const char*)mbuf.pMem, ((!e) ? "OK" : "ERROR")); + } + ddocMemBuf_free(&mbuf); + // print signers roles / manifests + m = getCountOfSignerRoles(pSigInfo, 0); + for(l = 0; l < m; l++) { + if(g_cgiMode) + fprintf(stdout, "\nClaimedRole%s%s", g_szOutputSeparator, getSignerRole(pSigInfo, 0, l)); + else + fprintf(stdout, "\n\tClaimedRole: %s", getSignerRole(pSigInfo, 0, l)); + } + if(pSigInfo->sigProdPlace.szCity || pSigInfo->sigProdPlace.szStateOrProvince || + pSigInfo->sigProdPlace.szPostalCode || pSigInfo->sigProdPlace.szCountryName) { + if(g_cgiMode) { + fprintf(stdout, "\nSignatureProductionPlace%s%s%s%s%s%s%s%s", g_szOutputSeparator, + (pSigInfo->sigProdPlace.szCountryName ? pSigInfo->sigProdPlace.szCountryName : ""), + g_szOutputSeparator, + (pSigInfo->sigProdPlace.szStateOrProvince ? pSigInfo->sigProdPlace.szStateOrProvince : ""), + g_szOutputSeparator, + (pSigInfo->sigProdPlace.szCity ? pSigInfo->sigProdPlace.szCity : ""), + g_szOutputSeparator, + (pSigInfo->sigProdPlace.szPostalCode ? pSigInfo->sigProdPlace.szPostalCode : "")); + } else { + fprintf(stdout, "\n\tnSignatureProductionPlace - Country: %s, State: %s, City: %s, Postal code: %s", + (pSigInfo->sigProdPlace.szCountryName ? pSigInfo->sigProdPlace.szCountryName : ""), + (pSigInfo->sigProdPlace.szStateOrProvince ? pSigInfo->sigProdPlace.szStateOrProvince : ""), + (pSigInfo->sigProdPlace.szCity ? pSigInfo->sigProdPlace.szCity : ""), + (pSigInfo->sigProdPlace.szPostalCode ? pSigInfo->sigProdPlace.szPostalCode : "")); + } + } + // signers certificate + if(ddocSigInfo_GetSignersCert(pSigInfo)) + printCertificateInfo(pSigDoc, ddocSigInfo_GetSignersCert(pSigInfo)); + // confirmation + if(pSigDoc && pSigInfo) { + pNot = getNotaryWithSigId(pSigDoc, pSigInfo->szId); + if(pNot) { + pMBuf = ddocNotInfo_GetResponderId(pNot); + buf1[0] = 0; + l1 = sizeof(buf1); + if(pNot->nRespIdType == RESPID_NAME_TYPE) { + strncpy(buf1, (const char*)pMBuf->pMem, l1); + } + if(pNot->nRespIdType == RESPID_KEY_TYPE) { + encode((const byte*)pMBuf->pMem, pMBuf->nLen, (byte*)buf1, &l1); + } + } + if(pNot && pMBuf) { + if(g_cgiMode) + fprintf(stdout, "\nRevocationValues%s%s%s%s", + g_szOutputSeparator, + buf1, + g_szOutputSeparator, + pNot->timeProduced); + else + fprintf(stdout, "\n\tRevocationValues responder: %s produced-at: %s", + buf1, pNot->timeProduced); + // certificate + pRcert = ddocSigInfo_GetOCSPRespondersCert(pSigInfo); + e3 = checkTestCert(pRcert); + if(!e2 && e3) { // live allkirjastaja sert aga test ocsp + fprintf(stdout, "\n\tSigner from LIVE CA-chain but OCSP from TEST CA-chain!"); + err = e3; + SET_LAST_ERROR(e3); + } + printCertificateInfo(pSigDoc, pRcert); + } + } + printErrorsAndWarnings(pSigDoc); + } + return err; +} + + +//-------------------------------------------------- +// Signs the document and gets configrmation +//-------------------------------------------------- +int cmdSign(SignedDoc* pSigDoc, const char* pin, const char* manifest, + const char* city, const char* state, const char* zip, const char* country, + int nSlot, int nOcsp, int nSigner, const char* szPkcs12File) +{ + int err = ERR_OK; + SignatureInfo* pSigInfo = NULL; + + ddocDebug(3, "cmdSign", "Creating new digital signature"); + err = signDocumentWithSlotAndSigner(pSigDoc, &pSigInfo, pin, manifest, + city, state, zip, country, nSlot, nOcsp, nSigner, szPkcs12File); + ddocDebug(3, "cmdSign", "End creating new digital signature"); + RETURN_IF_NOT(err == ERR_OK, err); + return ERR_OK; +} + + + + + + +//#ifdef WITH_DEPRECATED_FUNCTIONS +//-------------------------------------------------- +// Runs some test. This is used simply to test +// new features of the library and the functionality +// may change. +//-------------------------------------------------- +void cmdTest2(const char* infile) +{ + X509* pCert = 0; + //char buf1[X509_NAME_LEN + 10]; + int err = ERR_OK, nPols = 0; + PolicyIdentifier *pPolicies = 0; + + ddocDebug(3, "cmdTest2", "Reading cert: %s", infile); + err = ReadCertificate(&pCert, infile); + if(!err && pCert) { + err = readCertPolicies(pCert, &pPolicies, &nPols); + printf("Read pols rc: %d pols: %d\n", err, nPols); + + /* + l1 = sizeof(buf1); + memset(buf1, 0, sizeof(buf1)); + err = getCertSubjectName(pCert, buf1, &l1); + if(g_cgiMode) + fprintf(stdout, "\nDN%s%s%s%d", + g_szOutputSeparator, buf1, + g_szOutputSeparator, l1); + else + fprintf(stdout, "\nDN: %s len: %d", buf1, l1); + */ + } + if(pCert) + X509_free(pCert); + if(pPolicies) + PolicyIdentifiers_free(pPolicies, nPols); +} +//#endif + +//-------------------------------------------------- +// Encrypts a file +//-------------------------------------------------- +int cmdEncrypt(DEncEncryptedData** ppEncData, const char* szFileName) +{ + FILE* hFile; + SignedDoc *pSigDoc = 0; + DEncEncryptionProperty* pEncProperty = 0; + int len, err = ERR_OK, i = 0; + long lFileLen; + char buf[2048], *p = 0; +#ifdef WIN32 + wchar_t *convFileName = 0; + err = utf82unicode((const char*)szFileName, (char**)&convFileName, &i); +#endif + + ddocDebug(3, "cmdEncrypt", "Encrypting %s rc: %d", szFileName, err); + err = dencEncryptedData_new(ppEncData, DENC_XMLNS_XMLENC, DENC_ENC_METHOD_AES128, 0, 0, 0); + if(!err) { + err = dencMetaInfo_SetLibVersion(*ppEncData); + if(!err) { + err = dencMetaInfo_SetFormatVersion(*ppEncData); + if(!err) { + err = ddocConvertInput(szFileName, &p); + if(!err) { + err = dencEncryptionProperty_new(*ppEncData, &pEncProperty, + 0, 0, ENCPROP_FILENAME, getSimpleFileName(p)); + if(strstr(p, ".bdoc") || strstr(p, ".asice")) + err = dencEncryptedData_SetMimeType(*ppEncData, "application/vnd.etsi.asic-e+zip"); + err = calculateFileSize(szFileName, &lFileLen); + sprintf(buf, "%ld", lFileLen); + err = dencEncryptionProperty_new(*ppEncData, &pEncProperty, + 0, 0, ENCPROP_ORIG_SIZE, buf); + freeLibMem(p); + if(!err) { + ddocDebug(3, "cmdEncrypt", "Opening %s rc: %d", szFileName, err); +#ifdef WIN32 + if((hFile = _wfopen(convFileName, L"rb")) != NULL) { +#else + if((hFile = fopen(szFileName, "rb")) != NULL) { +#endif + do { + memset(buf,0,sizeof(buf)); + len = fread(buf, 1, sizeof(buf), hFile); + if(len) + err = dencEncryptedData_AppendData(*ppEncData, buf, len); + } while(len && !err); + fclose(hFile); + } // if fopen/wfopen + ddocDebug(3, "cmdEncrypt", "Enc data-len: %d rc: %d", (*ppEncData)->mbufEncryptedData.nLen, err); + if(!err) + err = dencEncryptedData_encryptData(*ppEncData, DENC_COMPRESS_NEVER); + } // err - endProp_new + } // err - conver filename + } // err - serFormatVer + } // err - setLibVer + } // err - dencEncData_new + return err; +} + + +//-------------------------------------------------- +// Encrypts a file and uses SK specific method of +// putting the file first in a ddoc container and then into +// cdoc +//-------------------------------------------------- +int cmdEncryptSk(DEncEncryptedData** ppEncData, const char* szFileName) +{ + SignedDoc *pSigDoc = 0; + DEncEncryptionProperty* pEncProperty = 0; + DataFile *pDf = 0; + int err = ERR_OK; + long lSize; + char buf[256], *p = 0; + DigiDocMemBuf mbuf1; + + mbuf1.pMem = 0; + mbuf1.nLen = 0; + err = dencEncryptedData_new(ppEncData, DENC_XMLNS_XMLENC, DENC_ENC_METHOD_AES128, 0, 0, 0); + if(!err) { + err = dencMetaInfo_SetLibVersion(*ppEncData); + if(!err) { + err = dencMetaInfo_SetFormatVersion(*ppEncData); + if(!err) { + err = ddocConvertInput(szFileName, &p); + if(!err) { + err = dencEncryptionProperty_new(*ppEncData, &pEncProperty, + 0, 0, ENCPROP_FILENAME, getSimpleFileName(p)); + if(!err) { + err = SignedDoc_new(&pSigDoc, "DIGIDOC-XML", "1.3"); + if(!err) { + calculateFileSize(szFileName, &lSize); + err = DataFile_new(&pDf, pSigDoc, NULL, szFileName, + "EMBEDDED_BASE64", "application/file", lSize, NULL, 0, NULL, NULL); + sprintf(buf, "%ld", lSize); + err = dencEncryptionProperty_new(*ppEncData, &pEncProperty, + 0, 0, ENCPROP_ORIG_SIZE, buf); + if(!err) { + err = dencOrigContent_registerDigiDoc(*ppEncData, pSigDoc); + if(!err) { + sprintf(buf, "%s.ddoc", szFileName); + err = createSignedDoc(pSigDoc, NULL, buf); + if(!err) { + err = ddocReadFile(buf, &mbuf1); + if(!err) { + err = dencEncryptedData_AppendData(*ppEncData, mbuf1.pMem, mbuf1.nLen); + //ddocDebug(3, "EncTest", "Enc data: \"%s\"", mbuf1.pMem); + remove(buf); + ddocMemBuf_free(&mbuf1); + if(!err) + err = dencEncryptedData_encryptData(*ppEncData, DENC_COMPRESS_NEVER); + } // err - ddocReadFile + } // err - createSignedDoc + } // err - origContent_register + } // err - sigDoc_new + } // err - sigDoc_new + SignedDoc_free(pSigDoc); + } // err - encProp_new + freeLibMem(p); + } // err - convert filename + } // err - setFormatVer + } // err - setLibVer + } // err - endData_new + return err; +} + + +//-------------------------------------------------- +// Runs various tests. Just for trying out new features +//-------------------------------------------------- +int runTestCmds(int argc, char** argv) +{ + int err = ERR_OK, i; + char* infile = NULL; + + for(i = 1; (err == ERR_OK) && (i < argc); i++) { + if(argv[i][0] == '-') { // all commands and options must start with - + if(!strcmp(argv[i], "-test")) { + // add a recipients key + + } +//#ifdef WITH_DEPRECATED_FUNCTIONS + if(!strcmp(argv[i], "-test2")) { + ddocConvertInput((const char*)argv[i+1], &infile); + cmdTest2(argv[i+1]); + } +//#endif + } + } + return err; +} + +//-------------------------------------------------- +// Encrypts whole files +//-------------------------------------------------- +int runEncryptFileCmds(int argc, char** argv, DEncEncryptedData* pEncData) +{ + int err = ERR_OK, i; + + for(i = 1; (err == ERR_OK) && (i < argc); i++) { + if(argv[i][0] == '-') { // all commands and options must start with - + // add a recipients key + if(!strcmp(argv[i], "-encrypt-file")) { + if(argc > i+2 && argv[i+1][0] != '-' && argv[i+2][0] != '-') { + char* infile = NULL; + char* outfile = NULL; + char* mime = NULL; + ddocConvertInput((const char*)argv[i+1], &infile); + ddocConvertInput((const char*)argv[i+2], &outfile); + ddocConvertInput((const char*)argv[i+3], &mime); + i += 3; + // optional arguments + checkArguments(argc, argv, &i, &mime); + // encrypt the file + err = dencEncryptFile(pEncData, infile, outfile, mime); + if(infile) freeLibMem(infile); + if(outfile) freeLibMem(outfile); + if(mime) freeLibMem(mime); + if(err) return err; + } else { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "Missing or argument of -encrypt-file command"); + } + } + } + } + return err; +} + + +//-------------------------------------------------- +// Decrypts whole files +//-------------------------------------------------- +int runDecryptFileCmds(int argc, char** argv) +{ + int err = ERR_OK, i; + + for(i = 1; (err == ERR_OK) && (i < argc); i++) { + if(argv[i][0] == '-') { // all commands and options must start with - + // decrypt a file + if(!strcmp(argv[i], "-decrypt-file")) { + if(argc > i+2 && argv[i+1][0] != '-' && + argv[i+2][0] != '-' && argv[i+3][0] != '-') { + char* infile = NULL; + char* outfile = NULL; + char* pin = NULL; + char* pkcs12file = NULL; + ddocConvertInput((const char*)argv[i+1], &infile); + ddocConvertInput((const char*)argv[i+2], &outfile); + ddocConvertInput((const char*)argv[i+3], &pin); + i += 3; + // optional arguments + checkArguments(argc, argv, &i, &pkcs12file); + // decrypt the file + err = dencSaxReadDecryptFile(infile, outfile, pin, pkcs12file); + if(infile) freeLibMem(infile); + if(outfile) freeLibMem(outfile); + if(pin) freeLibMem(pin); + if(pkcs12file) freeLibMem(pkcs12file); + if(err) return err; + } else { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "Missing , or argument of -decrypt-file command"); + } + } + } + } + return err; +} + +//-------------------------------------------------- +// Generates and encrypts a block of data with specific +// length +//-------------------------------------------------- +int cmdEncryptTestSet(DEncEncryptedData** ppEncData, int nSize, char* dir, int nDdoc, int nDel) +{ + DEncEncryptionProperty* pEncProperty = 0; + int err = ERR_OK, i, nCompress, l2; + char *p = 0, fname1[256], fname2[256], c, *p2; + SignedDoc* pSigDoc = 0; + DataFile *pDf = 0; + FILE* hFile; + DigiDocMemBuf mbuf1; + + mbuf1.pMem = 0; + mbuf1.nLen = 0; + nCompress = ConfigItem_lookup_int("DENC_COMPRESS_MODE", DENC_COMPRESS_ALLWAYS); + ddocDebug(3, "EncTest", "Test: %d", nSize); + err = dencEncryptedData_new(ppEncData, DENC_XMLNS_XMLENC, DENC_ENC_METHOD_AES128, 0, 0, 0); + if(err) return err; + err = dencMetaInfo_SetLibVersion(*ppEncData); + if(err) return err; + err = dencMetaInfo_SetFormatVersion(*ppEncData); + if(err) return err; + sprintf(fname1, "%stest-%d.dat", dir, nSize); + err = dencEncryptionProperty_new(*ppEncData, &pEncProperty, 0, 0, ENCPROP_FILENAME, fname1); + if(err) return err; + p = (char*)malloc(nSize+1); + if(!p) return ERR_BAD_ALLOC; + memset(p, 0, nSize+1); + for(i = 0, c = 'A'; i < nSize; i++, c++) { + p[i] = c; + if(c >= 'z') c = 'A'; + } + // append x.923 padding if last block is full + if(nSize % 16 == 0) { + ddocDebug(3, "EncTest", "Full size: %d. Change last 8 bytes to X.923 padding", nSize); + p[nSize-1] = 8; + for(i = nSize -2; i > nSize - 9; i--) + p[i] = 0; + } + l2 = nSize * 2 + 1; + p2 = (char*)malloc(l2); + if(!p2) return ERR_BAD_ALLOC; + memset(p2, 0, l2); + bin2hex((const byte*)p, nSize, (byte*)p2, &l2); + ddocDebug(3, "EncTest", "Fil data hex: \"%s\" len: %d", p2, l2); + if((hFile = fopen(fname1, "w")) != NULL) { + fwrite(p, nSize, 1, hFile); + fclose(hFile); + } + free(p); p = 0; + fname2[0] = 0; + if(nDdoc) { + err = dencEncryptionProperty_new(*ppEncData, &pEncProperty, 0, 0, ENCPROP_ORIG_MIME, DENC_ENCDATA_TYPE_DDOC); + err = SignedDoc_new(&pSigDoc, "DIGIDOC-XML", "1.3"); + if(!err) { + err = DataFile_new(&pDf, pSigDoc, NULL, fname1, "EMBEDDED_BASE64", "text/txt", nSize, NULL, 0, NULL, NULL); + if(!err) { + err = dencOrigContent_registerDigiDoc(*ppEncData, pSigDoc); + if(!err) { + sprintf(fname2, "%stest-%d.ddoc", dir, nSize); + err = createSignedDoc(pSigDoc, NULL, fname2); + if(!err) { + err = ddocReadFile(fname2, &mbuf1); + if(!err) + err = dencEncryptedData_AppendData(*ppEncData, mbuf1.pMem, mbuf1.nLen); + } + } + } + } + //ddocDebug(3, "EncTest", "Enc data: \"%s\"", mbuf1.pMem); + if(nDel && fname2[0]) + remove(fname2); + ddocMemBuf_free(&mbuf1); + } else { + err = dencEncryptedData_AppendData(*ppEncData, p, nSize); + } + if(nDel && fname1[0]) + remove(fname1); + + if(!err) + err = dencEncryptedData_encryptData(*ppEncData, nCompress); + if(pSigDoc) + SignedDoc_free(pSigDoc); + return err; +} + +//-------------------------------------------------- +// Encrypts generated test files +//-------------------------------------------------- +int runEncryptTestSetCmds(int argc, char** argv) +{ + int err = ERR_OK, i, j; + DEncEncryptedData* pEncData = 0; + DEncEncryptedKey* pEncKey = 0; + X509* pCert = 0; + char fname[128]; + + for(i = 1; (err == ERR_OK) && (i < argc); i++) { + if(argv[i][0] == '-') { // all commands and options must start with - + // add a recipients key + if(!strcmp(argv[i], "-encrypt-test-set")) { + if(argc > i+2 && argv[i+1][0] != '-' && argv[i+2][0] != '-') { + char* certfile = argv[i+1]; + char* recipient = argv[i+2]; + char* outdir = argv[i+3]; + int nStart = atoi(argv[i+4]); + int nEnd = atoi(argv[i+5]); + int nDdoc = atoi(argv[i+6]); + int nDel = atoi(argv[i+7]); + + for(j = nStart; j < nEnd; j++) { + err = ReadCertificate(&pCert, certfile); + if(err) return err; + pEncData = 0; + err = cmdEncryptTestSet(&pEncData, j, outdir, nDdoc, nDel); + if(err) return err; + err = dencEncryptedKey_new(pEncData, &pEncKey, pCert, + DENC_ENC_METHOD_RSA1_5, + NULL, recipient, NULL, NULL); + sprintf(fname, "%stest-%d.cdoc", outdir, j); + err = dencGenEncryptedData_writeToFile(pEncData, fname); + dencEncryptedData_free(pEncData); + pEncKey = 0; + pEncData = 0; + } + } else { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "Invalid parameters for -encrypt-test-set command"); + } + } + } + } + return err; +} + + +//-------------------------------------------------- +// Lists EncryptedData objects +//-------------------------------------------------- +int cmdListEncryptedData(DEncEncryptedData* pEncData) +{ + int err = ERR_OK, k; + char buf1[50], buf2[50]; + + if(g_cgiMode) { + fprintf(stdout, "\nEncryptedData%s%s%s%s%s%s%s%s", + g_szOutputSeparator, (pEncData->szId ? pEncData->szId : ""), + g_szOutputSeparator, (pEncData->szType ? pEncData->szType : ""), + g_szOutputSeparator, (pEncData->szMimeType ? pEncData->szMimeType : ""), + g_szOutputSeparator, (pEncData->szEncryptionMethod ? pEncData->szEncryptionMethod : "")); + err = dencMetaInfo_GetLibVersion(pEncData, buf1, sizeof(buf1), buf2, sizeof(buf2)); + fprintf(stdout, "\nLIBRARY%s%s%s%s", g_szOutputSeparator, buf1, g_szOutputSeparator, buf2); + err = dencMetaInfo_GetFormatVersion(pEncData, buf1, sizeof(buf1), buf2, sizeof(buf2)); + fprintf(stdout, "\nFORMAT%s%s%s%s", g_szOutputSeparator, buf1, g_szOutputSeparator, buf2); + } else { + fprintf(stdout, "\nEncryptedData - Id=%s Type=%s MimeType=%s EncryptionMethod=%s", + (pEncData->szId ? pEncData->szId : ""), + (pEncData->szType ? pEncData->szType : ""), + (pEncData->szMimeType ? pEncData->szMimeType : ""), + (pEncData->szEncryptionMethod ? pEncData->szEncryptionMethod : "")); + err = dencMetaInfo_GetLibVersion(pEncData, buf1, sizeof(buf1), buf2, sizeof(buf2)); + fprintf(stdout, "\n\tLIBRARY: %s VERSION: %s", buf1, buf2); + err = dencMetaInfo_GetFormatVersion(pEncData, buf1, sizeof(buf1), buf2, sizeof(buf2)); + fprintf(stdout, "\n\tFORMAT: %s VERSION: %s", buf1, buf2); + } + for(k = 0; k < pEncData->nEncryptedKeys; k++) { + DEncEncryptedKey *pEncKey = pEncData->arrEncryptedKeys[k]; + if(g_cgiMode) + fprintf(stdout, "\nEncryptedKey%s%s%s%s%s%s%s%s%s%s%s%s", + g_szOutputSeparator, (pEncKey->szId ? pEncKey->szId : ""), + g_szOutputSeparator, (pEncKey->szRecipient ? pEncKey->szRecipient : ""), + g_szOutputSeparator, (pEncKey->szKeyName ? pEncKey->szKeyName : ""), + g_szOutputSeparator, (pEncKey->szCarriedKeyName ? pEncKey->szCarriedKeyName : ""), + g_szOutputSeparator, (pEncKey->szEncryptionMethod ? pEncKey->szEncryptionMethod : ""), + g_szOutputSeparator, (pEncKey->pCert ? "OK" : "NULL")); + else + fprintf(stdout, "\nEncryptedKey: Id=%s Recipient=%s KeyName=%s CarriedKeyName=%s EncryptionMethod=%s Cert: %s", + (pEncKey->szId ? pEncKey->szId : ""), + (pEncKey->szRecipient ? pEncKey->szRecipient : ""), + (pEncKey->szKeyName ? pEncKey->szKeyName : ""), + (pEncKey->szCarriedKeyName ? pEncKey->szCarriedKeyName : ""), + (pEncKey->szEncryptionMethod ? pEncKey->szEncryptionMethod : ""), + (pEncKey->pCert ? "OK" : "NULL")); + } + if(pEncData->encProperties.nEncryptionProperties > 0) { + if(g_cgiMode) + fprintf(stdout, "\nEncryptionProperties%s%s", + g_szOutputSeparator, (pEncData->encProperties.szId ? pEncData->encProperties.szId : "")); + else + fprintf(stdout, "\nEncryptionProperties: %s", + (pEncData->encProperties.szId ? pEncData->encProperties.szId : "")); + for(k = 0; k < pEncData->encProperties.nEncryptionProperties; k++) { + DEncEncryptionProperty *pEncProp = pEncData->encProperties.arrEncryptionProperties[k]; + if(g_cgiMode) + fprintf(stdout, "\nEncryptionProperty%s%s%s%s%s%s%s%s\n", + g_szOutputSeparator, (pEncProp->szId ? pEncProp->szId : ""), + g_szOutputSeparator, (pEncProp->szTarget ? pEncProp->szTarget : ""), + g_szOutputSeparator, (pEncProp->szName ? pEncProp->szName : ""), + g_szOutputSeparator, (pEncProp->szContent ? pEncProp->szContent : "")); + else + fprintf(stdout, "\nEncryptionProperty Id=%s Target=%s Name=%s Content=%s\n", + (pEncProp->szId ? pEncProp->szId : ""), + (pEncProp->szTarget ? pEncProp->szTarget : ""), + (pEncProp->szName ? pEncProp->szName : ""), + (pEncProp->szContent ? pEncProp->szContent : "")); + } + } + return err; +} + +//-------------------------------------------------- +// Add ecryption recipients +//-------------------------------------------------- +int runDEncListCmds(int argc, char** argv) +{ + int err = ERR_OK, i; + DEncEncryptedData* pEncData = 0; + + for(i = 1; (err == ERR_OK) && (i < argc); i++) { + if(argv[i][0] == '-') { // all commands and options must start with - + // add a recipients key + if(!strcmp(argv[i], "-denc-list")) { + if(argc > i+1 && argv[i+1][0] != '-') { + char* file = argv[i+1]; + err = dencSaxReadEncryptedData(&pEncData, file); + if(!err) + err = cmdListEncryptedData(pEncData); + if(pEncData && !err) + dencEncryptedData_free(pEncData); + } else { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "Missing argument of -denc-list command"); + } + } + } + } + return err; +} + +//-------------------------------------------------- +// decryption file +//-------------------------------------------------- +int runDecryptCmds(int argc, char** argv, DEncEncryptedData** ppEncData) +{ + int err = ERR_OK, i, bDecSk = 0, nSlot = 0, nKey = 0; + DEncEncryptedKey* pEncKey = 0; + FILE *hFile = 0; + SignedDoc *pSigDoc = 0; + DataFile *pDf = 0; + char fname1[256], *pkcs12file = 0, *infile = 0, *pin = 0, key[300]; + EVP_PKEY *pkey; +#ifdef WIN32 + wchar_t *convFileName = 0; + int l1 = 0; +#endif + + for(i = 1; (err == ERR_OK) && (i < argc); i++) { + if(argv[i][0] == '-') { // all commands and options must start with - + // decrypt file + if(!strcmp(argv[i], "-decrypt") || + !strcmp(argv[i], "-decrypt-hex") || + !strcmp(argv[i], "-decrypt-sk")) { + if(!strcmp(argv[i], "-decrypt-sk")) bDecSk = 1; + if(argc > i+3 && + argv[i+1][0] != '-' && + argv[i+2][0] != '-') { + ddocConvertInput(argv[i+1], &infile); + ddocConvertInput(argv[i+2], &pin); + nKey = 0; + memset(key, 0, sizeof(key)); + if(!strcmp(argv[i], "-decrypt-hex")) { + nKey = sizeof(key); + hex2bin(pin, (byte*)key, &nKey); + } + i += 2; + if(argc > i+1 && argv[i+1][0] != '-') { + ddocConvertInput(argv[i+1], &pkcs12file); + i++; + } + if(argc > i+2 && argv[i+2][0] != '-') { + nSlot = atoi(argv[i+2]); + i++; + } + ddocDebug(3, "runDecryptCmds", "Decrypt file: %s pin: %s pkcs12: %s slot: %d", infile, pin, pkcs12file, nSlot); + err = dencSaxReadEncryptedData(ppEncData, infile); + if(err) return err; + // if using hex key + if(nKey > 0) { + ddocDebug(3, "runDecryptCmds", "Decrypt with key: %s len: %d", pin, nKey); + err = dencEncryptedData_decrypt_withKey(*ppEncData, key, nKey); + } + // if using pkcs12 + else if(pkcs12file != NULL && strlen(pkcs12file) > 0) { + ddocDebug(3, "runDecryptCmds", "Opening pkcs12 file: %s", pkcs12file); + err = dencEncryptedData_findEncryptedKeyByPKCS12(*ppEncData, &pEncKey, &pkey, pkcs12file, pin); + } else { + ddocDebug(3, "runDecryptCmds", "Connecting to pkcs11, slot: %d", nSlot); + err = dencEncryptedData_findEncryptedKeyByPKCS11UsingSlot(*ppEncData, &pEncKey, nSlot); + } + if(!nKey) { + if(pEncKey) { + if(pkcs12file != NULL && strlen(pkcs12file) > 0) + err = dencEncryptedData_decryptWithKey(*ppEncData, pEncKey, pkey); + else + err = dencEncryptedData_decryptUsingSlot(*ppEncData, pEncKey, pin, nSlot); + if(err) return err; + } else { + err = ERR_DENC_DECRYPT; + addError(err, __FILE__, __LINE__, "No transport key found for your smartcard"); + } + } + // read in ddoc if necessary + if(bDecSk && p_szOutFile) { + sprintf(fname1, "%s.ddoc", p_szOutFile); + ddocDebug(3, "runDecryptCmds", "writing ddoc to: %s", fname1); +#ifdef WIN32 + err = utf82unicode((const char*)fname1, (char**)&convFileName, &l1); + ddocDebug(3, "ddocReadFile", "file: %s, conv-file: %s len: %d", fname1, convFileName, i); + if((hFile = _wfopen(convFileName, L"wb")) != NULL) { +#else + if((hFile = fopen(fname1, "wb")) != NULL) { +#endif + fwrite((*ppEncData)->mbufEncryptedData.pMem, (*ppEncData)->mbufEncryptedData.nLen, 1, hFile); + fclose(hFile); + ddocDebug(3, "runDecryptCmds", "Reading ddoc: %s", fname1); + err = ddocSaxReadSignedDocFromFile(&pSigDoc, fname1, 0, 0); + if(!err && getCountOfDataFiles(pSigDoc) > 0) { + pDf = getDataFile(pSigDoc, 0); + ddocDebug(3, "runDecryptCmds", "writing DF: %s to: %s", pDf->szId, p_szOutFile); + err = ddocExtractDataFile(pSigDoc, fname1, p_szOutFile, pDf->szId, "UTF-8"); + dencEncryptedData_free(*ppEncData); + *ppEncData = NULL; + p_szOutFile = NULL; + } + SignedDoc_free(pSigDoc); + if(fname1[0] && !remove(fname1)) + ddocDebug(3, "runDecryptCmds", "error deleting file: %", fname1); + } + } + // p_szOutFile + } else { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "Missing or argument of -decrypt command"); + } + } + } + } + return err; +} + +//-------------------------------------------------- +// Create digidoc and adds datafiles +//-------------------------------------------------- +int runExtractCmds(int argc, char** argv, SignedDoc** ppSigDoc) +{ + int err = ERR_OK, i; + + for(i = 1; (err == ERR_OK) && (i < argc); i++) { + if(argv[i][0] == '-') { // all commands and options must start with - + // add a DataFile + if(!strcmp(argv[i], "-extract")) { + if(argc > i+2 && argv[i+1][0] != '-' && argv[i+2][0] != '-') { + char* id = NULL; + char* file = NULL; + char* charset = CHARSET_UTF_8; + checkArguments(argc, argv, &i, &id); + checkArguments(argc, argv, &i, &file); + // optional charset & filenamecharset + //checkArguments(argc, argv, &i, &charset); + err = ddocExtractDataFile(*ppSigDoc, (const char*)p_szInFile, (const char*)file, + (const char*)id, (const char*)charset); + // report success + if(g_cgiMode) + fprintf(stdout, "\nDataFile%s%s%s%s%s%d", g_szOutputSeparator, id, + g_szOutputSeparator, file, g_szOutputSeparator, err); + else + fprintf(stdout, "\nDataFile id: %s path: %s rc: %d", id, file, err); + + if(id) freeLibMem(id); + if(file) freeLibMem(file); + + } else { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "Missing argument of -extract command"); + } + } + } + } + return err; +} + +//-------------------------------------------------- +// Create digidoc and adds datafiles +//-------------------------------------------------- +int runExtractMemCmds(int argc, char** argv, SignedDoc** ppSigDoc) +{ + int err = ERR_OK, i; + char* pBuf = 0; + long nLen = 0; + FILE* hFile = 0; + + for(i = 1; (err == ERR_OK) && (i < argc); i++) { + if(argv[i][0] == '-') { // all commands and options must start with - + // extract DataFile + if(!strcmp(argv[i], "-extract-mem")) { + if(argc > i+2 && argv[i+1][0] != '-' && argv[i+2][0] != '-') { + char* id = NULL; + char* file = NULL; + checkArguments(argc, argv, &i, &id); + checkArguments(argc, argv, &i, &file); + err = ddocGetDataFileCachedData(*ppSigDoc, id, (void**)&pBuf, &nLen); + printf("\nExtract DF: %s, err: %d data: %ld", id, err, nLen); + if(file && (hFile = fopen(file, "wb")) != NULL && pBuf && nLen) + fwrite(pBuf, nLen, 1, hFile); + if(id) freeLibMem(id); + if(file) freeLibMem(file); + if(hFile) fclose(hFile); + } else { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "Missing argument of -extract command"); + } + } + } + } + if(pBuf) + freeLibMem(pBuf); + if(hFile) + fclose(hFile); + return err; +} + + +//-------------------------------------------------- +// Verfys sigantures and notaries +//-------------------------------------------------- +int runVerifyCmds(int argc, char** argv, SignedDoc* pSigDoc, DEncEncryptedData* pEncData) +{ + int err = ERR_OK, i; + + for(i = 1; !err && (i < argc); i++) { + if(argv[i][0] == '-') { // all commands and options must start with - + // create new digidoc + if(!strcmp(argv[i], "-verify")) { + if(pSigDoc) { + err = cmdVerify(pSigDoc); + } + if(pEncData) + err = dencValidate(pEncData); + } + } + } + return err; +} + + +//-------------------------------------------------- +// Handles signature commands +//-------------------------------------------------- +int runSignCmds(int argc, char** argv, SignedDoc** ppSigDoc) +{ + int err = ERR_OK, i, nManif, nSlot = 0, nOcsp = 1, nSigner = 1; + + for(i = 1; !err && (i < argc); i++) { + if(argv[i][0] == '-') { // all commands and options must start with - + // create new digidoc + if(!strcmp(argv[i], "-sign")) { + char* pin = NULL; + char* manifest = NULL; + char* city = NULL; + char* state = NULL; + char* zip = NULL; + char* country = NULL; + char* sSlot = NULL; + char* sOcsp = NULL; + char* sSigner = NULL; + char* szPkcs12File = NULL; + checkArguments(argc, argv, &i, &pin); + // if pin is NULL check for autosign pin + if(!pin) + pin = (char*)ConfigItem_lookup("AUTOSIGN_PIN"); + if(!pin) { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "Missing argument of -sign command"); + return err; + } + // check config file + nManif = ConfigItem_lookup_int("MANIFEST_MODE", 0); + if(nManif == 2) { + manifest = (char*)ConfigItem_lookup("DIGIDOC_ROLE_MANIFEST"); + country = (char*)ConfigItem_lookup("DIGIDOC_ADR_COUNTRY"); + state = (char*)ConfigItem_lookup("DIGIDOC_ADR_STATE"); + city = (char*)ConfigItem_lookup("DIGIDOC_ADR_CITY"); + zip = (char*)ConfigItem_lookup("DIGIDOC_ADR_ZIP"); + } + // optional mainfest argument + checkArguments(argc, argv, &i, &manifest); + // optional address + checkArguments(argc, argv, &i, &city); + checkArguments(argc, argv, &i, &state); + checkArguments(argc, argv, &i, &zip); + checkArguments(argc, argv, &i, &country); + checkArguments(argc, argv, &i, &sSlot); + checkArguments(argc, argv, &i, &sOcsp); + checkArguments(argc, argv, &i, &sSigner); + if(sSlot) + nSlot = atoi(sSlot); + if(sOcsp) + nOcsp = atoi(sOcsp); + if(sSigner) { + if(!strcmp(sSigner, "PKCS11")) nSigner = 1; + if(!strcmp(sSigner, "PKCS12")) nSigner = 3; +#ifdef WIN32 + if(!strcmp(sSigner, "CNG")) nSigner = 2; +#endif + } + checkArguments(argc, argv, &i, &szPkcs12File); + err = cmdSign(*ppSigDoc, (const char*)pin, (const char*)manifest, + (const char*)city, (const char*)state, (const char*)zip, + (const char*)country, nSlot, nOcsp, nSigner, szPkcs12File); + if(manifest) freeLibMem(manifest); + if(city) freeLibMem(city); + if(state) freeLibMem(state); + if(zip) freeLibMem(zip); + if(country) freeLibMem(country); + if(sSlot) freeLibMem(sSlot); + if(sOcsp) freeLibMem(sOcsp); + if(sSigner) freeLibMem(sSigner); + if(szPkcs12File) freeLibMem(szPkcs12File); + } + } + } + return err; +} + + +//-------------------------------------------------- +// Handles commands related to checking certificates +//-------------------------------------------------- +int runCheckCertCmds(int argc, char** argv) +{ + int err = ERR_OK, i; + char* szCertFile = 0; + X509* pCert = 0; + DigiDocMemBuf mbuf; + + mbuf.pMem = 0; + mbuf.nLen = 0; + for(i = 1; !err && (i < argc); i++) { + if(!strcmp(argv[i], "-check-cert") && + argv[i+1][0] != '-') { + szCertFile = argv[i+1]; + i++; + if(szCertFile) { + err = ReadCertificate(&pCert, szCertFile); + if(!err && pCert) { + err = ddocVerifyCertByOCSP(pCert, NULL); + err = ddocCertGetSubjectCN(pCert, &mbuf); + printf("Verifying cert: %s --> RC :%d\n", (const char*)mbuf.pMem, err); + ddocMemBuf_free(&mbuf); + } + if(pCert) { + X509_free(pCert); + pCert = 0; + } + } + } + } + return err; +} + +//-------------------------------------------------- +// Handles commands related to adding signature value +// from a file containging base64 encoded RSA-SHA1 signature value +//-------------------------------------------------- +int runAddSignValueCmds(int argc, char** argv, SignedDoc* pSigDoc) +{ + int err = ERR_OK, i; + char *szSignFile = 0, *szSignId = 0; + SignatureInfo* pSignInfo = 0; + + for(i = 1; !err && (i < argc); i++) { + if(!strcmp(argv[i], "-add-sign-value") && i < argc - 2 && + argv[i+1][0] != '-' && argv[i+2][0] != '-') { + checkArguments(argc, argv, &i, &szSignFile); + checkArguments(argc, argv, &i, &szSignId); + if(!pSigDoc) { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "No digidoc document read in yet!"); + return err; + } + if(szSignFile && szSignId) { + pSignInfo = getSignatureWithId(pSigDoc, szSignId); + if(pSignInfo) { + // read the file + err = setSignatureValueFromFile(pSignInfo, szSignFile); + } else { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "Wrong signature id"); + } + // report on success + if(g_cgiMode) + fprintf(stdout, "\nAddSignValue%s%s%s%s%s%d", g_szOutputSeparator, szSignId, + g_szOutputSeparator, szSignFile, g_szOutputSeparator, err); + else + fprintf(stdout, "\nAddSignValue id: %s path: %s rc: %d", szSignId, szSignFile, err); + } else { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "Missing or argument of -add-sign-value command"); + } + } + } + if(szSignFile) freeLibMem(szSignFile); + if(szSignId) freeLibMem(szSignId); + return err; +} + + +//-------------------------------------------------- +// Handles commands related to deleting signatures +//-------------------------------------------------- +int runDelSignCmds(int argc, char** argv, SignedDoc* pSigDoc) +{ + int err = ERR_OK, i; + char *szSignId = 0; + SignatureInfo* pSignInfo = 0; + + for(i = 1; !err && (i < argc); i++) { + if(!strcmp(argv[i], "-del-sign") && i < argc - 1 && + argv[i+1][0] != '-') { + checkArguments(argc, argv, &i, &szSignId); + if(!pSigDoc) { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "No digidoc document read in yet!"); + return err; + } + if(szSignId) { + pSignInfo = getSignatureWithId(pSigDoc, szSignId); + if(pSignInfo) { + err = SignatureInfo_delete(pSigDoc, szSignId); + // report on success + if(g_cgiMode) + fprintf(stdout, "\nDelSign%s%s%s%d", g_szOutputSeparator, szSignId, + g_szOutputSeparator, err); + else + fprintf(stdout, "\nDelSign id: %s rc: %d", szSignId, err); + + } else { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "wrong signature id!"); + } + } else { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "Missing argument of -del-sign command"); + } + } + } + if(szSignId) freeLibMem(szSignId); + return err; +} + + +//-------------------------------------------------- +// Handles commands related to notarizing signatures +//-------------------------------------------------- +int runGetConfirmationCmds(int argc, char** argv, SignedDoc* pSigDoc) +{ + int err = ERR_OK, i; + char *szSignId = 0; + SignatureInfo* pSignInfo = 0; + + for(i = 1; !err && (i < argc); i++) { + if(!strcmp(argv[i], "-get-confirmation") && i < argc - 1 && + argv[i+1][0] != '-') { + checkArguments(argc, argv, &i, &szSignId); + if(!pSigDoc) { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "No digidoc document read in yet!"); + return err; + } + if(szSignId) { + pSignInfo = getSignatureWithId(pSigDoc, szSignId); + if(pSignInfo) { + err = notarizeSignature(pSigDoc, pSignInfo); + // report on success + if(g_cgiMode) + fprintf(stdout, "\nNotarizeSignature%s%s%s%d", g_szOutputSeparator, szSignId, + g_szOutputSeparator, err); + else + fprintf(stdout, "\nNotarizeSignature id: %s rc: %d", szSignId, err); + + } else { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "wrong signature id!"); + } + if(szSignId) freeLibMem(szSignId); + } else { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "Missing argument of -get-confirmation command"); + } + } + } + return err; +} + + +//-------------------------------------------------- +// Signs the document and gets configrmation +//-------------------------------------------------- +int cmdCalcSign(SignedDoc* pSigDoc, const char* manifest, + const char* city, const char* state, const char* zip, + const char* country, const char* certFile) +{ + int err = ERR_OK, l1; + SignatureInfo* pSigInfo = NULL; + X509* pCert = NULL; + char buf1[50]; + + ddocDebug(3, "cmdCalcSign", "Creating new digital signature"); + RETURN_IF_NULL_PARAM(pSigDoc); + RETURN_IF_NULL_PARAM(certFile); + // read certificate + // this certificate will nto be released since new signature takes ownership! + err = ReadCertificate(&pCert, certFile); + RETURN_IF_NOT(err == ERR_OK, err); + // prepare signature + err = ddocPrepareSignature(pSigDoc, &pSigInfo, + manifest, city, state, zip, country, pCert, NULL); + // base64 encode final hash + l1 = sizeof(buf1); + err = ddocGetSignedHash(pSigInfo, buf1, &l1, 2, 0); + RETURN_IF_NOT(err == ERR_OK, err); + // report on success + if(g_cgiMode) + fprintf(stdout, "\nSignatureHash%s%s%s%s%s%d", g_szOutputSeparator, pSigInfo->szId, + g_szOutputSeparator, buf1, g_szOutputSeparator, err); + else + fprintf(stdout, "\nSignatureHash id: %s hash: %s rc: %d", pSigInfo->szId, buf1, err); + + + RETURN_IF_NOT(err == ERR_OK, err); + return ERR_OK; +} + +//-------------------------------------------------- +// Handles signature commands +//-------------------------------------------------- +int runCalcSignCmds(int argc, char** argv, SignedDoc* pSigDoc) +{ + int err = ERR_OK, i, nManif; + + for(i = 1; !err && (i < argc); i++) { + if(argv[i][0] == '-') { // all commands and options must start with - + // create new digidoc + if(!strcmp(argv[i], "-calc-sign")) { + char* certFile = NULL; + char* manifest = NULL; + char* city = NULL; + char* state = NULL; + char* zip = NULL; + char* country = NULL; + checkArguments(argc, argv, &i, &certFile); + // check config file + nManif = ConfigItem_lookup_int("MANIFEST_MODE", 0); + if(nManif == 2) { + manifest = (char*)ConfigItem_lookup("DIGIDOC_ROLE_MANIFEST"); + country = (char*)ConfigItem_lookup("DIGIDOC_ADR_COUNTRY"); + state = (char*)ConfigItem_lookup("DIGIDOC_ADR_STATE"); + city = (char*)ConfigItem_lookup("DIGIDOC_ADR_CITY"); + zip = (char*)ConfigItem_lookup("DIGIDOC_ADR_ZIP"); + } + // optional mainfest argument + checkArguments(argc, argv, &i, &manifest); + // optional address + checkArguments(argc, argv, &i, &city); + checkArguments(argc, argv, &i, &state); + checkArguments(argc, argv, &i, &zip); + checkArguments(argc, argv, &i, &country); + if(!certFile) { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "Missing argument of -calc-sign command"); + } + else + err = cmdCalcSign(pSigDoc, (const char*)manifest, + (const char*)city, (const char*)state, (const char*)zip, + (const char*)country, (const char*)certFile); + if(manifest) freeLibMem(manifest); + if(city) freeLibMem(city); + if(state) freeLibMem(state); + if(zip) freeLibMem(zip); + if(country) freeLibMem(country); + if(certFile) freeLibMem(certFile); + } + } + } + return err; +} + + +//-------------------------------------------------- +// Signs the document and gets configrmation +//-------------------------------------------------- +int cmdMidSign(SignedDoc* pSigDoc, const char* szPhoneNo, + const char* szIdCode, const char* szLang, + const char* szService, + const char* manifest, const char* city, + const char* state, const char* zip, + const char* country) +{ + int err = ERR_OK, nPollFreq = 0, l1, nStatus = 0; + char* szUrl = 0, *szProxyHost = 0, *szProxyPort = 0; + long lSesscode = 0; + char buf1[10]; + DigiDocMemBuf mbuf1; +#ifdef WIN32 + time_t tNew, tOld; +#endif + + mbuf1.pMem = 0; + mbuf1.nLen = 0; + nPollFreq = ConfigItem_lookup_int("DDS_POLLFREQ", 0); + szUrl = (char*)ConfigItem_lookup("DDS_URL"); + szProxyHost = (char*)ConfigItem_lookup("DDS_PROXY_HOST"); + szProxyPort = (char*)ConfigItem_lookup("DDS_PROXY_PORT"); + ddocDebug(3, "cmdMidSign", "Creating MID signature using: %s poll freq: %d", szPhoneNo, nPollFreq); + // send signature req + l1 = sizeof(buf1); + ddsSign(pSigDoc, szIdCode, szPhoneNo, szLang, szService, + manifest, city, state, zip, country, szUrl, szProxyHost, szProxyPort, + &lSesscode, buf1, l1); + + // report on success + if(g_cgiMode) + fprintf(stdout, "\nMIDSinatureReq%s%ld%s%d", + g_szOutputSeparator, lSesscode, + g_szOutputSeparator, err); + else + fprintf(stdout, "\nMIDSignatureReq session: %ld rc: %d", lSesscode, err); + // if we should keep on polling + if(!err && nPollFreq) { + // now check status + do { +#ifdef WIN32 + time(&tOld); + do { + time(&tNew); + } while(tNew - tOld < nPollFreq); +#else + ddocDebug(3, "cmdMidSign", "Sleep: %d", nPollFreq); + sleep(nPollFreq); +#endif + err = ddsGetStatusWithFile(pSigDoc, lSesscode, szUrl, szProxyHost, szProxyPort, &nStatus, p_szInFile); + ddocDebug(3, "cmdMidSign", "Txn: %ld status: %d err: %d", lSesscode, nStatus, err); + } while(!err && nStatus == 1); + } + if(g_cgiMode) + fprintf(stdout, "\nMID session %s%ld%s%d", + g_szOutputSeparator, lSesscode, + g_szOutputSeparator, nStatus); + else + fprintf(stdout, "\nMID session %ld complete status: %d", lSesscode, nStatus); + + RETURN_IF_NOT(err == ERR_OK, err); + return ERR_OK; +} + + +//-------------------------------------------------- +// Handles MID signature commands +//-------------------------------------------------- +int runMidSignCmds(int argc, char** argv, SignedDoc* pSigDoc) +{ + int err = ERR_OK, i, nManif = 0; + + for(i = 1; !err && (i < argc); i++) { + if(argv[i][0] == '-') { // all commands and options must start with - + // create new digidoc + if(!strcmp(argv[i], "-mid-sign")) { + char* phoneNo = NULL; + char* idcode = NULL; + char* service = NULL; + char* lang = NULL; + char* manifest = NULL; + char* city = NULL; + char* state = NULL; + char* zip = NULL; + char* country = NULL; + checkArguments(argc, argv, &i, &phoneNo); + // check config file + nManif = ConfigItem_lookup_int("MANIFEST_MODE", 0); + if(nManif == 2) { + manifest = (char*)ConfigItem_lookup("DIGIDOC_ROLE_MANIFEST"); + country = (char*)ConfigItem_lookup("DIGIDOC_ADR_COUNTRY"); + state = (char*)ConfigItem_lookup("DIGIDOC_ADR_STATE"); + city = (char*)ConfigItem_lookup("DIGIDOC_ADR_CITY"); + zip = (char*)ConfigItem_lookup("DIGIDOC_ADR_ZIP"); + } + // optional mainfest argument + checkArguments(argc, argv, &i, &idcode); + checkArguments(argc, argv, &i, &country); + checkArguments(argc, argv, &i, &lang); + checkArguments(argc, argv, &i, &service); + checkArguments(argc, argv, &i, &manifest); + checkArguments(argc, argv, &i, &city); + checkArguments(argc, argv, &i, &state); + checkArguments(argc, argv, &i, &zip); + if(!phoneNo) { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "Missing argument of -mid-sign command"); + } + else + cmdMidSign(pSigDoc, (const char*)phoneNo, (const char*)idcode, + (const char*)(lang ? lang : "EST"), + (const char*)(service ? service : "Testimine"), (const char*)manifest, + (const char*)city, (const char*)state, (const char*)zip, + (const char*)(country ? country : "EE")); + if(manifest) freeLibMem(manifest); + if(city) freeLibMem(city); + if(state) freeLibMem(state); + if(zip) freeLibMem(zip); + if(country) freeLibMem(country); + if(phoneNo) freeLibMem(phoneNo); + } + } + } + return err; +} + + +//-------------------------------------------------- +// Handles MID signature commands +//-------------------------------------------------- +int runMidTestCmds(int argc, char** argv, SignedDoc* pSigDoc) +{ + int err = ERR_OK, i, e1; + SignedDoc *pSdoc = NULL; + DataFile *pDf = NULL; + long lSize1, lSize2, lSize3; + + for(i = 1; !err && (i < argc); i++) { + if(argv[i][0] == '-') { // all commands and options must start with - + // create new digidoc + if(!strcmp(argv[i], "-mid-test")) { + char* infile = NULL; + char* inmime = NULL; + char* outddoc = NULL; + char* phoneNo = NULL; + char* idcode = NULL; + char* service = NULL; + checkArguments(argc, argv, &i, &infile); + checkArguments(argc, argv, &i, &inmime); + checkArguments(argc, argv, &i, &outddoc); + checkArguments(argc, argv, &i, &phoneNo); + checkArguments(argc, argv, &i, &idcode); + checkArguments(argc, argv, &i, &service); + if(!phoneNo || !idcode || !infile || !inmime || !outddoc) { + err = ERR_BAD_PARAM; + addError(err, __FILE__, __LINE__, "Missing or argument of -mid-test command"); + } else { + p_szInFile = outddoc; + printf("Creating ddoc: %s of file: %s mime: %s\n", outddoc, infile, inmime); + err = SignedDoc_new(&pSdoc, "DIGIDOC-XML", "1.3"); + printf("Creating ddoc: %s rc: %d\n", outddoc, err); + RETURN_IF_NOT(err == ERR_OK, err); + err = calculateFileSize(infile, &lSize1); + err = DataFile_new(&pDf, pSdoc, NULL, infile, "EMBEDDED_BASE64", inmime, 0, NULL, 0, NULL, NULL); + err = createSignedDoc(pSdoc, NULL, outddoc); + if(pDf->nSize == 0 && lSize1 > 0) + pDf->nSize = lSize1; + err = calculateFileSize(outddoc, &lSize2); + printf("Add data-file: %s rc: %d d-size: %ld ddoc-size: %ld\n", infile, err, lSize1, lSize2); + err = ddocExtractDataFile(pSdoc, outddoc, "D0.dat", "D0", "UTF-8"); + err = calculateFileSize("D0.dat", &lSize3); + printf("Extract before signing rc: %d size: %ld\n", err, lSize3); + printf("MID signing phone: %s id-code: %s\n", phoneNo, idcode); + err = cmdMidSign(pSdoc, (const char*)phoneNo, (const char*)idcode, + "EST", (service ? service : "Testimine"), NULL, NULL, NULL, NULL, "EE"); + err = calculateFileSize(outddoc, &lSize2); + printf("MID signed phone: %s id-code: %s ddoc-size: %ld rc: %d\n", phoneNo, idcode, lSize2, err); + err = cmdVerify(pSdoc); + printf("\nVerify after signing rc: %d\n", err); + lSize3 = 0; + err = ddocExtractDataFile(pSdoc, outddoc, "D0.dat", "D0", "UTF-8"); + err = calculateFileSize("D0.dat", &lSize3); + printf("Extract after signing rc: %d size: %ld\n", err, lSize3); + } + SignedDoc_free(pSdoc); + if(infile) freeLibMem(infile); + if(inmime) freeLibMem(inmime); + if(outddoc) freeLibMem(outddoc); + if(phoneNo) freeLibMem(phoneNo); + if(idcode) freeLibMem(idcode); + if(service) freeLibMem(service); + } + } + } + return err; +} + + + + +//-------------------------------------------------- +// Program main function +//-------------------------------------------------- +int main(int argc, char** argv) +{ + int err = ERR_OK, e = 0; + SignedDoc* pSigDoc = 0; + DEncEncryptedData* pEncData = 0; + time_t t1, t2; +#ifdef WIN32 + char buf1[250]; +#endif + + if(argc <= 1) { + printUsage(); + exit(0); + } + // init DigiDoc library + initDigiDocLib(); +#ifdef WIN32 + // find out program home if invoked with a long command line + memset(buf1, 0, sizeof(buf1)); + getFileNamePath(argv[0], buf1, sizeof(buf1)); + if(strlen(buf1)) { + strncat(buf1, "digidoc.ini", sizeof(buf1)); + err = initConfigStore(buf1); + } else + err = initConfigStore(NULL); +#else + // read in config file + err = initConfigStore(NULL); +#endif + // clear all errors that were encountered by not finding config files + err = checkProgArguments(argc, argv); + time(&t1); + // register program name and version + setGUIVersion(g_szProgNameVer); + ddocDebugTruncateLog(); + // use command line argument if required + if(p_szConfigFile) + err = readConfigFile(p_szConfigFile, ITEM_TYPE_PRIVATE); + // read flags from config file + readConfigParams(); + + // display programm name and version + if(g_cgiMode) { + if(ConfigItem_lookup_bool("DIGIDOC_CGI_PRINT_HEADER", 1)) + fprintf(stdout, "%s%s%s", getLibName(), g_szOutputSeparator, getLibVersion()); + } else { + fprintf(stdout, "%s - %s", getLibName(), getLibVersion()); + } + + // execute the commands now + // check certificate status throught OCSP + if(!err) + err = runCheckCertCmds(argc, argv); + // read input file if necessary + if(p_szInFile && !err) { + if(p_parseMode != 3) + err = cmdReadDigiDoc(&pSigDoc, &pEncData, p_parseMode); + else + err = cmdReadDigiDocFromMem(&pSigDoc, &pEncData); + // if read error was warning then continue + if(pSigDoc && isWarning(pSigDoc, err)) + err = ERR_OK; + if(err && pSigDoc) + printErrorsAndWarnings(pSigDoc); + } + // various tests + if(!err) + err = runTestCmds(argc, argv); + if(p_szInEncFile && !err) { + if(hasCmdLineArg(argc, argv, "-encrypt-sk")) + err = cmdEncryptSk(&pEncData, p_szInEncFile); + else + err = cmdEncrypt(&pEncData, p_szInEncFile); + } + // list encrypted files + if(!err) + err = runDEncListCmds(argc, argv); + // add data files + if(!err) + err = runAddCmds(argc, argv, &pSigDoc); + // add data files from mem + if(!err) + err = runAddMemCmds(argc, argv, &pSigDoc); + // add recipients + if(!err) + err = runRecipientCmds(argc, argv, &pEncData); + // encrypt whole files + if(!err) + err = runEncryptFileCmds(argc, argv, pEncData); + // decrypt whole files + if(!err) + err = runDecryptFileCmds(argc, argv); + // run signature commands + if(!err) + err = runSignCmds(argc, argv, &pSigDoc); + // calculate signature hash commands + if(!err) + err = runCalcSignCmds(argc, argv, pSigDoc); + if(!err) + err = runMidSignCmds(argc, argv, pSigDoc); + if(!err) + err = runMidTestCmds(argc, argv, pSigDoc); + // verify signatures + if(!err) { + e = runVerifyCmds(argc, argv, pSigDoc, pEncData); + if(!err && e) err = e; + } + // extract datafiles + if(!err) + err = runExtractCmds(argc, argv, &pSigDoc); + if(!err) + err = runExtractMemCmds(argc, argv, &pSigDoc); + // decrypt files + if(!err) + err = runDecryptCmds(argc, argv, &pEncData); + // add signature value from a file + if(!err) + err = runAddSignValueCmds(argc, argv, pSigDoc); + // delete signatures + if(!err) + err = runDelSignCmds(argc, argv, pSigDoc); + // notarize signatures + if(!err) + err = runGetConfirmationCmds(argc, argv, pSigDoc); + if(!err) + err = runEncryptTestSetCmds(argc, argv); + // write output file + if(p_szOutFile && !err) + if(p_parseMode != 3) + err = cmdWrite(pSigDoc, pEncData); + else + err = cmdWriteMem(pSigDoc, pEncData); + time(&t2); + + printErrorsAndWarnings(pSigDoc); + if(pSigDoc && !isError(pSigDoc, err)) { // find correct error from list + e = hasErrors(pSigDoc); + if((!err || !isError(pSigDoc,err)) && e) err = e; + } + // display programm error code and elapsed time + if(g_cgiMode) { + if(ConfigItem_lookup_bool("DIGIDOC_CGI_PRINT_TRAILER", 1)) + fprintf(stdout, "\n%s%s%d%s%ld", getLibName(), g_szOutputSeparator, (isError(pSigDoc,err) ? err : 0), g_szOutputSeparator, (long)(t2-t1)); + } else { + fprintf(stdout, "\n%s - time: %ld sec result: %s", getLibName(), (long)(t2-t1), (isError(pSigDoc,err) ? "failure" : "success")); + } + fprintf(stdout, "\n"); + // cleanup + if(pSigDoc) + SignedDoc_free(pSigDoc); + if(pEncData) + dencEncryptedData_free(pEncData); + if(g_szOutputSeparator) + free(g_szOutputSeparator); + // cleanup +#ifndef WIN32 // TODO: somehow this free gives invalid heap error on win32 + cleanupConfigStore(NULL); +#endif + finalizeDigiDocLib(); + + return err; +} -- cgit v1.2.3