1 /** NSS signature generator
2  *
3  * Mozilla has two APIs for generating signatures (older SEC_PKCS7)
4  * and newer SMIME (CMS). We are using newer API.
5  *
6  * You have to have certificate (CERTCertificate * )which will be
7  * used for signing.
8  */
9 
10 #include "NSSSignatureGenerator.h"
11 
12 #include <podofo.h>
13 
sm_write_stream(void * arg,const char * buf,unsigned long len)14 void NSSSignatureGenerator::sm_write_stream(void *arg, const char *buf, unsigned long len)
15 {
16     NSSSignatureGenerator* pThis = static_cast<NSSSignatureGenerator*>(arg);
17     pThis->signature.append(buf, len);
18 }
19 
getDigestAlgor(CERTCertificate * pCert)20 SECOidTag NSSSignatureGenerator::getDigestAlgor(CERTCertificate *pCert)
21 {
22     SECAlgorithmID *algID = &pCert->signature;
23     if(algID==NULL) return SEC_OID_MD5;
24     SECOidTag algOIDTag = SECOID_FindOIDTag(&algID->algorithm);
25     switch(algOIDTag)
26     {
27 		case SEC_OID_MD5:
28 		case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION:
29 		case SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC:
30 			return SEC_OID_MD5;
31 		case SEC_OID_SHA1:
32 		case SEC_OID_ISO_SHA_WITH_RSA_SIGNATURE:
33 		case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION:
34 		case SEC_OID_PKCS5_PBE_WITH_SHA1_AND_DES_CBC:
35 		case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC4:
36 		case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC4:
37 		case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC:
38 		case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
39 		case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
40 		case SEC_OID_PKCS12_RSA_SIGNATURE_WITH_SHA1_DIGEST:
41 		case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST:
42 		case SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST:
43 		case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC4:
44 		case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC4:
45 		case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC:
46 		case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_2KEY_TRIPLE_DES_CBC:
47 		case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
48 		case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
49 		case SEC_OID_HMAC_SHA1:
50 			return SEC_OID_SHA1;
51 		case SEC_OID_SHA256:
52 		case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION:
53 		case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE:
54 		case SEC_OID_HMAC_SHA256:
55 			return SEC_OID_SHA256;
56 		case SEC_OID_SHA384:
57 		case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION:
58 		case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE:
59 		case SEC_OID_HMAC_SHA384:
60 			return SEC_OID_SHA384;
61 		case SEC_OID_SHA512:
62 		case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION:
63 		case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE:
64 		case SEC_OID_HMAC_SHA512:
65 			return SEC_OID_SHA512;
66     }
67     PODOFO_ASSERT(0);
68     return SEC_OID_MD5;
69 }
70 
71 // create message with signature
createSign(CERTCertificate * cert)72 NSSCMSMessage* NSSSignatureGenerator::createSign(CERTCertificate *cert)
73 {
74     NSSCMSSignedData *sigd = NULL;
75     NSSCMSContentInfo *cinfo = NULL;
76     NSSCMSSignerInfo *signerinfo = NULL;
77 
78     SECOidTag hash=getDigestAlgor(cert);
79 
80     NSSCMSMessage *cmsg = NSS_CMSMessage_Create(NULL); // create a message on its own pool
81     if (cmsg == NULL) return NULL;
82     if ((sigd = NSS_CMSSignedData_Create(cmsg)) == NULL) {
83         NSS_CMSMessage_Destroy(cmsg); return NULL;
84     }
85     cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
86     if (NSS_CMSContentInfo_SetContent_SignedData(cmsg, cinfo, sigd) != SECSuccess) {
87         NSS_CMSMessage_Destroy(cmsg); return NULL;
88     }
89 
90     // if !detatched, the contentinfo will alloc a data item for us
91     cinfo = NSS_CMSSignedData_GetContentInfo(sigd);
92     if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_TRUE ) != SECSuccess) {
93         NSS_CMSMessage_Destroy(cmsg); return NULL;
94     }
95 
96     signerinfo = NSS_CMSSignerInfo_Create(cmsg, cert, hash);
97     if (signerinfo == NULL) { NSS_CMSMessage_Destroy(cmsg); return NULL; }
98 
99     // we want the cert chain included for this one
100     if (NSS_CMSSignerInfo_IncludeCerts(signerinfo, NSSCMSCM_CertChainWithRoot, certUsageEmailSigner) != SECSuccess)
101     { NSS_CMSMessage_Destroy(cmsg); return NULL; }
102 
103     // SMIME RFC says signing time should always be added
104     if (NSS_CMSSignerInfo_AddSigningTime(signerinfo, PR_Now()) != SECSuccess) { NSS_CMSMessage_Destroy(cmsg); return NULL; }
105 
106     if (NSS_CMSSignedData_AddSignerInfo(sigd, signerinfo) != SECSuccess) {
107         NSS_CMSMessage_Destroy(cmsg); return NULL; }
108     return cmsg;
109 }
110 
111 
NSSSignatureGenerator(CERTCertificate * pCert)112 NSSSignatureGenerator::NSSSignatureGenerator(CERTCertificate *pCert)
113     :pCert(pCert)
114 {
115     pSignature = NULL;
116 }
117 
~NSSSignatureGenerator()118 NSSSignatureGenerator::~NSSSignatureGenerator()
119 {
120     delete pSignature;
121     if(enc!=NULL) NSS_CMSEncoder_Cancel(enc);
122     if(cmsg!=NULL) NSS_CMSMessage_Destroy(cmsg);
123 }
124 
init()125 bool NSSSignatureGenerator::init()
126 {
127     cmsg = createSign(pCert);
128     if(cmsg==NULL) return false;
129     // Prepare encoder
130     enc = NSS_CMSEncoder_Start(cmsg,
131                                sm_write_stream, this, // DER output callback
132                                NULL, NULL,     // destination storage
133                                NULL, NULL,	   // password callback
134                                NULL, NULL,     // decrypt key callback
135                                NULL, NULL );   // detached digests
136 }
137 
appendData(const char * pData,unsigned int dataSize)138 bool NSSSignatureGenerator::appendData(const char *pData, unsigned int dataSize)
139 {
140     if(enc == NULL) return false;
141 
142     if (NSS_CMSEncoder_Update(enc, pData, dataSize) != SECSuccess) {
143         NSS_CMSEncoder_Cancel(enc);
144         enc = NULL;
145         return false;
146     }
147     return true;
148 }
149 
finishData()150 bool NSSSignatureGenerator::finishData()
151 {
152     if(enc == NULL) return false;
153 
154     if (NSS_CMSEncoder_Finish(enc) != SECSuccess) {
155         enc = NULL;
156         return false;
157     }
158     enc = NULL;
159     return true;
160 }
161 
getSignature()162 const PoDoFo::PdfData* NSSSignatureGenerator::getSignature()
163 {
164     if(pSignature==NULL) {
165         pSignature = new PoDoFo::PdfData(signature.c_str(), signature.size());
166     }
167     return pSignature;
168 }
169