signver.c (10168B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "secutil.h" 6 #include "secmod.h" 7 #include "cert.h" 8 #include "secoid.h" 9 #include "nss.h" 10 11 /* NSPR 2.0 header files */ 12 #include "prinit.h" 13 #include "prprf.h" 14 #include "prsystem.h" 15 #include "prmem.h" 16 /* Portable layer header files */ 17 #include "plstr.h" 18 #include "sechash.h" /* for HASH_GetHashObject() */ 19 20 static PRBool debugInfo; 21 static PRBool verbose; 22 static PRBool doVerify; 23 static PRBool displayAll; 24 25 static const char *const usageInfo[] = { 26 "signver - verify a detached PKCS7 signature - Version " NSS_VERSION, 27 "Commands:", 28 " -A display all information from pkcs #7", 29 " -V verify the signed object and display result", 30 "Options:", 31 " -a signature file is ASCII", 32 " -d certdir directory containing cert database", 33 " -i dataFileName input file containing signed data (default stdin)", 34 " -o outputFileName output file name, default stdout", 35 " -s signatureFileName input file for signature (default stdin)", 36 " -v display verbose reason for failure" 37 }; 38 static int nUsageInfo = sizeof(usageInfo) / sizeof(char *); 39 40 extern int SV_PrintPKCS7ContentInfo(FILE *, SECItem *); 41 42 static void 43 Usage(char *progName, FILE *outFile) 44 { 45 int i; 46 fprintf(outFile, "Usage: %s [ commands ] options\n", progName); 47 for (i = 0; i < nUsageInfo; i++) 48 fprintf(outFile, "%s\n", usageInfo[i]); 49 exit(-1); 50 } 51 52 static HASH_HashType 53 AlgorithmToHashType(SECAlgorithmID *digestAlgorithms) 54 { 55 SECOidTag tag = SECOID_GetAlgorithmTag(digestAlgorithms); 56 HASH_HashType hash = HASH_GetHashTypeByOidTag(tag); 57 return hash; 58 } 59 60 static SECStatus 61 DigestContent(SECItem *digest, SECItem *content, HASH_HashType hashType) 62 { 63 unsigned int maxLen = digest->len; 64 unsigned int len = HASH_ResultLen(hashType); 65 SECStatus rv; 66 67 if (len > maxLen) { 68 PORT_SetError(SEC_ERROR_OUTPUT_LEN); 69 return SECFailure; 70 } 71 72 rv = HASH_HashBuf(hashType, digest->data, content->data, content->len); 73 if (rv == SECSuccess) 74 digest->len = len; 75 return rv; 76 } 77 78 enum { 79 cmd_DisplayAllPCKS7Info = 0, 80 cmd_VerifySignedObj 81 }; 82 83 enum { 84 opt_ASCII, 85 opt_CertDir, 86 opt_InputDataFile, 87 opt_OutputFile, 88 opt_InputSigFile, 89 opt_PrintWhyFailure, 90 opt_DebugInfo 91 }; 92 93 static secuCommandFlag signver_commands[] = { 94 { /* cmd_DisplayAllPCKS7Info*/ 'A', PR_FALSE, 0, PR_FALSE }, 95 { /* cmd_VerifySignedObj */ 'V', PR_FALSE, 0, PR_FALSE } 96 }; 97 98 static secuCommandFlag signver_options[] = { 99 { /* opt_ASCII */ 'a', PR_FALSE, 0, PR_FALSE }, 100 { /* opt_CertDir */ 'd', PR_TRUE, 0, PR_FALSE }, 101 { /* opt_InputDataFile */ 'i', PR_TRUE, 0, PR_FALSE }, 102 { /* opt_OutputFile */ 'o', PR_TRUE, 0, PR_FALSE }, 103 { /* opt_InputSigFile */ 's', PR_TRUE, 0, PR_FALSE }, 104 { /* opt_PrintWhyFailure */ 'v', PR_FALSE, 0, PR_FALSE }, 105 { /* opt_DebugInfo */ 0, PR_FALSE, 0, PR_FALSE, "debug" } 106 }; 107 108 int 109 main(int argc, char **argv) 110 { 111 PRFileDesc *contentFile = NULL; 112 PRFileDesc *signFile = PR_STDIN; 113 FILE *outFile = stdout; 114 char *progName; 115 SECStatus rv; 116 int result = 1; 117 SECItem pkcs7der, content; 118 secuCommand signver; 119 120 pkcs7der.data = NULL; 121 content.data = NULL; 122 123 signver.numCommands = sizeof(signver_commands) / sizeof(secuCommandFlag); 124 signver.numOptions = sizeof(signver_options) / sizeof(secuCommandFlag); 125 signver.commands = signver_commands; 126 signver.options = signver_options; 127 128 #ifdef XP_PC 129 progName = strrchr(argv[0], '\\'); 130 #else 131 progName = strrchr(argv[0], '/'); 132 #endif 133 progName = progName ? progName + 1 : argv[0]; 134 135 rv = SECU_ParseCommandLine(argc, argv, progName, &signver); 136 if (SECSuccess != rv) { 137 Usage(progName, outFile); 138 } 139 debugInfo = signver.options[opt_DebugInfo].activated; 140 verbose = signver.options[opt_PrintWhyFailure].activated; 141 doVerify = signver.commands[cmd_VerifySignedObj].activated; 142 displayAll = signver.commands[cmd_DisplayAllPCKS7Info].activated; 143 if (!doVerify && !displayAll) 144 doVerify = PR_TRUE; 145 146 /* Set the certdb directory (default is ~/.netscape) */ 147 rv = NSS_Init(SECU_ConfigDirectory(signver.options[opt_CertDir].arg)); 148 if (rv != SECSuccess) { 149 SECU_PrintPRandOSError(progName); 150 return result; 151 } 152 /* below here, goto cleanup */ 153 SECU_RegisterDynamicOids(); 154 155 /* Open the input content file. */ 156 if (signver.options[opt_InputDataFile].activated && 157 signver.options[opt_InputDataFile].arg) { 158 if (PL_strcmp("-", signver.options[opt_InputDataFile].arg)) { 159 contentFile = PR_Open(signver.options[opt_InputDataFile].arg, 160 PR_RDONLY, 0); 161 if (!contentFile) { 162 PR_fprintf(PR_STDERR, 163 "%s: unable to open \"%s\" for reading.\n", 164 progName, signver.options[opt_InputDataFile].arg); 165 goto cleanup; 166 } 167 } else 168 contentFile = PR_STDIN; 169 } 170 171 /* Open the input signature file. */ 172 if (signver.options[opt_InputSigFile].activated && 173 signver.options[opt_InputSigFile].arg) { 174 if (PL_strcmp("-", signver.options[opt_InputSigFile].arg)) { 175 signFile = PR_Open(signver.options[opt_InputSigFile].arg, 176 PR_RDONLY, 0); 177 if (!signFile) { 178 PR_fprintf(PR_STDERR, 179 "%s: unable to open \"%s\" for reading.\n", 180 progName, signver.options[opt_InputSigFile].arg); 181 goto cleanup; 182 } 183 } 184 } 185 186 if (contentFile == PR_STDIN && signFile == PR_STDIN && doVerify) { 187 PR_fprintf(PR_STDERR, 188 "%s: cannot read both content and signature from standard input\n", 189 progName); 190 goto cleanup; 191 } 192 193 /* Open|Create the output file. */ 194 if (signver.options[opt_OutputFile].activated) { 195 outFile = fopen(signver.options[opt_OutputFile].arg, "w"); 196 if (!outFile) { 197 PR_fprintf(PR_STDERR, "%s: unable to open \"%s\" for writing.\n", 198 progName, signver.options[opt_OutputFile].arg); 199 goto cleanup; 200 } 201 } 202 203 /* read in the input files' contents */ 204 rv = SECU_ReadDERFromFile(&pkcs7der, signFile, 205 signver.options[opt_ASCII].activated, PR_FALSE); 206 if (signFile != PR_STDIN) 207 PR_Close(signFile); 208 if (rv != SECSuccess) { 209 SECU_PrintError(progName, "problem reading PKCS7 input"); 210 goto cleanup; 211 } 212 if (contentFile) { 213 rv = SECU_FileToItem(&content, contentFile); 214 if (contentFile != PR_STDIN) 215 PR_Close(contentFile); 216 if (rv != SECSuccess) 217 content.data = NULL; 218 } 219 220 /* Signature Verification */ 221 if (doVerify) { 222 SEC_PKCS7ContentInfo *cinfo; 223 SEC_PKCS7SignedData *signedData; 224 HASH_HashType digestType; 225 PRBool contentIsSigned; 226 227 cinfo = SEC_PKCS7DecodeItem(&pkcs7der, NULL, NULL, NULL, NULL, 228 NULL, NULL, NULL); 229 if (cinfo == NULL) { 230 PR_fprintf(PR_STDERR, "Unable to decode PKCS7 data\n"); 231 goto cleanup; 232 } 233 /* below here, goto done */ 234 235 contentIsSigned = SEC_PKCS7ContentIsSigned(cinfo); 236 if (debugInfo) { 237 PR_fprintf(PR_STDERR, "Content is%s encrypted.\n", 238 SEC_PKCS7ContentIsEncrypted(cinfo) ? "" : " not"); 239 } 240 if (debugInfo || !contentIsSigned) { 241 PR_fprintf(PR_STDERR, "Content is%s signed.\n", 242 contentIsSigned ? "" : " not"); 243 } 244 245 if (!contentIsSigned) 246 goto done; 247 248 signedData = cinfo->content.signedData; 249 250 /* assume that there is only one digest algorithm for now */ 251 digestType = AlgorithmToHashType(signedData->digestAlgorithms[0]); 252 if (digestType == HASH_AlgNULL) { 253 PR_fprintf(PR_STDERR, "Invalid hash algorithmID\n"); 254 goto done; 255 } 256 if (content.data) { 257 SECCertUsage usage = certUsageEmailSigner; 258 SECItem digest; 259 unsigned char digestBuffer[HASH_LENGTH_MAX]; 260 261 if (debugInfo) 262 PR_fprintf(PR_STDERR, "contentToVerify=%s\n", content.data); 263 264 digest.data = digestBuffer; 265 digest.len = sizeof digestBuffer; 266 267 if (DigestContent(&digest, &content, digestType)) { 268 SECU_PrintError(progName, "Message digest computation failure"); 269 goto done; 270 } 271 272 if (debugInfo) { 273 unsigned int i; 274 PR_fprintf(PR_STDERR, "Data Digest=:"); 275 for (i = 0; i < digest.len; i++) 276 PR_fprintf(PR_STDERR, "%02x:", digest.data[i]); 277 PR_fprintf(PR_STDERR, "\n"); 278 } 279 280 fprintf(outFile, "signatureValid="); 281 PORT_SetError(0); 282 if (SEC_PKCS7VerifyDetachedSignature(cinfo, usage, 283 &digest, digestType, PR_FALSE)) { 284 fprintf(outFile, "yes"); 285 } else { 286 fprintf(outFile, "no"); 287 if (verbose) { 288 fprintf(outFile, ":%s", 289 SECU_Strerror(PORT_GetError())); 290 } 291 } 292 fprintf(outFile, "\n"); 293 result = 0; 294 } 295 done: 296 SEC_PKCS7DestroyContentInfo(cinfo); 297 } 298 299 if (displayAll) { 300 if (SV_PrintPKCS7ContentInfo(outFile, &pkcs7der)) 301 result = 1; 302 } 303 304 cleanup: 305 SECITEM_FreeItem(&pkcs7der, PR_FALSE); 306 SECITEM_FreeItem(&content, PR_FALSE); 307 308 if (NSS_Shutdown() != SECSuccess) { 309 result = 1; 310 } 311 312 return result; 313 }