1 /*
2  *
3  *  Copyright (C) 1998-2019, 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: DcmSignature
20  *
21  */
22 
23 #ifndef DCMSIGN_H
24 #define DCMSIGN_H
25 
26 #include "dcmtk/config/osconfig.h"
27 
28 #ifdef WITH_OPENSSL
29 
30 #include "dcmtk/dcmsign/sitypes.h"
31 #include "dcmtk/dcmdata/dcxfer.h"    /* for E_TransferSyntax */
32 #include "dcmtk/dcmsign/sipurpos.h"  /* for E_SignaturePurposeType */
33 
34 #define INCLUDE_CSTDIO
35 #include "dcmtk/ofstd/ofstdinc.h"
36 
37 class DcmAttributeTag;
38 class DcmDateTime;
39 class DcmItem;
40 class DcmSequenceOfItems;
41 class DcmStack;
42 class DcmTagKey;
43 class SiPrivateKey;
44 class SiCertificate;
45 class SiSecurityProfile;
46 class SiMAC;
47 class SiTimeStamp;
48 
49 /** this class provides the main interface to the dcmsign module - it allows
50  *  to create, examine and verify digital signatures in DICOM datasets or
51  *  items. The methods in this class do not handle digital signatures
52  *  embedded in sequence items within the dataset, other than providing
53  *  helper functions that allow to locate and attach the sub-items
54  *  separately.
55  *  @remark this class is only available if DCMTK is compiled with
56  *  OpenSSL support enabled.
57  */
58 class DCMTK_DCMSIGN_EXPORT DcmSignature
59 {
60 public:
61   /** initializes the dcmsign library including the underlying OpenSSL library.
62    *  this method should be called by main() before any object of the dcmsign
63    *  library is created or used.
64    */
65   static void initializeLibrary();
66 
67   /// default constructor
68   DcmSignature();
69 
70   /// destructor
71   virtual ~DcmSignature();
72 
73   /** attaches a DICOM dataset or item to the signature object.
74    *  The dataset is detached by a call to detach() or by destruction
75    *  of the signature object.  This object may modify but never deletes
76    *  an attached dataset.
77    *  @param dataset dataset or item to be attached
78    */
79   void attach(DcmItem *dataset);
80 
81   /** detaches an attached DICOM dataset from the signature object.
82    */
83   void detach();
84 
85   /** creates a new digital signature in the current dataset.
86    *  Checks whether private and public key match and whether
87    *  all requirements of the given security profile are fulfilled.
88    *  @param key private key for signature creation
89    *  @param cert certificate with public key
90    *  @param mac MAC algorithm to be used for signature creation
91    *  @param profile security profile for signature creation
92    *  @param xfer transfer syntax to use when serializing DICOM data
93    *  @param tagList pointer to list of attribute tags to sign, may be NULL.
94    *    If this parameter is nonzero, it contains a list of attribute sign.
95    *    The real list of attributes signed is derived from this parameter plus the
96    *    requirements of the security profile. If NULL, a universal match is assumed,
97    *    i.e. all signable attributes in the data set are signed.
98    *  @param timeStamp pointer to time stamp client used to create timestamps
99    *    for the digital signature.
100    *  @param sigPurpose digital signature purpose
101    *  @return status code
102    */
103   OFCondition createSignature(
104     SiPrivateKey& key,
105     SiCertificate& cert,
106     SiMAC& mac,
107     SiSecurityProfile& profile,
108     E_TransferSyntax xfer=EXS_LittleEndianExplicit,
109     const DcmAttributeTag *tagList=NULL,
110     SiTimeStamp *timeStamp=NULL,
111     SiSignaturePurpose::E_SignaturePurposeType sigPurpose=SiSignaturePurpose::ESP_none);
112 
113   /** returns the number of signatures in the dataset. Does not count
114    *  signatures embedded in sequence items within the dataset.
115    */
116   unsigned long numberOfSignatures();
117 
118   /** removes a signature from the dataset.
119    *  @param i index, must be < numberOfSignatures().
120    *  @return status code
121    */
122   OFCondition removeSignature(unsigned long i);
123 
124   /** selects one of the digital signatures from the attached dataset for reading.
125    *  @param i index, must be < numberOfSignatures()
126    *  @return status code
127    */
128   OFCondition selectSignature(unsigned long i);
129 
130   /** verifies the current signature.
131    *  Current signature must be selected with selectSignature().
132    *  @return SI_EC_Normal if signature is complete and valid, an error code
133    *    describing the type of verification failure otherwise.
134    */
135   OFCondition verifyCurrent();
136 
137   /** returns the MAC ID of the current signature.
138    *  Current signature must be selected with selectSignature().
139    *  @param macID MAC ID returned in this parameter upon success
140    *  @return status code
141    */
142   OFCondition getCurrentMacID(Uint16& macID);
143 
144   /** returns the MAC Calculation Transfer Syntax of the current signature.
145    *  If the transfer syntax is well-known, the UID is replaced by the
146    *  transfer syntax name preceded by '='.
147    *  Current signature must be selected with selectSignature().
148    *  @param str transfer syntax name or UID returned in this parameter upon success
149    *  @return status code
150    */
151   OFCondition getCurrentMacXferSyntaxName(OFString& str);
152 
153   /** returns the MAC Algorithm Name of the current signature.
154    *  Current signature must be selected with selectSignature().
155    *  @param str MAC algorithm name returned in this parameter upon success
156    *  @return status code
157    */
158   OFCondition getCurrentMacName(OFString& str);
159 
160   /** returns the Digital Signature UID of the current signature.
161    *  Current signature must be selected with selectSignature().
162    *  @param str signature UID returned in this parameter upon success
163    *  @return status code
164    */
165   OFCondition getCurrentSignatureUID(OFString& str);
166 
167   /** returns the Signature Date/Time of the current signature.
168    *  Current signature must be selected with selectSignature().
169    *  @param str signature date/time returned in this parameter upon success
170    *  @return status code
171    */
172   OFCondition getCurrentSignatureDateTime(OFString& str);
173 
174   /** returns a pointer to the object representing Signature Date/Time
175    *  of the current signature.
176    *  Current signature must be selected with selectSignature().
177    *  @return pointer to signature datetime if present, NULL otherwise
178    */
179   DcmDateTime *getCurrentSignatureDateTime();
180 
181   /** returns the Data Elements Signed attribute of the current signature if present.
182    *  Current signature must be selected with selectSignature().
183    *  If a valid signature is selected but the signature does not contain
184    *  the Data Elements Signed element (i.e. all attributes are signed), this method
185    *  returns an error code.
186    *  @param desig data elements signed returned in this parameter upon success
187    *  @return status code
188    */
189   OFCondition getCurrentDataElementsSigned(DcmAttributeTag& desig);
190 
191   /** returns the signature purpose code of the current signature if present.
192    *  Current signature must be selected with selectSignature().
193    *  If a valid signature is selected but the signature does not contain
194    *  a valid SignaturePurposeCodeSequence, this method returns an error code.
195    *  @param codeValue signature purpose code value returned in this parameter upon success
196    *  @param codeMeaning signature purpose code meaning returned in this parameter upon success
197    *  @param codingSchemeDesignator signature purpose coding scheme designator returned in this parameter upon success
198    *  @return status code
199    */
200   OFCondition getCurrentSignaturePurpose(OFString& codeValue, OFString& codeMeaning, OFString& codingSchemeDesignator);
201 
202   /** verifies whether the currently selected signature within the
203    *  currently attached dataset matches the requirements of the
204    *  given signature profile.
205    *  @param sprof security profile
206    *  @return EC_Normal if signature matches, an error code otherwise
207    */
208   OFCondition verifySignatureProfile(SiSecurityProfile &sprof);
209 
210   /** returns the certificate of the current signature if present.
211    *  Current signature must be selected with selectSignature().
212    *  May return NULL if certificate is unavailable.
213    *  @return pointer to current certificate, NULL if unavailable.
214    */
215   SiCertificate *getCurrentCertificate();
216 
217   /** returns the certified timestamp of the current signature if present.
218    *  Current signature must be selected with selectSignature().
219    *  May return NULL if timestamp is unavailable.
220    *  @return pointer to current timestamp, NULL if unavailable.
221    */
222   SiTimeStamp *getCurrentTimestamp();
223 
224   /** returns the item of the DigitalSignaturesSequence selected by the last call
225    *  to selectSignature(), or NULL if no signature has been selected.
226    *  @return pointer to current signature item, may be NULL
227    */
228   DcmItem *getSelectedSignatureItem();
229 
230   /** dump all data that is fed into the MAC algorithm into the given file,
231    *  which must be opened and closed by caller.
232    *  @param f pointer to file already opened for writing; may be NULL.
233    */
234   void setDumpFile(FILE *f);
235 
236   /** recursively browses through the given dataset and searches the first
237    *  occurrence of the DigitalSignaturesSequence. If found, returns
238    *  a pointer to the Item in which the sequence is contained.
239    *  @param item dataset to be browsed
240    *  @param stack search stack, must be passed to findNextSignatureItem() later on.
241    *  @return pointer to Item containing a DigitalSignatureSequence if found, NULL otherwise.
242    */
243   static DcmItem *findFirstSignatureItem(DcmItem& item, DcmStack& stack);
244 
245   /** recursively browses through the given dataset and searches the next
246    *  occurrence of the DigitalSignaturesSequence. If found, returns
247    *  a pointer to the Item in which the sequence is contained.
248    *  @param item dataset to be browsed
249    *  @param stack search stack as returned by findFirstSignatureItem() or the last call to this method.
250    *  @return pointer to Item containing a DigitalSignatureSequence if found, NULL otherwise.
251    */
252   static DcmItem *findNextSignatureItem(DcmItem& item, DcmStack& stack);
253 
254   /** check a DER encoded ASN.1 SEQUENCE structure for a possible pad byte
255    *  and, if a pad byte is detected, remove it by decreasing buflen.
256    *  This will work for SEQUENCEs with one byte and two byte encoding
257    *  (i.e. max 64 kBytes).
258    *  @param buf pointer to DER encoded ASN.1 data
259    *  @param buflen length of buffer pointed to, in bytes. The variable is
260    *    decreased by one if a pad byte is detected.
261    */
262   static void adjustASN1SequenceLength(const unsigned char *buf, unsigned long& buflen);
263 
264 private:
265 
266   /// private undefined copy constructor
267   DcmSignature(DcmSignature& arg);
268 
269   /// private undefined copy assignment operator
270   DcmSignature& operator=(DcmSignature& arg);
271 
272   /// removes the selection of a current signature if present
273   void deselect();
274 
275   /** allocates a new mac ID number for a new signature.
276    *  examines all mac ID numbers in the digital signatures sequence
277    *  and in the mac parameters sequence and returns an unused number.
278    *  @param newID upon successful return, new number is passed in this parameter
279    *  @return status code
280    */
281   OFCondition allocateMACID(Uint16& newID);
282 
283   /** searches a given item for the DCM_MACIDnumber element and returns
284    *  its value if present
285    *  @param item item to be searched
286    *  @param macid MAC ID returned in this parameter upon success
287    *  @return EC_Normal if successful, an error code otherwise
288    */
289   static OFCondition getMACIDnumber(DcmItem &item, Uint16& macid);
290 
291   /** checks if all tags from tagList are present in tagListOut,
292    *  which is the list of attribute tags actually included in a signature,
293    *  including tags added due to a signature profile, and without tags
294    *  that were absent in the dataset.
295    *  @param tagList list of attribute tags that should be present in the signature, may be NULL
296    *  @param tagListOut list of attribute tags actually present in the signature, must not be NULL
297    *  @return EC_Normal if check succeeds, an error code otherwise
298    */
299   static OFCondition checkListOfSignedTags(const DcmAttributeTag *tagList, const DcmAttributeTag *tagListOut);
300 
301   /** checks if the given tag key is present in tagList
302    *  @param tag tag key
303    *  @param tagList list of tag keys
304    *  @return OFTrue of tag is present in tagList, OFFalse otherwise
305    */
306   static OFBool inTagList(const DcmTagKey &tag, const DcmAttributeTag& tagList);
307 
308   /** returns the current date and time as a DICOM DT string.
309    *  @param str date/time returned in this string.
310    */
311   static void currentDateTime(OFString &str);
312 
313   /// pointer to current item if attached, NULL otherwise
314   DcmItem *currentItem;
315 
316   /// pointer to mac parameters sequence of attached item, may be NULL if not attached or not yet present
317   DcmSequenceOfItems *macParametersSq;
318 
319   /// pointer to digital signatures sequence of attached item, may be NULL if not attached or not yet present
320   DcmSequenceOfItems *signatureSq;
321 
322   /// if nonzero, the data fed to the MAC algorithm is also stored in this file.
323   FILE *dumpFile;
324 
325   /// pointer to currently selected signature item
326   DcmItem *selectedSignatureItem;
327 
328   /// pointer to currently selected mac parameters item
329   DcmItem *selectedMacParametersItem;
330 
331   /// pointer to certificate for currently selected signature item
332   SiCertificate *selectedCertificate;
333 
334   /// pointer to certified timestamp for currently selected signature item
335   SiTimeStamp *selectedTimestamp;
336 };
337 
338 #endif
339 #endif
340