/* * CheckBodyHash() * * Validate the message according to rfc6376 */ void Validatory::CheckBodyHash(const DKIM::Signature& sig) throw (DKIM::PermanentError) { std::unique_ptr<EVP_MD_CTX, std::function<void(EVP_MD_CTX*)>> evpmdbody(EVP_MD_CTX_create(), [] (EVP_MD_CTX* p) { EVP_MD_CTX_destroy(p); }); // create signature for our body (message data) switch (sig.GetDigestAlgorithm()) { case DKIM::DKIM_A_SHA1: EVP_DigestInit_ex(evpmdbody.get(), EVP_sha1(), nullptr); break; case DKIM::DKIM_A_SHA256: EVP_DigestInit_ex(evpmdbody.get(), EVP_sha256(), nullptr); break; } DKIM::Conversion::EVPDigest evpupd; evpupd.ctx = evpmdbody.get(); CanonicalizationBody(m_file, sig.GetCanonModeBody(), m_msg.GetBodyOffset(), sig.GetBodySizeLimit(), sig.GetBodySize(), std::bind(&DKIM::Conversion::EVPDigest::update, &evpupd, std::placeholders::_1, std::placeholders::_2)); unsigned char md_value[EVP_MAX_MD_SIZE]; unsigned int md_len; EVP_DigestFinal_ex(evpmdbody.get(), md_value, &md_len); if (sig.GetBodyHash().size() != md_len || memcmp(sig.GetBodyHash().c_str(), md_value, md_len) != 0) { throw DKIM::PermanentError("Body hash did not verify"); } }
/* * CheckSignature() * * Validate the message according to rfc6376 */ void Validatory::CheckSignature(const std::shared_ptr<DKIM::Header> header, const DKIM::Signature& sig, const DKIM::PublicKey& pub) throw (DKIM::PermanentError) { // sanity checking (between sig and pub) if (pub.GetDigestAlgorithms().size() > 0) if (find(pub.GetDigestAlgorithms().begin(), pub.GetDigestAlgorithms().end(), sig.GetDigestAlgorithm()) == pub.GetDigestAlgorithms().end()) throw DKIM::PermanentError("Algorithm is not allowed"); if (sig.GetSignatureAlgorithm() != pub.GetSignatureAlgorithm()) throw DKIM::PermanentError("Signature algorithm type mismatch"); if (!sig.IsARC()) { if (find(pub.GetFlags().begin(), pub.GetFlags().end(), "s") != pub.GetFlags().end()) if (sig.GetDomain() != sig.GetMailDomain()) throw DKIM::PermanentError("Domain must match sub-domain (flag s)"); } // create signature for our header std::unique_ptr<EVP_MD_CTX, std::function<void(EVP_MD_CTX*)>> evpmdhead(EVP_MD_CTX_create(), [] (EVP_MD_CTX* p) { EVP_MD_CTX_destroy(p); }); int md_nid; switch (sig.GetDigestAlgorithm()) { case DKIM::DKIM_A_SHA1: EVP_DigestInit_ex(evpmdhead.get(), EVP_sha1(), nullptr); md_nid = NID_sha1; break; case DKIM::DKIM_A_SHA256: EVP_DigestInit_ex(evpmdhead.get(), EVP_sha256(), nullptr); md_nid = NID_sha256; break; } CanonicalizationHeader canonicalhead(sig.GetCanonModeHeader()); // add all headers to our cache (they will be pop of the end) std::map<std::string, Message::HeaderList> headerCache; for (const auto & hIter : m_msg.GetHeaders()) { std::string headerName = hIter->GetName(); transform(headerName.begin(), headerName.end(), headerName.begin(), tolower); headerCache[headerName].push_back(hIter); } // add all signed headers to our hash for (auto name : sig.GetSignedHeaders()) { std::string tmp; transform(name.begin(), name.end(), name.begin(), tolower); std::map<std::string, Message::HeaderList>::iterator head = headerCache.find(name); // if this occurred // 1. we do not have a header of that name at all // 2. all headers with that name has been included... if (head == headerCache.end() || head->second.size() == 0) continue; #ifdef DEBUG printf("[%s]\n", canonicalhead.FilterHeader(head->second.back()->GetHeader()).c_str()); printf("[CRLF]\n"); #endif tmp = canonicalhead.FilterHeader(head->second.back()->GetHeader()) + "\r\n"; head->second.pop_back(); EVP_DigestUpdate(evpmdhead.get(), tmp.c_str(), tmp.size()); } // add our dkim-signature to the calculation (remove the "b"-tag) std::string h = header->GetHeader().substr(0, header->GetValueOffset()); std::string v = header->GetHeader().substr(header->GetValueOffset()); DKIM::TagListEntry bTag; sig.GetTag("b", bTag); v.erase((int)bTag.GetValueOffset(), bTag.GetValue().size()); std::string tmp = canonicalhead.FilterHeader(h + v); #ifdef DEBUG printf("[%s]\n", tmp.c_str()); #endif EVP_DigestUpdate(evpmdhead.get(), tmp.c_str(), tmp.size()); unsigned char md[EVP_MAX_MD_SIZE]; unsigned int md_len; EVP_DigestFinal_ex(evpmdhead.get(), md, &md_len); // verify the header signature switch (sig.GetSignatureAlgorithm()) { case DKIM::DKIM_SA_RSA: { int r = RSA_verify(md_nid, md, md_len, (const unsigned char *)sig.GetSignatureData().c_str(), (unsigned int)sig.GetSignatureData().size(), pub.GetRSAPublicKey()); if (r != 1) throw DKIM::PermanentError("Signature did not verify"); } break; case DKIM::DKIM_SA_ED25519: if (crypto_sign_verify_detached((const unsigned char*)sig.GetSignatureData().c_str(), md, md_len, (const unsigned char *)pub.GetED25519PublicKey().c_str()) != 0) throw DKIM::PermanentError("Signature did not verify"); break; } // success! }