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