Beispiel #1
0
/*
 * 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!
}
Beispiel #2
0
/*
 * CheckSignature()
 *
 * Validate the message according to rfc4871
 */
void Validatory::CheckSignature(const Message::HeaderList::const_iterator& headerIter,
		const DKIM::Signature& sig,
		const DKIM::PublicKey& pub)
	throw (DKIM::PermanentError)
{
	// multiple signatures (must cleanup)
	EVP_MD_CTX_cleanup(&m_ctx_body);
	EVP_MD_CTX_cleanup(&m_ctx_head);

	// sanity checking (between sig and pub)
	if (pub.GetAlgorithms().size() > 0)
		if (find(pub.GetAlgorithms().begin(), pub.GetAlgorithms().end(), sig.GetAlgorithm()) == pub.GetAlgorithms().end())
			throw DKIM::PermanentError("Algorithm is not allowed");

	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)");

	/*
	 If a DKIM verifier finds a selector record that has an empty "g" field ("g=;")
	 and it does not have a "v" field ("v=DKIM1;") at its beginning, it is faced with deciding if this record was

	 1. from a DK signer that transitioned to supporting DKIM but forgot to remove the "g"
	 	field (so that it could be used by both DK and DKIM verifiers), or
	 2. from a DKIM signer that truly meant to use the empty "g" field but forgot to put in
	 	the "v" field. It is RECOMMENDED that you treat such records using the first
		interpretation, and treat such records as if the signer did not have a "g" field in the record.
	*/

	// if we have a "g"-tag
	if (find(pub.GetFlags().begin(), pub.GetFlags().end(), "g") != pub.GetFlags().end())
	{
		// if it's empty... and we don't have a version...
		if (pub.GetMailLocalPart().empty() && find(pub.GetFlags().begin(), pub.GetFlags().end(), "v") == pub.GetFlags().end())
		{
			// do the RECOMMENDED interpretation and treat such records as if the signer did not have a "g" field in the record.
		} else
		{
			if (DKIM::Util::MatchWithWildCard(pub.GetMailLocalPart(), sig.GetMailLocalPart()) != true)
				throw DKIM::PermanentError("Unmatched local-part");
		}
	}

	// create signature for our header
	switch (sig.GetAlgorithm())
	{
		case DKIM::DKIM_A_SHA1:
			EVP_VerifyInit(&m_ctx_head, EVP_sha1());
			break;
		case DKIM::DKIM_A_SHA256:
			EVP_VerifyInit(&m_ctx_head, EVP_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 (Message::HeaderList::const_iterator hIter = m_msg.GetHeaders().begin();
			hIter != m_msg.GetHeaders().end();
			++hIter)
	{
		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 (std::list<std::string>::const_iterator hIter = sig.GetSignedHeaders().begin();
			hIter != sig.GetSignedHeaders().end(); ++hIter)
	{
		std::string tmp;
		std::string name = *hIter;
		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_VerifyUpdate(&m_ctx_head, tmp.c_str(), tmp.size());
	}

	// add our dkim-signature to the calculation (remove the "b"-tag)
	std::string h = (*headerIter)->GetHeader().substr(0, (*headerIter)->GetValueOffset());
	std::string v = (*headerIter)->GetHeader().substr((*headerIter)->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_VerifyUpdate(&m_ctx_head, tmp.c_str(), tmp.size());

	// verify the header signature
	if (EVP_VerifyFinal(&m_ctx_head,
				(const unsigned char*)sig.GetSignatureData().c_str(),
				sig.GetSignatureData().size(),
				pub.GetPublicKey()
				) != 1)
		throw DKIM::PermanentError("Signature did not verify");
	EVP_MD_CTX_cleanup(&m_ctx_head);

	// success!
}