s/qmail 4.3.20
Next generation secure email transport
Loading...
Searching...
No Matches
dkimsign.cpp
Go to the documentation of this file.
1/*****************************************************************************
2* Copyright 2005 Alt-N Technologies, Ltd.
3*
4* Licensed under the Apache License, Version 2.0 (the "License");
5* you may not use this file except in compliance with the License.
6* You may obtain a copy of the License at
7*
8* http://www.apache.org/licenses/LICENSE-2.0
9*
10* This code incorporates intellectual property owned by Yahoo! and licensed
11* pursuant to the Yahoo! DomainKeys Patent License Agreement.
12*
13* Unless required by applicable law or agreed to in writing, software
14* distributed under the License is distributed on an "AS IS" BASIS,
15* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16* See the License for the specific language governing permissions and
17* limitations under the License.
18*
19* Changes done by ¢feh@fehcom.de obeying the above license
20*
21*****************************************************************************/
22
23#define _strnicmp strncasecmp
24#define _stricmp strcasecmp
25#define LOWORD(l) ((unsigned)(l) & 0xffff)
26#define HIWORD(l) ((unsigned)(l) >> 16)
27
28#include <string.h>
29#include <map>
30
31#include "dkim.h"
32#include "dkimsign.h"
33
34/*****************************************************************************
35*
36* Generating Ed25519 signed message:
37*
38* 1. RSA SHA1/SHA256 signatures are generated in streaming mode together with
39* their hashes. Two different 'contexts' (ctx) are used here:
40* m_Hdr_shaXctx => Used for signing (EVP_Sign...) -- covering the header only
41* m_[B,E]dy_shaXctx => Used for hashing (EVP_Digest..) -- covering the body only
42*
43* 2. Private keys
44* For hybrid signing we need two distinct keys:
45* - RSAKey
46* - ECCKey
47* These private keys needs to be passed concurrently to the signature functions.
48* Given those keys, the signature operation itself is executed in one step.
49*
50* 3. Public keys
51* The 'public keys' need to be deployed in the DNS:
52* - The RSA public key is DER-header enriched base64-encoded; thus is 9 byte larger
53* than the 'naked' public key, which size depends on the given parameters.
54* - The Ed25519 public key is also base64-encoded with a constant length of 60 byte.
55*
56* 4. DKIM message preparation scheme
57* According to RFC 6376 Sec. 3.7, we have a conducted hash for
58* - the previously available headers in the message;
59* selected and given in order by h=...,
60* - any existing DKIM signature fields b=...,
61* - except for previous added 'X-Authentication ...' header fields,
62* - and all (new) synthezised DKIM header tokens; except of course for the
63* signature itself - treated as 'null string': b="".
64* All this is subject of canonicalization (adding/removing CRLF, whitespaces ...).
65+ As a result, the input for further calculations depends on this order given.
66*
67* Results following the 'preparation scheme':
68* - The message body hash is included in the DKIM header => bh=[m_[B,E]dy_shaXctx].
69* - The message signature (including the result of bh=...) => b=[m_Hdr_shaXctx]
70*
71* We consider SHA256 as default hash function and SHA1 as exception (on demand).
72*
73* 5. Generating (ECC) signatures
74* According to RFC 8032 Sect 4., we have two possible Ed25519 signature schemes:
75*
76* a) PureEd25519, as a one shot signature calculation swallowing the
77* complete message and employing a shortened SHA-512 hash input.
78* b) HashEd25519 working again in 'streaming mode' and permitting a choice
79* for the hash function - which is in RFC 8463 - defined to be SHA-256.
80*
81* RFC 8463 in Sect 3 is a bit ambiguous about the signing function:
82* Ed25519-256 vs. PureEd25519.
83* In fact (after consulting John Levine), it is PureEd25519.
84*
85* In order to allow parallel RSA/Ed25519 processing, we need to generate:
86* m_Hdr_sha256ctx => Used for RSA signatures
87* m_Bdy_sha256ctx => The SHA256 hash of selected header parts and body (RSA)
88* m_Edy_sha256ctx => The SHA256 hash of selected header parts and body (Ed25519)
89* m_Hdr_ed25519ctx => The signature of the messsage header using PureEd25519
90* following the 'preparation' scheme
91*
92* Now, two cryptographic informations are provided in the header:
93* bh=[m_Edy_sha256ctx] => The SHA256 digest of the message (BodyHash),
94* b=[m_Hdr_ed25519ctx] => The PureED25519 signature.
95* including the value of bh=... (EmailSignature)
96* having a length of 512 bits => 64 bytes.
97*
98* 6. Hybrid signatures (RSA and Ed25519)
99* They involve
100* m_Hdr_sha256ctx => Used for RSA signatures
101* m_Hdr_ed25519ctx => PureED25519 signature
102* m_Bdy_sha256ctx => SHA256 digest of the message (BodyHash) for RSA
103* m_Edy_sha256ctx => SHA256 digest of the message (BodyHash) for Ed25519
104*
105* The EVP_DigestFinal routine has to be replaced by EVP_DigestFinal_ex.
106* However; after the first call, its content seems to be garbeled.
107* A common MD for both RSA and Ed2551 seems to be infeasible.
108*
109* ------
110*
111* The particular function and variable names chosen here do not obviously match
112* what they are intended to do. However, in order to keep traceablility of the
113* changes, I left those untouched.
114*
115*****************************************************************************/
116
118{
120 m_pfnHdrCallback = NULL;
121
122 m_Hdr_sha1ctx = EVP_MD_CTX_create();
123 EVP_SignInit_ex(m_Hdr_sha1ctx,EVP_sha1(),NULL);
124
125 m_Hdr_sha256ctx = EVP_MD_CTX_create();
126 EVP_SignInit_ex(m_Hdr_sha256ctx,EVP_sha256(),NULL);
127
128 m_Bdy_sha1ctx = EVP_MD_CTX_create();
129 EVP_DigestInit_ex(m_Bdy_sha1ctx,EVP_sha1(),NULL);
130
131 m_Bdy_sha256ctx = EVP_MD_CTX_create();
132 EVP_DigestInit_ex(m_Bdy_sha256ctx,EVP_sha256(),NULL);
133
134 m_Hdr_ed25519ctx = EVP_MD_CTX_create();
135
136 m_Edy_sha256ctx = EVP_MD_CTX_create();
137 EVP_DigestInit_ex(m_Edy_sha256ctx,EVP_sha256(),NULL);
138}
139
141{
142 EVP_MD_CTX_free(m_Hdr_sha1ctx);
143 EVP_MD_CTX_free(m_Hdr_sha256ctx);
144 EVP_MD_CTX_free(m_Hdr_ed25519ctx);
145 EVP_MD_CTX_free(m_Bdy_sha1ctx);
146 EVP_MD_CTX_free(m_Bdy_sha256ctx);
147 EVP_MD_CTX_free(m_Edy_sha256ctx);
148}
149
151//
152// Init - save the options
153//
156{
157 int nRet = CDKIMBase::Init();
158
159 m_Canon = pOptions->nCanon;
160
161 // as of draft 01, these are the only allowed signing types:
166 }
167
168 sSelector.assign(pOptions->szSelector);
169 eSelector.assign(pOptions->szSelectorE);
170
172
173 sDomain.assign(pOptions->szDomain);
174
176
177 m_nBodyLength = 0;
178
179 m_ExpireTime = pOptions->expireTime;
180
181 sIdentity.assign(pOptions->szIdentity);
182
186
187 // NOTE: the following line is not backwards compatible with MD 8.0.3
188 // because the szRequiredHeaders member was added after the release
189 //sRequiredHeaders.assign(pOptions->szRequiredHeaders);
190
191 //make sure there is a colon after the last header in the list
192 if ((sRequiredHeaders.size() > 0) &&
193 sRequiredHeaders.at(sRequiredHeaders.size() - 1) != ':') {
194 sRequiredHeaders.append(":");
195 }
196
197 m_nHash = pOptions->nHash;
199 m_sCopiedHeaders.erase();
200
201 // Initializes ED25519 header fields SigHdrs
202 SigHdrs.assign("");
203 m_SigHdrs = 0;
204
205 return nRet;
206}
207
209//
210// Hash - update the hash
211//
213void CDKIMSign::Hash(const char *szBuffer,int nBufLength,bool bHdr)
214{
215
234 if (bHdr) { /* Generate signature: b=... */
235 if ((m_nHash == DKIM_HASH_SHA1) ||
237 EVP_SignUpdate(m_Hdr_sha1ctx,szBuffer,nBufLength);
238 if ((m_nHash == DKIM_HASH_SHA256) ||
241 EVP_SignUpdate(m_Hdr_sha256ctx,szBuffer,nBufLength);
242 if ((m_nHash == DKIM_HASH_ED25519) ||
244 SigHdrs.append(szBuffer,nBufLength);
245 m_SigHdrs += nBufLength;
246 }
247 } else { /* lets go for body hash values: bh=... (either SHA1 or SHA256) */
248 if ((m_nHash == DKIM_HASH_SHA1) ||
250 EVP_DigestUpdate(m_Bdy_sha1ctx,szBuffer,nBufLength);
251 if (m_nHash != DKIM_HASH_SHA1)
252 EVP_DigestUpdate(m_Bdy_sha256ctx,szBuffer,nBufLength);
253 if ((m_nHash == DKIM_HASH_ED25519) ||
255 EVP_DigestUpdate(m_Edy_sha256ctx,szBuffer,nBufLength);
256 }
257}
258
260//
261// SignThisTag - return boolean whether or not to sign this tag
262//
264bool CDKIMSign::SignThisTag(const string& sTag)
265{
266 bool bRet = true;
267
268 if (_strnicmp(sTag.c_str(),"X-",2) == 0 ||
269 _stricmp(sTag.c_str(),"Authentication-Results:") == 0 ||
270 _stricmp(sTag.c_str(),"Return-Path:") == 0) {
271 bRet = false;
272 }
273
274 return bRet;
275}
276
277bool ConvertHeaderToQuotedPrintable(const char* source, char* dest)
278{
279 bool bConvert = false;
280
281 // do quoted printable
282 static unsigned char hexchars[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
283
284 unsigned char *d = (unsigned char*)dest;
285 for (const unsigned char *s = (const unsigned char *)source; *s != '\0'; s++)
286 {
287 if (*s >= 33 && *s <= 126 && *s != '=' && *s != ':' && *s != ';' && *s != '|') {
288 *d++ = *s;
289 } else {
290 bConvert = true;
291 *d++ = '=';
292 *d++ = hexchars[*s >> 4];
293 *d++ = hexchars[*s & 15];
294 }
295 }
296 *d = '\0';
297
298 return bConvert;
299}
300
302//
303// GetHeaderParams - Extract any needed header parameters
304//
306void CDKIMSign::GetHeaderParams(const string &sHdr)
307{
308 if (_strnicmp(sHdr.c_str(),"X",1) == 0) return;
309 if (_strnicmp(sHdr.c_str(),"From:",5) == 0) { sFrom.assign(sHdr.c_str() + 5); }
310 if (_strnicmp(sHdr.c_str(),"Sender:",7) == 0) { sSender.assign(sHdr.c_str() + 7); }
311
313 string::size_type pos = sHdr.find(':');
314
315 if (pos != string::npos) {
316 string sTag, sValue;
317 char *workBuffer = new char[sHdr.size() * 3 + 1];
318
319 sTag.assign(sHdr.substr(0,pos));
320 sValue.assign(sHdr.substr(pos + 1,string::npos));
321
322 ConvertHeaderToQuotedPrintable(sTag.c_str(),workBuffer);
323 if (!m_sCopiedHeaders.empty()) { m_sCopiedHeaders.append("|"); }
324 m_sCopiedHeaders.append(workBuffer); m_sCopiedHeaders.append(":");
325 ConvertHeaderToQuotedPrintable(sValue.c_str(),workBuffer);
326 m_sCopiedHeaders.append(workBuffer);
327
328 delete[] workBuffer;
329 }
330 }
331}
332
334//
335// ProcessHeaders - sign headers and save needed parameters (this is a lie)
336//
339{
340 map<string,list<string>::reverse_iterator> IterMap;
341 map<string,list<string>::reverse_iterator>::iterator IterMapIter;
342 list<string>::reverse_iterator riter;
343 list<string>::iterator iter;
344 string sTag;
345 bool bFromHeaderFound = false;
346
347 // walk the header list
348 for (iter = HeaderList.begin(); iter != HeaderList.end(); iter++) {
349 sTag.assign(*iter);
350
351 // look for a colon
352 string::size_type pos = sTag.find(':');
353
354 if (pos != string::npos) {
355 int nSignThisTag = 1;
356
357 // hack off anything past the colon
358 sTag.erase(pos + 1,string::npos);
359
360 // is this the From: header?
361 if (_stricmp(sTag.c_str(),"From:") == 0) {
362 bFromHeaderFound = true;
363 nSignThisTag = 1;
364 IsRequiredHeader(sTag); // remove from required header list
365 }
366 // is this in the list of headers that must be signed?
367 else if (IsRequiredHeader(sTag)) {
368 nSignThisTag = 1;
369 }
370 else {
371 if(m_pfnHdrCallback) {
372 nSignThisTag = m_pfnHdrCallback(iter->c_str());
373 } else {
374 nSignThisTag = SignThisTag(sTag) ? 1 : 0;
375 }
376 }
377
378 // save header parameters
379 GetHeaderParams(*iter);
380
381 if (nSignThisTag > 0) {
382 // add this tag to h=
383 hParam.append(sTag);
384
385 IterMapIter = IterMap.find(sTag);
386
387 riter = (IterMapIter == IterMap.end()) ? HeaderList.rbegin() : IterMapIter->second;
388
389 // walk the list in reverse looking for the last instance of this header
390 while (riter != HeaderList.rend()) {
391 if (_strnicmp(riter->c_str(),sTag.c_str(),sTag.size()) == 0) {
392 ProcessHeader(*riter);
393
394 // save the reverse iterator position for this tag
395 riter++;
396 IterMap[sTag] = riter;
397 break;
398 }
399 riter++;
400 }
401 }
402 }
403 }
404
405 if(!bFromHeaderFound) {
406 string sFrom("From:");
407 hParam.append(sFrom);
408 IsRequiredHeader(sFrom); // remove from required header list
409// Hash("\r\n",2);
410 }
411
412 hParam.append(sRequiredHeaders);
413
414// string::size_type end = sRequiredHeaders.find(':');
415// while (end != string::npos)
416// {
417// Hash("\r\n",2);
418// end = sRequiredHeaders.find(':', end+1);
419// }
420
421 // remove the last colon from h=
422 if (hParam.at(hParam.size() - 1) == ':')
423 hParam.erase(hParam.size() - 1,string::npos);
424
425 return DKIM_SUCCESS;
426}
427
428void CDKIMSign::ProcessHeader(const string &sHdr)
429{
430 switch (HIWORD(m_Canon)) {
432 Hash(sHdr.c_str(),sHdr.size(),true);
433 Hash("\r\n",2,true);
434 break;
435
436 case DKIM_CANON_NOWSP: {
437 string sTemp = sHdr;
438 RemoveSWSP(sTemp);
439
440 // convert characters before ':' to lower case
441 for (char *s = (char*)sTemp.c_str(); *s != '\0' && *s != ':'; s++) {
442 if (*s >= 'A' && *s <= 'Z')
443 *s += 'a' - 'A';
444 }
445
446 Hash(sTemp.c_str(),sTemp.size(),true);
447 Hash("\r\n",2,true);
448 }
449 break;
450
451 case DKIM_CANON_RELAXED: {
452 string sTemp = RelaxHeader(sHdr);
453 Hash(sTemp.c_str(),sTemp.length(),true);
454 Hash("\r\n",2,true);
455 }
456 break;
457 }
458}
459
460int CDKIMSign::ProcessBody(char *szBuffer,int nBufLength,bool bEOF)
461{
462 switch(LOWORD(m_Canon)) {
464 if (nBufLength > 0) {
465 while (m_EmptyLineCount > 0) {
466 Hash("\r\n",2,false);
467 m_nBodyLength += 2;
469 }
470 Hash(szBuffer,nBufLength,false);
471 Hash("\r\n",2,false);
472 m_nBodyLength += nBufLength + 2;
473 } else {
475 if (bEOF) {
476 Hash("\r\n",2,false);
477 m_nBodyLength += 2;
478 }
479 }
480 break;
481 case DKIM_CANON_NOWSP:
482 RemoveSWSP(szBuffer,nBufLength);
483 if (nBufLength > 0) {
484 Hash(szBuffer,nBufLength,false);
485 m_nBodyLength += nBufLength;
486 }
487 break;
489 CompressSWSP(szBuffer,nBufLength);
490 if (nBufLength > 0) {
491 while (m_EmptyLineCount > 0) {
492 Hash("\r\n",2,false);
493 m_nBodyLength += 2;
495 }
496 Hash(szBuffer,nBufLength,false);
497 m_nBodyLength += nBufLength;
498 if (!bEOF) {
499 Hash("\r\n",2,false);
500 m_nBodyLength += 2;
501 }
502 } else
504 break;
505 }
506
507 return DKIM_SUCCESS;
508}
509
511{
512 string::size_type pos, poss, pose; // npos is iterator
513 string sAddress;
514
515 if (!sFrom.empty()) {
516 sAddress.assign(sFrom);
517 } else if (!sSender.empty()) {
518 sAddress.assign(sSender);
519 } else {
520 return false;
521 }
522
523 // simple for now, beef it up later
524
525 // remove '<' and anything before it
526 poss = sAddress.find('<');
527 if (poss != string::npos)
528 sAddress.erase(0,poss);
529
530 // remove '>' and anything after it
531 pose = sAddress.find('>');
532 if (pose != string::npos)
533 sAddress.erase(pose,string::npos);
534
535 if (pose == poss + 1) return false;
536
537 // look for '@' symbol
538 pos = sAddress.find('@');
539 if (pos == string::npos)
540 return false;
541
542 if (sDomain.empty()) {
543 sDomain.assign (sAddress.c_str() + pos + 1);
545 }
546
547 return true;
548}
549
551//
552// InitSig - initialize signature folding algorithm
553//
556{
557 m_sSig.reserve(1024);
558 m_sSig.assign("DKIM-Signature:");
559 m_nSigPos = m_sSig.size();
560}
561
563//
564// AddTagToSig - add tag and value to signature folding if necessary
565// if bFold, fold at cbrk char
566//
568void CDKIMSign::AddTagToSig(const char* const Tag,const string &sValue,char cbrk,bool bFold)
569{
570 int nTagLen = strlen(Tag);
571
572 AddInterTagSpace((!bFold) ? sValue.size() + nTagLen + 2 : nTagLen + 2);
573
574 m_sSig.append(Tag);
575 m_sSig.append("=");
576 m_nSigPos += 1 + nTagLen;
577
578 if (!bFold) {
579 m_sSig.append(sValue);
580 m_nSigPos += sValue.size();
581 } else {
582 AddFoldedValueToSig(sValue,cbrk);
583 }
584 m_sSig.append(";");
585 m_nSigPos++;
586}
587
589//
590// AddTagToSig - add tag and numeric value to signature folding if necessary
591//
593void CDKIMSign::AddTagToSig(const char* const Tag,unsigned long nValue)
594{
595 char szValue[64];
596 sprintf(szValue,"%lu",nValue);
597 AddTagToSig(Tag,szValue,0,false);
598}
599
601//
602// AddInterTagSpace - add space or fold here
603//
605void CDKIMSign::AddInterTagSpace(int nSizeOfNextTag)
606{
607 if (m_nSigPos + nSizeOfNextTag + 1 > OptimalHeaderLineLength) {
608// m_sSig.append("\r\n\t");
609 m_sSig.append("\r\n "); /* s/qmail style */
610 m_nSigPos = 1;
611 } else {
612 m_sSig.append(" ");
613 m_nSigPos++;
614 }
615}
616
618//
619// AddTagToSig - add value to signature folding if necessary
620// if cbrk == 0 fold anywhere, otherwise fold only at cbrk
621//
623void CDKIMSign::AddFoldedValueToSig(const string &sValue,char cbrk)
624{
625 string::size_type pos = 0;
626
627 if (cbrk == 0) {
628 // fold anywhere
629 while (pos < sValue.size()) {
630 string::size_type len = OptimalHeaderLineLength - m_nSigPos;
631 if (len > sValue.size() - pos)
632 len = sValue.size() - pos;
633 m_sSig.append(sValue.substr(pos,len));
634 m_nSigPos += len;
635 pos += len;
636
637 if (pos < sValue.size()) {
638// m_sSig.append("\r\n\t");
639 m_sSig.append("\r\n "); /* s/qmail style */
640 m_nSigPos = 1;
641 }
642 }
643 } else {
644 // fold only at cbrk
645 while (pos < sValue.size()) {
646 string::size_type len = OptimalHeaderLineLength - m_nSigPos;
647 string::size_type brkpos;
648
649 if (sValue.size() - pos < len) {
650 brkpos = sValue.size();
651 } else {
652 brkpos = sValue.rfind(cbrk,pos + len);
653 }
654
655 if (brkpos == string::npos || brkpos < pos) {
656 brkpos = sValue.find(cbrk,pos);
657 if (brkpos == string::npos) {
658 brkpos = sValue.size();
659 }
660 }
661
662 len = brkpos - pos + 1;
663
664 m_sSig.append(sValue.substr(pos,len));
665
666 m_nSigPos += len;
667 pos += len;
668
669 if (pos < sValue.size()) {
670// m_sSig.append("\r\n\t");
671 m_sSig.append("\r\n "); /* s/qmail style */
672 m_nSigPos = 1;
673 }
674 }
675 }
676}
677
679//
680// GetSig - compute hash and return signature header in szSignature
681//
683int CDKIMSign::GetSig2(char* szRSAKey,char* szECCKey,char** pszSignature)
684{
685 if (szRSAKey == NULL && szECCKey == NULL) {
687 }
688
689 if (pszSignature == NULL) {
691 }
692
693 int nRet = AssembleReturnedSig(szRSAKey,szECCKey);
694
695 if (nRet != DKIM_SUCCESS)
696 return nRet;
697
698 *pszSignature = (char*)m_sReturnedSig.c_str();
699
700 return DKIM_SUCCESS;
701}
702
703
705//
706// IsRequiredHeader - Check if header in required list. If so, delete
707// header from list.
708//
710bool CDKIMSign::IsRequiredHeader(const string& sTag)
711{
712 string::size_type start = 0;
713 string::size_type end = sRequiredHeaders.find(':');
714
715 while (end != string::npos) {
716 // check for a zero-length header
717 if(start == end) {
718 sRequiredHeaders.erase(start,1);
719 } else {
720 if (_stricmp(sTag.c_str(),sRequiredHeaders.substr(start,end - start + 1).c_str()) == 0) {
721 sRequiredHeaders.erase(start,end - start + 1);
722 return true;
723 } else {
724 start = end + 1;
725 }
726 }
727
728 end = sRequiredHeaders.find(':',start);
729 }
730
731 return false;
732}
734//
735// ConstructSignature
736//
737// Here, we don't construct the 'signature' but rather the DKIM header
738// multiply and indidually crafted for each distinct nSigAlg method
739//
740// nSigAlg: DKIM_HASH_SHA1, DKIM_HASH_SHA256, DKIM_HASH_ED25519
741//
743int CDKIMSign::ConstructSignature(char* szPrivKey,int nSigAlg)
744{
745 string sSignedSig;
746 unsigned char* sig;
747 EVP_PKEY *pkey = 0;
748 BIO *bio, *b64;
749 unsigned int siglen;
750 int size;
751 int len;
752 char* buf;
753 int nSignRet;
754
755 /* construct the DKIM-Signature: header and add to hash */
756 InitSig();
757
758 AddTagToSig("v","1",0,false);
759
760 switch (nSigAlg) {
761 case DKIM_HASH_SHA1:
762 AddTagToSig("a","rsa-sha1",0,false); break;
763 case DKIM_HASH_SHA256:
764 AddTagToSig("a","rsa-sha256",0,false); break;
765 case DKIM_HASH_ED25519:
766 AddTagToSig("a","ed25519-sha256",0,false); break;
767 }
768
769 switch (m_Canon) {
770 case DKIM_SIGN_SIMPLE:
771 AddTagToSig("c","simple/simple",0,false); break;
773 AddTagToSig("c","simple/relaxed",0,false); break;
775 AddTagToSig("c","relaxed/relaxed",0,false); break;
777 AddTagToSig("c","relaxed/simple",0,false); break;
778 }
779
780 AddTagToSig("d",sDomain,0,false);
781 if (nSigAlg == DKIM_HASH_ED25519)
782 AddTagToSig("s",eSelector,0,false);
783 else
784 AddTagToSig("s",sSelector,0,false);
786 if (m_nIncludeTimeStamp != 0) { time_t t; time(&t); AddTagToSig("t",t); }
787 if (m_ExpireTime != 0) { AddTagToSig("x",m_ExpireTime); }
788 if (!sIdentity.empty()) { AddTagToSig("i",sIdentity,0,false); }
789 if (m_nIncludeQueryMethod) { AddTagToSig("q","dns/txt",0,false); }
790
791 AddTagToSig("h",hParam,':',true); // copied headers follow the ':'
793
794 /* Set up context for (body) hash */
795
796 unsigned char Hash[4096];
797 unsigned int nHashLen = 0;
798
799 switch (nSigAlg) {
800 case DKIM_HASH_SHA1:
801 EVP_DigestFinal_ex(m_Bdy_sha1ctx,Hash,&nHashLen); break;
802 case DKIM_HASH_SHA256:
803 EVP_DigestFinal_ex(m_Bdy_sha256ctx,Hash,&nHashLen); break;
804 case DKIM_HASH_ED25519:
805 EVP_DigestFinal_ex(m_Edy_sha256ctx,Hash,&nHashLen); break;
806 }
807
808 bio = BIO_new(BIO_s_mem());
809 if (!bio) return DKIM_OUT_OF_MEMORY;
810
811 b64 = BIO_new(BIO_f_base64());
812 if (!b64) {
813 BIO_free(bio);
814 return DKIM_OUT_OF_MEMORY;
815 }
816 BIO_set_flags(b64,BIO_FLAGS_BASE64_NO_NL);
817 BIO_push(b64,bio);
818 if (BIO_write(b64,Hash,nHashLen) < (int)nHashLen) {
819 BIO_free_all(b64);
820 return DKIM_OUT_OF_MEMORY;
821 }
822 BIO_flush(b64);
823
824 len = nHashLen * 2;
825 buf = new char[len];
826
827 if (buf == NULL) {
828 BIO_free_all(b64);
829 return DKIM_OUT_OF_MEMORY;
830 }
831
832 size = BIO_read(bio,buf,len);
833 BIO_free_all(b64);
834
835 // this should never happen
836 if (size >= len) {
837 delete[] buf;
838 return DKIM_OUT_OF_MEMORY;
839 }
840
841 buf[size] = '\0';
842 AddTagToSig("bh",buf,0,true);
843 delete[] buf;
844
846
847 m_sSig.append("b=");
848 m_nSigPos += 2;
849
850 // Force a full copy - no reference copies please
851 sSignedSig.assign(m_sSig.c_str());
852
853 // note that since we're not calling hash here, need to dump this
854 // to the debug file if you want the full canonical form
855
856 string sTemp;
857
859 sTemp = RelaxHeader(sSignedSig);
860 } else {
861 sTemp = sSignedSig.c_str();
862 }
863
864 /* Update streaming signatures */
865
866 switch (nSigAlg) {
867 case DKIM_HASH_SHA1:
868 EVP_SignUpdate(m_Hdr_sha1ctx,sTemp.c_str(),sTemp.size()); break;
869 case DKIM_HASH_SHA256:
870 EVP_SignUpdate(m_Hdr_sha256ctx,sTemp.c_str(),sTemp.size()); break;
871 case DKIM_HASH_ED25519:
872 SigHdrs.append(sTemp.c_str(),sTemp.size());
873 m_SigHdrs += sTemp.size(); break;
874 }
875
876 bio = BIO_new_mem_buf(szPrivKey, -1);
877 if (bio == NULL) return DKIM_OUT_OF_MEMORY;
878
879 pkey = PEM_read_bio_PrivateKey(bio,NULL,NULL,NULL); // FIXME - done
880 BIO_free(bio);
881
882 if (!pkey) { return DKIM_BAD_PRIVATE_KEY; }
883 siglen = EVP_PKEY_size(pkey);
884
885 sig = (unsigned char*) OPENSSL_malloc(siglen);
886 if (sig == NULL) {
887 EVP_PKEY_free(pkey);
888 return DKIM_OUT_OF_MEMORY;
889 }
890
891 /* Finish streaming signature and potentially go for Ed25519 signatures */
892
893 size_t sig_len;
894 unsigned char* SignMsg;
895
896 switch (nSigAlg) {
897 case DKIM_HASH_SHA1:
898 nSignRet = EVP_SignFinal(m_Hdr_sha1ctx,sig,&siglen,pkey); break;
899 case DKIM_HASH_SHA256:
900 nSignRet = EVP_SignFinal(m_Hdr_sha256ctx,sig,&siglen,pkey); break;
901 case DKIM_HASH_ED25519:
902 EVP_DigestSignInit(m_Hdr_ed25519ctx,NULL,NULL,NULL,pkey);
903 SignMsg = (unsigned char*) SigHdrs.c_str();
904 EVP_DigestSign(m_Hdr_ed25519ctx,NULL,&sig_len,SignMsg,m_SigHdrs);
905 sig = (unsigned char*) OPENSSL_malloc(sig_len);
906 nSignRet = EVP_DigestSign(m_Hdr_ed25519ctx,sig,&sig_len,SignMsg,m_SigHdrs);
907 siglen = (unsigned int) sig_len; break;
908 }
909 EVP_PKEY_free(pkey);
910
911 if (!nSignRet) {
912 OPENSSL_free(sig);
913 return DKIM_BAD_PRIVATE_KEY; // key too small
914 }
915
916 bio = BIO_new(BIO_s_mem());
917 if (!bio) {
918 return DKIM_OUT_OF_MEMORY;
919 }
920
921 b64 = BIO_new(BIO_f_base64());
922 if (!b64) {
923 BIO_free(bio);
924 return DKIM_OUT_OF_MEMORY;
925 }
926
927 BIO_set_flags(b64,BIO_FLAGS_BASE64_NO_NL);
928 BIO_push(b64,bio);
929
930 if (BIO_write(b64,sig,siglen) < (int) siglen) {
931 OPENSSL_free(sig);
932 BIO_free_all(b64);
933 return DKIM_OUT_OF_MEMORY;
934 }
935 BIO_flush(b64);
936 OPENSSL_free(sig);
937
938 len = siglen * 2;
939 buf = new char[len];
940
941 if (buf == NULL) {
942 BIO_free_all(b64);
943 return DKIM_OUT_OF_MEMORY;
944 }
945
946 size = BIO_read(bio,buf,len);
947 BIO_free_all(b64);
948
949 // this should never happen
950 if (size >= len) {
951 delete[] buf;
952 return DKIM_OUT_OF_MEMORY;
953 }
954
955 buf[size] = '\0';
957 delete[] buf;
958 return DKIM_SUCCESS;
959}
960
962//
963// AssembleReturnSig
964//
965// calls ConstructSignature
966// for all different hashes and signature key files
967//
969int CDKIMSign::AssembleReturnedSig(char* szRSAKey,char* szECCKey)
970{
971 int nRet;
972
974 return DKIM_SUCCESS;
975
976 ProcessFinal();
977
978 if (ParseFromAddress() == false) {
979 return DKIM_NO_SENDER;
980 }
981
982 string ed25519Sig, sha256Sig, sha1Sig;
983
984 if ((m_nHash == DKIM_HASH_ED25519) ||
986 nRet = ConstructSignature(szECCKey,DKIM_HASH_ED25519);
987 if (nRet == DKIM_SUCCESS) {
988 ed25519Sig.assign(m_sSig);
989 } else {
990 return nRet;
991 }
992 }
993
994 if ((m_nHash == DKIM_HASH_SHA256) ||
997 nRet = ConstructSignature(szRSAKey,DKIM_HASH_SHA256);
998 if (nRet == DKIM_SUCCESS) {
999 sha256Sig.assign(m_sSig);
1000 } else {
1001 return nRet;
1002 }
1003 }
1004
1005 if ((m_nHash == DKIM_HASH_SHA1) ||
1007 nRet = ConstructSignature(szRSAKey,DKIM_HASH_SHA1);
1008 if (nRet == DKIM_SUCCESS) {
1009 sha1Sig.assign(m_sSig);
1010 } else {
1011 return nRet;
1012 }
1013 }
1014
1015// fclose(fpdebug);
1016// fpdebug = NULL;
1017
1018 if (!ed25519Sig.empty()) {
1019/* if (!m_sReturnedSig.empty()) {
1020 m_sReturnedSig.append("\r\n");
1021 }
1022 */
1023 m_sReturnedSig.assign(ed25519Sig);
1024 }
1025
1026 if (!sha1Sig.empty()) {
1027 if (!m_sReturnedSig.empty()) {
1028 m_sReturnedSig.append("\r\n");
1029 }
1030 m_sReturnedSig.append(sha1Sig);
1031 }
1032
1033 if (!sha256Sig.empty()) {
1034 if (!m_sReturnedSig.empty()) {
1035 m_sReturnedSig.append("\r\n");
1036 }
1037 m_sReturnedSig.append(sha256Sig);
1038 }
1039
1041 return DKIM_SUCCESS;
1042}
static string RelaxHeader(const string &sHeader)
Definition: dkimbase.cpp:293
int ProcessFinal(void)
Definition: dkimbase.cpp:167
static void CompressSWSP(char *pBuffer, int &nBufLength)
Definition: dkimbase.cpp:235
static void RemoveSWSP(char *szBuffer)
Definition: dkimbase.cpp:214
int Init(void)
Definition: dkimbase.cpp:49
list< string > HeaderList
Definition: dkimbase.h:75
string m_sSig
Definition: dkimsign.h:94
DKIMHEADERCALLBACK m_pfnHdrCallback
Definition: dkimsign.h:92
EVP_MD_CTX * m_Hdr_ed25519ctx
Definition: dkimsign.h:65
int GetSig2(char *szRSAPrivKey, char *szECCPrivKey, char **pszSignature)
Definition: dkimsign.cpp:683
bool ParseFromAddress(void)
Definition: dkimsign.cpp:510
int m_nIncludeCopiedHeaders
Definition: dkimsign.h:90
int m_nBodyLength
Definition: dkimsign.h:85
string hParam
Definition: dkimsign.h:75
string sDomain
Definition: dkimsign.h:80
void AddInterTagSpace(int nSizeOfNextTag)
Definition: dkimsign.cpp:605
void ProcessHeader(const string &sHdr)
Definition: dkimsign.cpp:428
void InitSig(void)
Definition: dkimsign.cpp:555
time_t m_ExpireTime
Definition: dkimsign.h:86
EVP_MD_CTX * m_Edy_sha256ctx
Definition: dkimsign.h:69
bool IsRequiredHeader(const string &sTag)
Definition: dkimsign.cpp:710
string sFrom
Definition: dkimsign.h:76
void GetHeaderParams(const string &sHdr)
Definition: dkimsign.cpp:306
EVP_MD_CTX * m_Hdr_sha1ctx
Definition: dkimsign.h:63
bool m_IncludeBodyLengthTag
Definition: dkimsign.h:84
int m_nHash
Definition: dkimsign.h:89
int m_nSigPos
Definition: dkimsign.h:95
EVP_MD_CTX * m_Hdr_sha256ctx
Definition: dkimsign.h:64
string SigHdrs
Definition: dkimsign.h:102
string eSelector
Definition: dkimsign.h:79
int m_SigHdrs
Definition: dkimsign.h:103
virtual int ProcessBody(char *szBuffer, int nBufLength, bool bEOF) override
Definition: dkimsign.cpp:460
EVP_MD_CTX * m_Bdy_sha256ctx
Definition: dkimsign.h:68
int m_EmptyLineCount
Definition: dkimsign.h:73
void AddTagToSig(const char *const Tag, const string &sValue, char cbrk, bool bFold)
Definition: dkimsign.cpp:568
int ConstructSignature(char *szSignKey, int nSigAlg)
Definition: dkimsign.cpp:743
string sIdentity
Definition: dkimsign.h:81
EVP_MD_CTX * m_Bdy_sha1ctx
Definition: dkimsign.h:67
void AddFoldedValueToSig(const string &sValue, char cbrk)
Definition: dkimsign.cpp:623
bool m_bReturnedSigAssembled
Definition: dkimsign.h:98
int AssembleReturnedSig(char *szRSAPrivKey, char *szECCPrivKey)
Definition: dkimsign.cpp:969
virtual int ProcessHeaders(void) override
Definition: dkimsign.cpp:338
string m_sCopiedHeaders
Definition: dkimsign.h:100
bool SignThisTag(const string &sTag)
Definition: dkimsign.cpp:264
string sRequiredHeaders
Definition: dkimsign.h:82
void Hash(const char *szBuffer, int nBufLength, bool bHdr)
Definition: dkimsign.cpp:213
int m_nIncludeTimeStamp
Definition: dkimsign.h:87
string sSelector
Definition: dkimsign.h:78
int m_nIncludeQueryMethod
Definition: dkimsign.h:88
string m_sReturnedSig
Definition: dkimsign.h:97
string sSender
Definition: dkimsign.h:77
int m_Canon
Definition: dkimsign.h:71
@ OptimalHeaderLineLength
Definition: dkimsign.h:41
#define DKIM_BUFFER_TOO_SMALL
Definition: dkim.h:72
#define DKIM_CANON_SIMPLE
Definition: dkim.h:39
#define DKIM_NO_SENDER
Definition: dkim.h:70
#define DKIM_CANON_NOWSP
Definition: dkim.h:40
#define DKIM_OUT_OF_MEMORY
Definition: dkim.h:68
#define DKIM_CANON_RELAXED
Definition: dkim.h:41
#define DKIM_HASH_RSA256_AND_ED25519
Definition: dkim.h:36
#define DKIM_BAD_PRIVATE_KEY
Definition: dkim.h:71
#define DKIM_SIGN_RELAXED
Definition: dkim.h:45
#define DKIM_SIGN_SIMPLE
Definition: dkim.h:43
#define DKIM_HASH_SHA1_AND_SHA256
Definition: dkim.h:34
#define DKIM_SIGN_RELAXED_SIMPLE
Definition: dkim.h:46
#define DKIM_SIGN_SIMPLE_RELAXED
Definition: dkim.h:44
#define DKIM_HASH_ED25519
Definition: dkim.h:35
#define DKIM_HASH_SHA1
Definition: dkim.h:32
#define DKIM_HASH_SHA256
Definition: dkim.h:33
#define DKIM_SUCCESS
Definition: dkim.h:49
#define LOWORD(l)
Definition: dkimsign.cpp:25
#define _stricmp
Definition: dkimsign.cpp:24
#define _strnicmp
Definition: dkimsign.cpp:23
#define HIWORD(l)
Definition: dkimsign.cpp:26
bool ConvertHeaderToQuotedPrintable(const char *source, char *dest)
Definition: dkimsign.cpp:277
char buf[100+FMT_ULONG]
Definition: hier.c:11
int
Definition: qmail-mrtg.c:27
unsigned long size
Definition: qmail-qread.c:56
struct del * d[CHANNELS]
Definition: qmail-send.c:726
int nIncludeBodyLengthTag
Definition: dkim.h:101
DKIMHEADERCALLBACK pfnHeaderCallback
Definition: dkim.h:109
int nIncludeCopiedHeaders
Definition: dkim.h:113
unsigned long expireTime
Definition: dkim.h:108
char szDomain[256]
Definition: dkim.h:106
char szIdentity[256]
Definition: dkim.h:107
int nIncludeTimeStamp
Definition: dkim.h:102
char szSelector[64]
Definition: dkim.h:104
int nIncludeQueryMethod
Definition: dkim.h:103
char szSelectorE[64]
Definition: dkim.h:105