1 /*
2 *
3 * Copyright (C) 2000-2020, OFFIS e.V.
4 * All rights reserved. See COPYRIGHT file for details.
5 *
6 * This software and supporting documentation were developed by
7 *
8 * OFFIS e.V.
9 * R&D Division Health
10 * Escherweg 2
11 * D-26121 Oldenburg, Germany
12 *
13 *
14 * Module: dcmsign
15 *
16 * Author: Marco Eichelberg
17 *
18 * Purpose:
19 * classes: DcmSignatureHelper
20 *
21 */
22
23 #include "dcmtk/config/osconfig.h"
24
25 #ifdef WITH_OPENSSL
26
27 #include "dcmtk/dcmsign/dcsighlp.h"
28 #include "dcmtk/dcmdata/dcdeftag.h" /* for attribute tag constants */
29 #include "dcmtk/dcmdata/dcdicent.h" /* for class DcmDictEntry */
30 #include "dcmtk/dcmdata/dcdict.h" /* for class DcmDataDictionary */
31 #include "dcmtk/dcmdata/dcitem.h" /* for class DcmItem */
32 #include "dcmtk/dcmdata/dcsequen.h" /* for class DcmSequenceOfItems */
33 #include "dcmtk/dcmdata/dcvrat.h" /* for class DcmAttributeTag */
34 #include "dcmtk/dcmsign/dcsignat.h" /* for class DcmSignature */
35 #include "dcmtk/dcmsign/sicert.h" /* for class SiCertificate */
36 #include "dcmtk/dcmsign/sicertvf.h" /* for class SiCertificateVerifier */
37 #include "dcmtk/dcmsign/sisprof.h" /* for class SiSecurityProfile */
38 #include "dcmtk/dcmsign/sitsfs.h" /* for class SiTimeStampFS */
39 #include "dcmtk/dcmsign/sitypes.h" /* for logger macros */
40 #include "dcmtk/dcmsign/sicreapr.h" /* for class SiCreatorProfile */
41 #include "dcmtk/dcmsign/siautopr.h" /* for class SiAuthorizationProfile */
42 #include "dcmtk/dcmsign/sisrvpr.h" /* for class SiStructuredReportingVerificationProfile */
43 #include "dcmtk/ofstd/ofexit.h" /* for common exit codes */
44 #include "dcmtk/dcmsign/siexit.h" /* for dcmsign specific exit codes */
45
46
DcmSignatureHelper()47 DcmSignatureHelper::DcmSignatureHelper()
48 {
49 }
50
~DcmSignatureHelper()51 DcmSignatureHelper::~DcmSignatureHelper()
52 {
53 }
54
readNextToken(const char * c,int & pos,DcmTagKey & key,Uint32 & idx)55 int DcmSignatureHelper::readNextToken(const char *c, int& pos, DcmTagKey& key, Uint32& idx)
56 {
57 OFString aString;
58 int lpos = pos;
59 int spos = 0;
60 while(isspace(OFstatic_cast(unsigned char, c[lpos]))) ++lpos; // ignore leading space
61
62 if (c[lpos]=='\0') return -1; // EOF
63 if (c[lpos]=='.')
64 {
65 ++pos;
66 return 3; // period
67 }
68 if (c[lpos]=='[')
69 {
70 spos = ++lpos;
71 while ((c[lpos] >= '0')&&(c[lpos] <= '9')) ++lpos;
72 if (c[lpos] != ']') return 0; // parse error
73 unsigned long newindex = 0;
74 if (1 != sscanf(c+spos,"%lu", &newindex)) return 0; // parse error
75 idx = OFstatic_cast(Uint32, newindex);
76 pos = ++lpos;
77 return 2; // index
78 }
79 if (c[lpos]=='(')
80 {
81 spos = ++lpos;
82 while ((c[lpos] != ')')&&(c[lpos] != '\0')) ++lpos;
83 if (c[lpos] != ')') return 0; // parse error
84 unsigned int group=0;
85 unsigned int elem=0;
86 if (2 != sscanf(c+spos,"%x,%x", &group, &elem)) return 0; // parse error
87 key = DcmTagKey(group,elem);
88 pos = ++lpos;
89 return 1; // tag key
90 }
91 spos = lpos;
92 while ( ((c[lpos] >= 'a')&&(c[lpos] <= 'z')) || ((c[lpos] >= 'A')&&(c[lpos] <= 'Z')) || ((c[lpos] >= '0')&&(c[lpos] <= '9'))) ++lpos;
93 aString.append(c + spos, (lpos-spos));
94 const DcmDataDictionary& globalDataDict = dcmDataDict.rdlock();
95 const DcmDictEntry *dicent = globalDataDict.findEntry(aString.c_str());
96 if (dicent)
97 {
98 key = dicent->getKey();
99 dcmDataDict.rdunlock();
100 pos = lpos;
101 return 1; // tag key;
102 }
103 dcmDataDict.rdunlock();
104 DCMSIGN_ERROR("attribute name '" << aString.c_str() << "' unknown.");
105 return 0; // parse error
106 }
107
108
readTextFile(const char * filename)109 char *DcmSignatureHelper::readTextFile(const char *filename)
110 {
111 char *result = NULL;
112 FILE *file = fopen(filename, "rb");
113 if (file)
114 {
115 fseek(file, 0, SEEK_END);
116 long numBytes = ftell(file);
117 fseek(file, 0, SEEK_SET);
118 result = new char[numBytes+1];
119 if (result)
120 {
121 result[numBytes] = '\0'; // ensure zero termination of text field
122 if (OFstatic_cast(size_t, numBytes) != fread(result, 1, OFstatic_cast(size_t, numBytes), file))
123 {
124 DCMSIGN_WARN("read error in file " << filename);
125 delete[] result;
126 result = NULL;
127 }
128 }
129 fclose(file);
130 } else {
131 DCMSIGN_ERROR("file not found: " << filename);
132 }
133 return result;
134 }
135
136
parseTextFile(const char * filename,DcmAttributeTag & tagList)137 int DcmSignatureHelper::parseTextFile(const char *filename, DcmAttributeTag& tagList)
138 {
139 char *c=readTextFile(filename);
140 if (c==NULL) return EXITCODE_CANNOT_READ_TAG_FILE; // bail out
141 int position = 0;
142 int token = 0;
143 Uint32 idx = 0;
144 DcmTagKey key;
145 int result = EXITCODE_NO_ERROR;
146 do
147 {
148 token = readNextToken(c, position, key, idx);
149 if (token == 1) // we have read a tag key
150 {
151 if (EC_Normal != tagList.putTagVal(key, tagList.getVM()))
152 {
153 result = EXITCODE_SYNTAX_ERROR_IN_TAG_FILE;
154 token = -1;
155 }
156 }
157 else if (token >= 0)
158 {
159 DCMSIGN_ERROR("parse error in text file '" << filename << "'");
160 result = EXITCODE_SYNTAX_ERROR_IN_TAG_FILE;
161 token = -1;
162 }
163 } while (token >= 0);
164 delete[] c;
165 return result;
166 }
167
168
addTag(const char * c,DcmAttributeTag & tagList)169 OFBool DcmSignatureHelper::addTag(const char *c, DcmAttributeTag& tagList)
170 {
171 OFBool result = OFFalse;
172 unsigned int group = 0xffff;
173 unsigned int elem = 0xffff;
174 if (sscanf(c, "%x,%x", &group, &elem ) != 2 )
175 {
176 /* it is a name */
177 const DcmDataDictionary& globalDataDict = dcmDataDict.rdlock();
178 const DcmDictEntry *dicent = globalDataDict.findEntry(c);
179 if (dicent)
180 {
181 if (EC_Normal == tagList.putTagVal(dicent->getKey(), tagList.getVM())) result = OFTrue;
182 }
183 dcmDataDict.rdunlock();
184 } else {
185 if (EC_Normal == tagList.putTagVal(DcmTagKey(group,elem), tagList.getVM())) result = OFTrue;
186 }
187 return result;
188 }
189
190
locateItemforSignatureCreation(DcmItem & dataset,const char * location)191 DcmItem *DcmSignatureHelper::locateItemforSignatureCreation(DcmItem& dataset, const char *location)
192 {
193 DcmTagKey key;
194 Uint32 idx;
195 int pos = 0;
196 int token = 0;
197 int expected = 1;
198 OFBool finished = OFFalse;
199 DcmItem *result = &dataset;
200 DcmSequenceOfItems *sq = NULL;
201 DcmStack stack;
202 do
203 {
204 token = readNextToken(location, pos, key, idx);
205 if ((token != expected)&&(token != -1))
206 {
207 DCMSIGN_ERROR("parse error in item location string '" << location << "'");
208 return NULL;
209 }
210 if (token == -1)
211 {
212 if (! finished)
213 {
214 DCMSIGN_ERROR("item location string '" << location << "' incomplete.");
215 return NULL;
216 }
217 return result;
218 }
219 if (token == 1)
220 {
221 // we have read a tag key
222 stack.clear();
223 if (EC_Normal != result->search(key, stack, ESM_fromHere, OFFalse))
224 {
225 DCMSIGN_ERROR("attribute " << key << " not found in dataset (item location string is '" << location << "')");
226 return NULL;
227 }
228 if (stack.top()->ident() == EVR_SQ)
229 {
230 sq = OFstatic_cast(DcmSequenceOfItems *, (stack.top()));
231 } else {
232 DCMSIGN_ERROR("attribute " << key << " is not a sequence (item location string is '" << location << "')");
233 return NULL;
234 }
235 expected = 2;
236 finished = OFFalse;
237 }
238 else if (token == 2)
239 {
240 // we have read an index
241 if (sq == NULL)
242 {
243 DCMSIGN_ERROR("sequence not found in item location string '" << location << "'");
244 return NULL;
245 }
246 if (idx >= sq->card())
247 {
248 DCMSIGN_ERROR("sequence " << sq->getTag() << " only has " << sq->card()
249 << " items, cannot locate item " << idx << " (item location string is '" << location << "')");
250 return NULL;
251 }
252 result = sq->getItem(idx);
253 if (result == NULL)
254 {
255 DCMSIGN_ERROR("item not found in item location string '" << location << "'");
256 return NULL;
257 }
258 expected = 3;
259 finished = OFTrue;
260 }
261 else if (token == 3)
262 {
263 // we have read a period
264 expected = 1;
265 finished = OFFalse;
266 }
267 } while (token > 0);
268 return NULL;
269 }
270
271
printSignatureItemPosition(DcmStack & stack,OFString & str)272 void DcmSignatureHelper::printSignatureItemPosition(DcmStack& stack, OFString& str)
273 {
274 str.clear();
275 DcmObject *elem = NULL;
276 DcmSequenceOfItems *sq = NULL;
277 unsigned long sqCard=0;
278 const char *tagname = NULL;
279 unsigned long m=0;
280 char buf[30];
281 if (stack.card() > 2)
282 {
283 // signature is located within a sequence
284 for (unsigned long l=stack.card()-2; l>0; --l) // loop over all elements except the stack top and bottom
285 {
286 elem = stack.elem(l);
287 if (elem)
288 {
289 if ((elem->ident() == EVR_item) && sq)
290 {
291 sqCard = sq->card();
292 for (m=0; m<sqCard; m++)
293 {
294 if (sq->getItem(m) == elem)
295 {
296 sprintf(buf, "[%lu]", m);
297 str.append(buf);
298 }
299 }
300 }
301 else
302 {
303 if (str.size() > 0) str.append(".");
304 sq = OFstatic_cast(DcmSequenceOfItems *, elem);
305 DcmTag currentTag(elem->getTag());
306 tagname = currentTag.getTagName();
307 if (tagname) str.append(tagname); else
308 {
309 sprintf(buf, "(%04x,%04x)", elem->getTag().getGroup(), elem->getTag().getElement());
310 str.append(buf);
311 }
312 if (elem->ident() == EVR_SQ) sq = OFstatic_cast(DcmSequenceOfItems *, elem); else sq = NULL;
313 }
314 }
315 }
316 } else {
317 // signature is located in the main dataset
318 str = "Main Dataset";
319 }
320 }
321
322
do_sign(DcmItem * dataset,SiPrivateKey & key,SiCertificate & cert,SiMAC * opt_mac,SiSecurityProfile * opt_profile,DcmAttributeTag * opt_tagList,E_TransferSyntax opt_signatureXfer,FILE * dumpFile,SiSignaturePurpose::E_SignaturePurposeType opt_sigPurpose,SiTimeStamp * timeStamp)323 int DcmSignatureHelper::do_sign(
324 DcmItem *dataset,
325 SiPrivateKey& key,
326 SiCertificate& cert,
327 SiMAC *opt_mac,
328 SiSecurityProfile *opt_profile,
329 DcmAttributeTag *opt_tagList,
330 E_TransferSyntax opt_signatureXfer,
331 FILE *dumpFile,
332 SiSignaturePurpose::E_SignaturePurposeType opt_sigPurpose,
333 SiTimeStamp *timeStamp)
334 {
335 OFCondition sicond = opt_profile->inspectSignatureDataset(*dataset);
336 if (sicond.good())
337 {
338 DcmSignature signer;
339 signer.attach(dataset);
340 signer.setDumpFile(dumpFile);
341 sicond = signer.createSignature(key, cert, *opt_mac, *opt_profile, opt_signatureXfer, opt_tagList, timeStamp, opt_sigPurpose);
342 signer.detach();
343 }
344
345 if (sicond.bad())
346 {
347 DCMSIGN_ERROR(sicond.text() << " while creating signature in main dataset");
348 return EXITCODE_SIGNATURE_CREATION_FAILED;
349 }
350 return EXITCODE_NO_ERROR;
351 }
352
353
do_sign_item(DcmItem * dataset,SiPrivateKey & key,SiCertificate & cert,SiMAC * opt_mac,SiSecurityProfile * opt_profile,DcmAttributeTag * opt_tagList,const char * opt_location,E_TransferSyntax opt_signatureXfer,FILE * dumpFile,SiSignaturePurpose::E_SignaturePurposeType opt_sigPurpose,SiTimeStamp * timeStamp)354 int DcmSignatureHelper::do_sign_item(
355 DcmItem *dataset,
356 SiPrivateKey& key,
357 SiCertificate& cert,
358 SiMAC *opt_mac,
359 SiSecurityProfile *opt_profile,
360 DcmAttributeTag *opt_tagList,
361 const char *opt_location,
362 E_TransferSyntax opt_signatureXfer,
363 FILE *dumpFile,
364 SiSignaturePurpose::E_SignaturePurposeType opt_sigPurpose,
365 SiTimeStamp *timeStamp)
366 {
367 OFCondition sicond = EC_Normal;
368 DcmItem *sigItem = locateItemforSignatureCreation(*dataset, opt_location);
369 if (sigItem == NULL) sicond = SI_EC_ItemLocationNotFound;
370 else
371 {
372 sicond = opt_profile->inspectSignatureDataset(*sigItem);
373 if (sicond.good())
374 {
375 DcmSignature signer;
376 signer.attach(sigItem);
377 signer.setDumpFile(dumpFile);
378 sicond = signer.createSignature(key, cert, *opt_mac, *opt_profile, opt_signatureXfer, opt_tagList, timeStamp, opt_sigPurpose);
379 signer.detach();
380 }
381 }
382
383 if (sicond.bad())
384 {
385 DCMSIGN_ERROR(sicond.text() << " while creating signature in item '" << opt_location << "'");
386 return EXITCODE_SIGNATURE_CREATION_FAILED;
387 }
388 return EXITCODE_NO_ERROR;
389 }
390
391
do_verify(DcmItem * dataset,SiCertificateVerifier & certVerifier,E_SignatureVerificationPolicy verificationPolicy,E_TimestampVerificationPolicy timestampPolicy)392 int DcmSignatureHelper::do_verify(
393 DcmItem *dataset,
394 SiCertificateVerifier& certVerifier,
395 E_SignatureVerificationPolicy verificationPolicy,
396 E_TimestampVerificationPolicy timestampPolicy)
397 {
398 OFCondition sicond = EC_Normal;
399 DcmStack stack;
400 DcmSignature signer;
401 OFString aString;
402
403 // this counter counts the number of signatures found in the dataset
404 int counter = 0;
405
406 // this counter contains the number of signatures in the dataset
407 // that have failed validation
408 int corrupt_counter = 0;
409
410 unsigned long numSignatures = 0;
411 unsigned long l=0;
412 DcmItem *sigItem = DcmSignature::findFirstSignatureItem(*dataset, stack);
413
414 // this flag is set to true if we find any signature in the dataset
415 // that complies with the verification policy.
416 OFBool verificationPolicyFulfilled = OFFalse;
417 const char *verificationPolicyName = "(undefined)";
418 OFString s;
419
420 // for these verification policies, there is nothing to check,
421 // ESVP_requireSignature is checked elsewhere.
422 if ((verificationPolicy == ESVP_verifyIfPresent) ||
423 (verificationPolicy == ESVP_requireSignature))
424 verificationPolicyFulfilled = OFTrue;
425
426 while (sigItem)
427 {
428 signer.attach(sigItem);
429 numSignatures = signer.numberOfSignatures();
430 for (l=0; l<numSignatures; l++)
431 {
432 OFBool cert_expiry = OFFalse;
433 OFBool checkTimestamp = OFFalse;
434 if (EC_Normal == signer.selectSignature(l))
435 {
436 ++counter;
437 printSignatureDetails(signer, stack, counter);
438
439 if (dcmsignLogger.isEnabledFor(OFLogger::INFO_LOG_LEVEL))
440 aString = " Signature Verification : ";
441 else aString = " Signature Verification : ";
442
443 // verify signature profile
444 switch (verificationPolicy)
445 {
446 case ESVP_requireCreatorRSASignature:
447 {
448 verificationPolicyName = "Creator RSA Signature";
449 SiCreatorProfile sprofCr;
450 if (! verificationPolicyFulfilled) verificationPolicyFulfilled = signer.verifySignatureProfile(sprofCr).good();
451 }
452 break;
453 case ESVP_requireAuthorizationRSASignature:
454 {
455 verificationPolicyName = "Authorization RSA Signature";
456 SiAuthorizationProfile sprofAu;
457 if (! verificationPolicyFulfilled) verificationPolicyFulfilled = signer.verifySignatureProfile(sprofAu).good();
458 }
459 break;
460 case ESVP_requireSRRSASignature:
461 {
462 // there are two types of possible matches here:
463 // either the dataset is an UNVERIFIED SR and SiStructuredReportingProfile matches,
464 // or the dataset is a VERIFIED SR and SiStructuredReportingVerificationProfile matches.
465 // We select the profile based on the value of VerificationFlag on main dataset level.
466 verificationPolicyName = "SR RSA Signature";
467 if (! verificationPolicyFulfilled)
468 {
469 if (dataset->findAndGetOFString(DCM_VerificationFlag, s).good() && (s == "VERIFIED"))
470 {
471 SiStructuredReportingVerificationProfile sprofSRV;
472 verificationPolicyFulfilled = signer.verifySignatureProfile(sprofSRV).good();
473 }
474 else
475 {
476 SiStructuredReportingProfile sprofSR;
477 verificationPolicyFulfilled = signer.verifySignatureProfile(sprofSR).good();
478 }
479 }
480 }
481 break;
482 case ESVP_verifyIfPresent:
483 case ESVP_requireSignature:
484 break;
485 // There is deliberately no default here because if we extend
486 // the enum then we will have to extend this code.
487 }
488
489 // first verify if the signature matches the dataset
490 sicond = signer.verifyCurrent();
491 if (sicond.good())
492 {
493 // now check if we can successfully verify the signer's certificate
494 SiCertificate *cert = signer.getCurrentCertificate();
495 if (cert)
496 {
497 // print a warning if we have a weak (i.e. too short) key in the certificate
498 cert->checkForWeakKey();
499
500 if (certVerifier.verifyCertificate(*cert).good())
501 {
502 DCMSIGN_WARN(aString << "OK");
503 checkTimestamp = OFTrue;
504 }
505 else
506 {
507 cert_expiry = certVerifier.lastErrorIsCertExpiry();
508 DCMSIGN_WARN(aString << "signature is OK but certificate verification failed: " << certVerifier.lastError());
509 corrupt_counter++;
510 }
511 }
512 else
513 {
514 DCMSIGN_WARN(aString << "failed, certificate empty or invalid");
515 corrupt_counter++;
516 }
517 } else {
518 DCMSIGN_WARN(aString << sicond.text());
519 corrupt_counter++;
520 }
521
522 // check certified timestamp (if any).
523 // We only do this if the signature verification has passed
524 // or if the signature is OK but the signer certificate has expired,
525 // which may be "healed" by a timestamp that is still valid
526 if (checkTimestamp || cert_expiry)
527 {
528 printTimestampDetails(signer, timestampPolicy);
529
530 SiTimeStamp *tstamp = signer.getCurrentTimestamp();
531 OFBool haveTS = (tstamp != NULL) && tstamp->have_tsinfo();
532 if (haveTS)
533 {
534 if (timestampPolicy != ETVP_ignoreTS)
535 {
536 // timestamp is present and according to our policy we should verify it
537 if (dcmsignLogger.isEnabledFor(OFLogger::INFO_LOG_LEVEL))
538 aString = " Timestamp Verification : ";
539 else aString = " Timestamp Verification : ";
540
541 sicond = tstamp->verifyTSSignature(certVerifier);
542 if (sicond.good())
543 {
544 sicond = tstamp->verifyTSToken(certVerifier, *signer.getSelectedSignatureItem(), *signer.getCurrentCertificate());
545 if (sicond.good())
546 {
547 if (cert_expiry)
548 {
549 // we have an expired signature but a timestamp that is still valid,
550 // which means that the timestamp "heals" the failed signature verification
551 corrupt_counter--;
552 DCMSIGN_WARN(aString << "OK, extends validity period of expired signature.");
553 }
554 else
555 {
556 DCMSIGN_WARN(aString << "OK");
557 }
558 }
559 else
560 {
561 OFString errString;
562 tstamp->lastError(errString); // check if we have an OpenSSL error message
563 if (errString.length() == 0) errString = sicond.text(); // use DCMTK error message otherwise
564 DCMSIGN_WARN(aString << "timestamp signature verification failed: " << errString);
565 if (!cert_expiry) corrupt_counter++; // if cert_expiry is true, the counter has already been increased
566 }
567 }
568 else
569 {
570 OFString errString;
571 tstamp->lastError(errString);
572 DCMSIGN_WARN(aString << "timestamp signature verification failed: " << errString);
573 if (!cert_expiry) corrupt_counter++; // if cert_expiry is true, the counter has already been increased
574 }
575 }
576 }
577 else
578 {
579 // no timestamp present. Check if this is a problem.
580 if (timestampPolicy == ETVP_requireTS)
581 {
582 DCMSIGN_WARN(" Certified timestamp : absent, but required by timestamp policy");
583 if (!cert_expiry) corrupt_counter++; // if cert_expiry is true, the counter has already been increased
584 }
585 }
586 } // if (checkTimestamp)
587 } // if (EC_Normal == signer.selectSignature(l))
588 } // for (l=0; l<numSignatures; l++)
589 signer.detach();
590 sigItem = DcmSignature::findNextSignatureItem(*dataset, stack);
591 } // while (sigItem)
592
593 if (counter == 0)
594 {
595 if (verificationPolicy == ESVP_verifyIfPresent)
596 {
597 DCMSIGN_WARN("no signatures found in dataset.");
598 }
599 else
600 {
601 // no signature present but according to the signature policy, we would have expected one
602 DCMSIGN_ERROR("no signatures found although required by the signature verification policy.");
603 return EXITCODE_NO_SIGNATURES_PRESENT;
604 }
605 }
606 else
607 {
608 DCMSIGN_INFO(counter << " signatures verified in dataset, " << corrupt_counter << " corrupted.");
609 }
610
611 if (! verificationPolicyFulfilled)
612 {
613 DCMSIGN_ERROR("No signature in this dataset fulfills the required " << verificationPolicyName << " signature policy");
614 return EXITCODE_SIGNATURE_VERIFICATION_POLICY;
615 }
616
617 // return non-zero if any verification has failed
618 return (corrupt_counter == 0 ? EXITCODE_NO_ERROR : EXITCODE_SIGNATURE_VERIFICATION_FAILED);
619 }
620
621
do_remove_all(DcmItem * dataset)622 int DcmSignatureHelper::do_remove_all(DcmItem *dataset)
623 {
624 OFCondition sicond = EC_Normal;
625 DcmSignature signer;
626 int counter = 0;
627 OFString aString;
628 DcmStack stack;
629 DcmItem *sigItem = DcmSignature::findFirstSignatureItem(*dataset, stack);
630 while (sigItem)
631 {
632 signer.attach(sigItem);
633 while (signer.numberOfSignatures() > 0)
634 {
635 ++counter;
636 if (EC_Normal == signer.selectSignature(0))
637 {
638 if (EC_Normal == signer.getCurrentSignatureUID(aString))
639 DCMSIGN_WARN("Signature #" << counter << " UID=" << aString);
640 else
641 DCMSIGN_WARN("Signature #" << counter << " UID=(unknown)");
642 printSignatureItemPosition(stack, aString);
643 DCMSIGN_WARN(" Location : " << aString);
644 }
645 sicond = signer.removeSignature(0);
646 if (sicond != EC_Normal)
647 {
648 DCMSIGN_ERROR(sicond.text() << ": while removing signature");
649 return EXITCODE_SIGNATURE_REMOVAL_FAILED;
650 }
651 }
652 signer.detach();
653 stack.pop(); // remove pointer to the Digital Signatures Sequence that we've just deleted.
654 sigItem = DcmSignature::findNextSignatureItem(*dataset, stack);
655 }
656 DCMSIGN_INFO(counter << " signatures found and removed from dataset.");
657 return EXITCODE_NO_ERROR;
658 }
659
660
do_remove(DcmItem * dataset,const char * opt_location)661 int DcmSignatureHelper::do_remove(
662 DcmItem *dataset,
663 const char *opt_location)
664 {
665 OFCondition sicond = EC_Normal;
666 DcmSignature signer;
667 OFString aString;
668 DcmStack stack;
669 unsigned long cardSQ;
670 unsigned long i;
671 DcmItem *sigItem = DcmSignature::findFirstSignatureItem(*dataset, stack);
672 while (sigItem)
673 {
674 signer.attach(sigItem);
675 cardSQ = signer.numberOfSignatures();
676 for (i=0; i<cardSQ; i++)
677 {
678 if (EC_Normal == signer.selectSignature(i))
679 {
680 if (EC_Normal == signer.getCurrentSignatureUID(aString))
681 {
682 if (aString == opt_location)
683 {
684 DCMSIGN_WARN("Signature UID=" << aString);
685 printSignatureItemPosition(stack, aString);
686 DCMSIGN_WARN(" Location : " << aString);
687 sicond = signer.removeSignature(i);
688 if (sicond != EC_Normal)
689 {
690 DCMSIGN_ERROR(sicond.text() << ": while removing signature");
691 return EXITCODE_SIGNATURE_REMOVAL_FAILED;
692 } else {
693 return EXITCODE_NO_ERROR;
694 }
695 }
696 }
697 }
698 }
699 signer.detach();
700 sigItem = DcmSignature::findNextSignatureItem(*dataset, stack);
701 }
702 DCMSIGN_ERROR("signature with UID '" << opt_location << "' not found.");
703 return EXITCODE_SIGNATURE_UID_NOT_FOUND;
704 }
705
do_insert_ts(DcmItem * dataset,SiTimeStampFS * timeStamp)706 int DcmSignatureHelper::do_insert_ts(DcmItem *dataset, SiTimeStampFS *timeStamp)
707 {
708 // check parameters
709 if (dataset == NULL || timeStamp == NULL) return EXITCODE_CANNOT_ACCESS_TS;
710
711 // load timestamp query
712 if (timeStamp->load_ts_query_from_file().bad())
713 {
714 return EXITCODE_CANNOT_READ_TSQ_FILE;
715 }
716
717 // load timestamp response
718 if (timeStamp->load_ts_response_from_file().bad())
719 {
720 return EXITCODE_CANNOT_READ_TSR_FILE;
721 }
722
723 // load digital signature UID
724 OFString uid;
725 OFCondition result = timeStamp->getUIDFromFile(uid);
726 if (result.bad()) return EXITCODE_CANNOT_READ_UID_FILE;
727
728 // locate digital signature
729 DcmSignature signer;
730 OFString currentUID;
731 DcmStack stack;
732 unsigned long cardSQ;
733 unsigned long i;
734 OFBool found = OFFalse;
735
736 DcmItem *sigItem = DcmSignature::findFirstSignatureItem(*dataset, stack);
737 while (sigItem)
738 {
739 // attach the item in which a digital signatures sequence was found
740 signer.attach(sigItem);
741 // determine the number of signatures in this item
742 cardSQ = signer.numberOfSignatures();
743 for (i=0; i<cardSQ; i++)
744 {
745 if (signer.selectSignature(i).good() && signer.getCurrentSignatureUID(currentUID).good() && (currentUID == uid))
746 {
747 // we have found the right digital signature
748 found = OFTrue;
749 DcmItem *currentSignatureItem = signer.getSelectedSignatureItem();
750 if (currentSignatureItem == NULL)
751 {
752 return EXITCODE_CANNOT_ACCESS_SIGNATURE; // should never happen
753 }
754 else
755 {
756 // check if the timestamp response matches the query and the dataset
757 result = timeStamp->check_ts_response(*currentSignatureItem);
758 if (result.bad()) return EXITCODE_TS_CONSISTENCY_CHECK_FAILED;
759
760 // write timestamp to dataset
761 result = timeStamp->write_ts_token(*currentSignatureItem);
762 if (result.bad()) return EXITCODE_CANNOT_INSERT_TS;
763 }
764 }
765 }
766 signer.detach();
767 if (found) sigItem = NULL; else sigItem = DcmSignature::findNextSignatureItem(*dataset, stack);
768 }
769
770 if (!found)
771 {
772 DCMSIGN_ERROR("signature with UID '" << uid << "' not found.");
773 return EXITCODE_SIGNATURE_UID_NOT_FOUND;
774 }
775 return EXITCODE_NO_ERROR;
776 }
777
778
printSignatureDetails(DcmSignature & sig,DcmStack & stack,int count)779 void DcmSignatureHelper::printSignatureDetails(DcmSignature& sig, DcmStack& stack, int count)
780 {
781 OFString aString;
782 OFString codeValue;
783 OFString codeMeaning;
784 OFString codingSchemeDesignator;
785 Uint16 macID = 0;
786 DcmAttributeTag at(DCM_DataElementsSigned);
787 DcmTagKey tagkey;
788 DcmTag tag;
789 const char *tagName = NULL;
790
791 if (EC_Normal == sig.getCurrentSignatureUID(aString))
792 DCMSIGN_WARN("Signature #" << count << " UID=" << aString);
793 else
794 DCMSIGN_WARN("Signature #" << count << " UID=" << "(unknown)");
795 printSignatureItemPosition(stack, aString);
796 if (dcmsignLogger.isEnabledFor(OFLogger::INFO_LOG_LEVEL))
797 {
798 DCMSIGN_INFO(" Location : " << aString);
799 if (EC_Normal == sig.getCurrentMacID(macID))
800 DCMSIGN_INFO(" MAC ID : " << macID);
801 else
802 DCMSIGN_INFO(" MAC ID : (unknown)");
803 if (EC_Normal == sig.getCurrentMacName(aString))
804 DCMSIGN_INFO(" MAC algorithm : " << aString);
805 else
806 DCMSIGN_INFO(" MAC algorithm : (unknown)");
807 if (EC_Normal == sig.getCurrentMacXferSyntaxName(aString))
808 DCMSIGN_INFO(" MAC calculation xfer syntax : " << aString);
809 else
810 DCMSIGN_INFO(" MAC calculation xfer syntax : (unknown)");
811 // data elements signed
812 if (EC_Normal != sig.getCurrentDataElementsSigned(at))
813 DCMSIGN_INFO(" Data elements signed : (unknown)");
814 else
815 {
816 DCMSIGN_INFO(" Data elements signed :");
817 unsigned long atVM = at.getVM();
818 for (unsigned long n=0; n<atVM; n++)
819 {
820 if (EC_Normal == at.getTagVal(tagkey, n))
821 {
822 tag = tagkey;
823 tagName = tag.getTagName();
824 DCMSIGN_INFO(" " << tagkey << " " << (tagName != NULL ? tagName : ""));
825 }
826 }
827 }
828 if (EC_Normal == sig.getCurrentSignatureDateTime(aString))
829 DCMSIGN_INFO(" Signature date/time : " << aString);
830 else
831 DCMSIGN_INFO(" Signature date/time : (unknown)");
832
833 if (sig.getCurrentSignaturePurpose(codeValue, codeMeaning, codingSchemeDesignator).good())
834 DCMSIGN_INFO(" Signature purpose : " << codeMeaning << " (" << codeValue << ", " << codingSchemeDesignator << ")" );
835 else
836 DCMSIGN_INFO(" Signature purpose : (not specified)");
837
838 DCMSIGN_INFO(" Certificate of signer : ");
839 SiCertificate *cert = sig.getCurrentCertificate();
840 if ((cert == NULL)||(cert->getKeyType()==EKT_none))
841 DCMSIGN_INFO(" none");
842 else
843 {
844 DCMSIGN_INFO(" X.509v" << cert->getX509Version());
845 cert->getCertSubjectName(aString);
846 DCMSIGN_INFO(" Subject : " << aString);
847 cert->getCertIssuerName(aString);
848 DCMSIGN_INFO(" Issued by : " << aString);
849 DCMSIGN_INFO(" Serial no. : " << cert->getCertSerialNo());
850 cert->getCertValidityNotBefore(aString);
851 DCMSIGN_INFO(" Validity : not before " << aString);
852 cert->getCertValidityNotAfter(aString);
853 DCMSIGN_INFO(" Validity : not after " << aString);
854 const char *ecname = NULL;
855 switch (cert->getKeyType())
856 {
857 case EKT_RSA:
858 DCMSIGN_INFO(" Public key : RSA, " << cert->getCertKeyBits() << " bits");
859 break;
860 case EKT_DSA:
861 DCMSIGN_INFO(" Public key : DSA, " << cert->getCertKeyBits() << " bits");
862 break;
863 case EKT_EC:
864 ecname = cert->getCertCurveName();
865 if (ecname)
866 {
867 DCMSIGN_INFO(" Public key : EC, curve " << ecname << ", " << cert->getCertKeyBits() << " bits");
868 }
869 else
870 {
871 DCMSIGN_INFO(" Public key : EC, " << cert->getCertKeyBits() << " bits");
872 }
873 break;
874 case EKT_DH:
875 DCMSIGN_INFO(" Public key : DH, " << cert->getCertKeyBits() << " bits");
876 break;
877 default:
878 case EKT_none: // should never happen
879 DCMSIGN_INFO(" Public key : unknown type");
880 break;
881 }
882 }
883 } else {
884 DCMSIGN_INFO(" Location : " << aString);
885 }
886 }
887
888
printTimestampDetails(DcmSignature & sig,E_TimestampVerificationPolicy tsPolicy)889 void DcmSignatureHelper::printTimestampDetails(DcmSignature& sig, E_TimestampVerificationPolicy tsPolicy)
890 {
891 SiTimeStamp *tstamp = sig.getCurrentTimestamp();
892 OFBool haveTS = (tstamp != NULL) && tstamp->have_tsinfo();
893
894 if (dcmsignLogger.isEnabledFor(OFLogger::INFO_LOG_LEVEL))
895 {
896 switch (tsPolicy)
897 {
898 case ETVP_verifyTSIfPresent:
899 if (haveTS)
900 {
901 DCMSIGN_INFO(" Certified timestamp : CMS_TSP version " << tstamp->get_tsinfo_version());
902 }
903 else
904 {
905 DCMSIGN_INFO(" Certified timestamp : absent");
906 return;
907 }
908 break;
909 case ETVP_ignoreTS:
910 if (haveTS)
911 {
912 DCMSIGN_INFO(" Certified timestamp : present, but ignored according to timestamp policy");
913 return;
914 }
915 else
916 {
917 DCMSIGN_INFO(" Certified timestamp : absent");
918 return;
919 }
920 break;
921
922 case ETVP_requireTS:
923 if (haveTS)
924 {
925 DCMSIGN_INFO(" Certified timestamp : CMS_TSP version " << tstamp->get_tsinfo_version());
926 }
927 else
928 {
929 DCMSIGN_INFO(" Certified timestamp : absent, but required by timestamp policy");
930 return;
931 }
932 break;
933 }
934 }
935
936 if (haveTS)
937 {
938 // Now print timestamp details
939 OFString s;
940 tstamp->get_tsinfo_policy_oid(s);
941 DCMSIGN_INFO(" Policy OID : " << s);
942 tstamp->get_tsinfo_imprint_algorithm_name(s);
943 DCMSIGN_INFO(" Imprint MAC algorithm : " << s);
944 tstamp->get_tsinfo_serial_number(s);
945 DCMSIGN_INFO(" Serial number : " << s);
946 tstamp->get_tsinfo_nonce(s);
947 if (s.length() == 0) s = "none";
948 DCMSIGN_INFO(" Nonce : " << s);
949 tstamp->get_tsinfo_tsa_name(s);
950 if (s.length() == 0) s = "none";
951 DCMSIGN_INFO(" TSA Name : " << s);
952 DCMSIGN_INFO(" Ordering : " << (tstamp->get_tsinfo_ordering() ? "true" : "false" ));
953 tstamp->get_tsinfo_accuracy(s);
954 if (s.length() == 0) s = "not specified";
955 DCMSIGN_INFO(" Accuracy : " << s);
956 tstamp->get_tsinfo_timestamp(s);
957 DCMSIGN_INFO(" Timestamp : " << s);
958 tstamp->get_tsinfo_policy_oid(s);
959
960 // finally, print extensions (if any)
961 int numext = tstamp->get_tsinfo_numextensions();
962 if (numext == 0)
963 {
964 DCMSIGN_INFO(" Extensions : none");
965 }
966 else
967 {
968 DCMSIGN_INFO(" Extensions :");
969 for (int i=0; i<numext; ++i)
970 {
971 tstamp->get_tsinfo_extension(s, i);
972 DCMSIGN_INFO(" - " << s);
973 }
974 }
975 }
976 }
977
978
979
980 #else /* WITH_OPENSSL */
981
982 int sisighlp_cc_dummy_to_keep_linker_from_moaning = 0;
983
984 #endif
985