blob: eda700548999643501953d77d91e86ef0438bf58 [file]
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "crypto/default/cms/cms_parser.h"
#include <string>
#include <vector>
#include "absl/log/check.h"
#include "absl/strings/escaping.h"
#include "crypto/default/cms/cms_error_code.h"
#include "crypto/default/cms/cms_test_helper.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "openssl/bytestring.h"
#include "openssl/obj_mac.h"
namespace credentio_cms {
namespace {
using testing::HasSubstr;
// DER encoded Oids.
const char kSha1Oid[] = "2b0e03021a";
const char kSha256Oid[] = "608648016503040201";
const char kAes256CbcOid[] = "60864801650304012a";
const char kRsaEncryptionOid[] = "2a864886f70d010101";
const char kCmsDataOid[] = "2a864886f70d010701";
const char kDesEde3Cbc[] = "2a864886f70d0307";
const char kEcdhStdSha1KdfOid[] = "2b81051086483f0002";
// The one line issuer name for User1 and User2 certificates.
const char kTestIssuer[] =
"C=US, ST=CA, L=Mountain View, O=Smime Test CA, CN=Smime Test CA";
const char kUser1NoCnSerialNumber[] = "1235";
const char kUser2NoCnSerialNumber[] = "1236";
const char kUser2BothSerialNumber[] = "1237";
const char kUser1EccSerialNumber[] = "123b";
const char kUser1EcdsaSerialNumber[] =
"6bef0b67723662c54e991de365cddf84e2a402cd";
std::string PemToDer(const std::string& cert) {
const char kStart[] = "-----BEGIN CERTIFICATE-----";
int start = cert.find(kStart) + sizeof(kStart);
CHECK_GE(start, 0);
int end = cert.find("-----END CERTIFICATE-----", start);
CHECK_GT(end, 0);
std::string decoded;
CHECK(absl::Base64Unescape(cert.substr(start, end - start), &decoded));
return decoded;
}
std::string CBSToString(const ByteString& b) {
std::string s;
s.assign(reinterpret_cast<const char*>(CBS_data(b.cbs_ptr())),
CBS_len(b.cbs_ptr()));
return s;
}
std::string CBSChunksToString(const std::vector<ByteString>& data) {
std::string s;
for (const auto& chunk : data) {
s.append(reinterpret_cast<const char*>(CBS_data(chunk.cbs_ptr())),
CBS_len(chunk.cbs_ptr()));
}
return s;
}
std::string CBSToHex(const ByteString& b) {
return absl::BytesToHexString(CBSToString(b));
}
TEST(ParseCms, OpenSSLDetached) {
std::string cms = GetCms(GetFileContents("signed-openssl-detached.msg"));
std::string ca_cert = PemToDer(GetFileContents("ca.pem"));
std::string user_cert = PemToDer(GetFileContents("user1_no_cn.pem"));
Content signed_data;
std::string error_message;
EXPECT_EQ(ErrorCode::OK,
ParseCms(cms.data(), cms.length(), &signed_data, &error_message))
<< error_message;
EXPECT_EQ(ContentType::SIGNED_DATA, signed_data.type);
EXPECT_EQ(1, signed_data.version);
ASSERT_EQ(2, signed_data.certificates.size());
EXPECT_EQ(ca_cert, CBSToString(signed_data.certificates[0]));
EXPECT_EQ(user_cert, CBSToString(signed_data.certificates[1]));
EXPECT_EQ(0, signed_data.content.size());
// The message is not encrypted.
EXPECT_EQ("", CBSToString(signed_data.encryption_algorithm.algorithm_oid));
EXPECT_EQ("", CBSToString(signed_data.encryption_algorithm.parameter));
// No recipient info in signed data.
EXPECT_EQ(0, signed_data.recipients.size());
// One signer.
ASSERT_EQ(1, signed_data.signers.size());
const auto& signer = signed_data.signers[0];
EXPECT_EQ(1, signer.version);
EXPECT_EQ(kUser1NoCnSerialNumber, CBSToHex(signer.serial_number));
EXPECT_EQ(kTestIssuer, CBSToName(signer.issuer_name));
EXPECT_EQ("348910ba3e8dc1d51162ee9dc634c8a0b557479ae95c592c044dc85013d6ced1",
CBSToHex(signer.message_digest));
EXPECT_EQ(
"6d6a93c0e9faeeb01ad176087975f8ac27218074afe7def711520b3ac99159aa627e00ad"
"07cfd0c5eca749950c896b51dea87b706f5ca85b1f8f5da8e2144befe9d396d5fbf9dc2b"
"fa7acd2f0db6f57ac8dc3daca2aea85263fe7d56c544bce58b3c49b1f78150ac6ba06d62"
"075be7c9165a41d5ab525b7cfc2631c0b70339bc84c2de9aa54c08393f362e2ea16f2e1a"
"e6a585da4daeb269495fb676b451bd9ed10669be940b2b40a952a4cabc775dc08182d304"
"a5b718d5562133a469302c50d09f6176d539bf300dee6c3014b5fe3dad1c2ea39149d9db"
"77bc5c64fdeb611a5ae4d7ab073532ffa4a439736fbd0525c742ebd93a2d234c8acac942"
"f80dd2ea",
CBSToHex(signer.signature_value));
EXPECT_EQ(kCmsDataOid, CBSToHex(signer.content_type_signed));
EXPECT_EQ(kSha256Oid, CBSToHex(signer.digest_algorithm.algorithm_oid));
EXPECT_EQ(kRsaEncryptionOid,
CBSToHex(signer.signature_algorithm.algorithm_oid));
EXPECT_EQ("300d06092a864886f70d0101010500",
CBSToHex(signer.signature_algorithm.raw_value));
}
TEST(ParseCms, OpenSSLOpaque) {
std::string cms = GetCms(GetFileContents("signed-openssl-opaque.msg"));
std::string ca_cert = PemToDer(GetFileContents("ca.pem"));
std::string user_cert = PemToDer(GetFileContents("user1_no_cn.pem"));
Content signed_data;
std::string error_message;
EXPECT_EQ(ErrorCode::OK,
ParseCms(cms.data(), cms.length(), &signed_data, &error_message));
EXPECT_EQ(ContentType::SIGNED_DATA, signed_data.type);
EXPECT_EQ(1, signed_data.version);
ASSERT_EQ(2, signed_data.certificates.size());
EXPECT_EQ(ca_cert, CBSToString(signed_data.certificates[0]));
EXPECT_EQ(user_cert, CBSToString(signed_data.certificates[1]));
EXPECT_EQ(
"Content-Type: text/plain\r\n\r\nThis is a message.\r\n"
"There are many like it, but this one is mine.\r\n",
CBSChunksToString(signed_data.content));
// The message is not encrypted.
EXPECT_EQ("", CBSToString(signed_data.encryption_algorithm.algorithm_oid));
EXPECT_EQ("", CBSToString(signed_data.encryption_algorithm.parameter));
// No recipient info in signed data.
EXPECT_EQ(0, signed_data.recipients.size());
// One signer.
ASSERT_EQ(1, signed_data.signers.size());
const auto& signer = signed_data.signers[0];
EXPECT_EQ(1, signer.version);
EXPECT_EQ(kUser1NoCnSerialNumber, CBSToHex(signer.serial_number));
EXPECT_EQ(kTestIssuer, CBSToName(signer.issuer_name));
EXPECT_EQ("348910ba3e8dc1d51162ee9dc634c8a0b557479ae95c592c044dc85013d6ced1",
CBSToHex(signer.message_digest));
EXPECT_EQ(
"323ef3ee26aa0bf4ccfc147a73536dabfeda7ee84f6c32ff96b4669a790e69f4f07e80cd"
"ff7357dd5ede286a33dd0985bd8a55aa531f8f93fb33ac336a0fddbcb5a14ad4337fd15c"
"fb63a16e72a3f2b7ef34a8d868767fd0b5d6316c44402ff2993a969c08c41496048e08be"
"18eedf1289fd9242a6b7b9a1f242cea7d2c86f097ddf8b241797856a82c3a8ccb0376a3c"
"82b79feba37886c96a6f00192ac243b310a99665d6653dcde777c22861828a6aa39867b5"
"a600ac6e5ab4eb1460dd0c2355c402ef8db6631e818d09dd1c66e81d87efd02fadf1b063"
"7fadcf930d531c8519518e33890b274bdf0a51fe51c68a4b14fc30fc01ec9a8d83b0c637"
"d3434565",
CBSToHex(signer.signature_value));
EXPECT_EQ(kCmsDataOid, CBSToHex(signer.content_type_signed));
EXPECT_EQ(kSha256Oid, CBSToHex(signer.digest_algorithm.algorithm_oid));
EXPECT_EQ(kRsaEncryptionOid,
CBSToHex(signer.signature_algorithm.algorithm_oid));
}
TEST(ParseCms, OpenSSLEcdsaOpaque) {
std::string cms = GetCms(GetFileContents("signed-openssl-ecdsa.msg"));
std::string user_cert = PemToDer(GetFileContents("user1_ecdsa.pem"));
Content signed_data;
std::string error_message;
EXPECT_EQ(ErrorCode::OK,
ParseCms(cms.data(), cms.length(), &signed_data, &error_message));
EXPECT_EQ(ContentType::SIGNED_DATA, signed_data.type);
EXPECT_EQ(1, signed_data.version);
ASSERT_EQ(1, signed_data.certificates.size());
EXPECT_EQ(user_cert, CBSToString(signed_data.certificates[0]));
EXPECT_EQ(
"Content-Type: text/plain\r\n\r\nThis is a message.\r\n"
"There are many like it, but this one is mine.\r\n",
CBSChunksToString(signed_data.content));
// The message is not encrypted.
EXPECT_EQ("", CBSToString(signed_data.encryption_algorithm.algorithm_oid));
EXPECT_EQ("", CBSToString(signed_data.encryption_algorithm.parameter));
// No recipient info in signed data.
EXPECT_EQ(0, signed_data.recipients.size());
// One signer.
ASSERT_EQ(1, signed_data.signers.size());
const auto& signer = signed_data.signers[0];
EXPECT_EQ(1, signer.version);
EXPECT_EQ(kUser1EcdsaSerialNumber, CBSToHex(signer.serial_number));
// This particular certificate is self-signed.
EXPECT_EQ("emailAddress=user1@foo.bar", CBSToName(signer.issuer_name));
EXPECT_EQ("348910ba3e8dc1d51162ee9dc634c8a0b557479ae95c592c044dc85013d6ced1",
CBSToHex(signer.message_digest));
EXPECT_EQ(
"304402200c42ce53ff672e2adf9d87da9c3c2d6346894d68dff9cc23ddaf1bd96cf42cd1"
"02206d6b318cbea8a477aa540c4ec9eebdc63281e3a16258540eb8cd5f010c91dd59",
CBSToHex(signer.signature_value));
EXPECT_EQ(kCmsDataOid, CBSToHex(signer.content_type_signed));
EXPECT_EQ(kSha256Oid, CBSToHex(signer.digest_algorithm.algorithm_oid));
EXPECT_EQ(absl::BytesToHexString(NidToDerString(NID_ecdsa_with_SHA256)),
CBSToHex(signer.signature_algorithm.algorithm_oid));
}
TEST(ParseCms, OpenSSLTwoSignaturesDetached) {
std::string cms =
GetCms(GetFileContents("two-signatures-openssl-detached.msg"));
std::string ca_cert = PemToDer(GetFileContents("ca.pem"));
std::string user1_cert = PemToDer(GetFileContents("user1_no_cn.pem"));
std::string user2_cert = PemToDer(GetFileContents("user2_no_cn.pem"));
Content signed_data;
std::string error_message;
EXPECT_EQ(ErrorCode::OK,
ParseCms(cms.data(), cms.length(), &signed_data, &error_message));
EXPECT_EQ(ContentType::SIGNED_DATA, signed_data.type);
EXPECT_EQ(1, signed_data.version);
ASSERT_EQ(3, signed_data.certificates.size());
EXPECT_EQ(ca_cert, CBSToString(signed_data.certificates[0]));
EXPECT_EQ(user1_cert, CBSToString(signed_data.certificates[1]));
EXPECT_EQ(user2_cert, CBSToString(signed_data.certificates[2]));
EXPECT_EQ(0, signed_data.content.size());
// The message is not encrypted.
EXPECT_EQ("", CBSToString(signed_data.encryption_algorithm.algorithm_oid));
EXPECT_EQ("", CBSToString(signed_data.encryption_algorithm.parameter));
// No recipient info in signed data.
EXPECT_EQ(0, signed_data.recipients.size());
// Two signers.
ASSERT_EQ(2, signed_data.signers.size());
// First signer.
const auto& signer1 = signed_data.signers[0];
EXPECT_EQ(1, signer1.version);
EXPECT_EQ("1236", CBSToHex(signer1.serial_number));
EXPECT_EQ("348910ba3e8dc1d51162ee9dc634c8a0b557479ae95c592c044dc85013d6ced1",
CBSToHex(signer1.message_digest));
EXPECT_EQ(
"b3ae16888b424fa19484cd823ada53856c4d79065d47a33393b23372da06afb9c38d610e"
"851ef25b0fee4fd17df179c8476fb02dc1cc4ed347a9ac4b6b1ec1508d31980264ea1b03"
"7d87d9cb3d03c462871f96f5feb3be843f356a8d167feddebbe3891e9d45a1161b44b116"
"cc39cf1454988e027b309f2f29a4c0efb89b41f9e2ab63b6e446c69d6fc0e0b67729921b"
"7c1da6aa026f3e6245d8de327f7e55a247b65f800fb0907f2c48cc6d2ef640895a01816a"
"370d5447ef5d13d360386b342ebac7350f046e2d609b3c76188645e694b681589445a79c"
"f142b2ec85cbd1ce7e35bb89040809998540dd1a6fa0eff9f0688589f7e886b1c4608a1f"
"8095a2eb",
CBSToHex(signer1.signature_value));
// PKCS-7.1 Data
EXPECT_EQ("2a864886f70d010701", CBSToHex(signer1.content_type_signed));
EXPECT_EQ(kSha256Oid, CBSToHex(signer1.digest_algorithm.algorithm_oid));
EXPECT_EQ(kRsaEncryptionOid,
CBSToHex(signer1.signature_algorithm.algorithm_oid));
EXPECT_EQ(kTestIssuer, CBSToName(signer1.issuer_name));
// Second signer.
const auto& signer2 = signed_data.signers[1];
EXPECT_EQ(1, signer2.version);
EXPECT_EQ(kUser1NoCnSerialNumber, CBSToHex(signer2.serial_number));
EXPECT_EQ("348910ba3e8dc1d51162ee9dc634c8a0b557479ae95c592c044dc85013d6ced1",
CBSToHex(signer2.message_digest));
EXPECT_EQ(kTestIssuer, CBSToName(signer2.issuer_name));
EXPECT_EQ(
"6d6a93c0e9faeeb01ad176087975f8ac27218074afe7def711520b3ac99159aa627e00ad"
"07cfd0c5eca749950c896b51dea87b706f5ca85b1f8f5da8e2144befe9d396d5fbf9dc2b"
"fa7acd2f0db6f57ac8dc3daca2aea85263fe7d56c544bce58b3c49b1f78150ac6ba06d62"
"075be7c9165a41d5ab525b7cfc2631c0b70339bc84c2de9aa54c08393f362e2ea16f2e1a"
"e6a585da4daeb269495fb676b451bd9ed10669be940b2b40a952a4cabc775dc08182d304"
"a5b718d5562133a469302c50d09f6176d539bf300dee6c3014b5fe3dad1c2ea39149d9db"
"77bc5c64fdeb611a5ae4d7ab073532ffa4a439736fbd0525c742ebd93a2d234c8acac942"
"f80dd2ea",
CBSToHex(signer2.signature_value));
// PKCS-7.1 Data
EXPECT_EQ(kCmsDataOid, CBSToHex(signer2.content_type_signed));
EXPECT_EQ(kSha256Oid, CBSToHex(signer2.digest_algorithm.algorithm_oid));
EXPECT_EQ(kRsaEncryptionOid,
CBSToHex(signer2.signature_algorithm.algorithm_oid));
}
TEST(ParseCms, ThunderbirdSigned) {
std::string cms = GetCms(GetFileContents("signed-thunderbird.msg"));
std::string ca_cert = PemToDer(GetFileContents("ca.pem"));
std::string user_cert = PemToDer(GetFileContents("user1_no_cn.pem"));
Content signed_data;
std::string error_message;
EXPECT_EQ(ErrorCode::OK,
ParseCms(cms.data(), cms.length(), &signed_data, &error_message));
EXPECT_EQ(ContentType::SIGNED_DATA, signed_data.type);
EXPECT_EQ(1, signed_data.version);
ASSERT_EQ(1, signed_data.certificates.size());
EXPECT_EQ(user_cert, CBSToString(signed_data.certificates[0]));
EXPECT_EQ(0, signed_data.content.size());
// The message is not encrypted.
EXPECT_EQ("", CBSToString(signed_data.encryption_algorithm.algorithm_oid));
EXPECT_EQ("", CBSToString(signed_data.encryption_algorithm.parameter));
// No recipient info in signed data.
EXPECT_EQ(0, signed_data.recipients.size());
// One signer.
ASSERT_EQ(1, signed_data.signers.size());
const auto& signer = signed_data.signers[0];
EXPECT_EQ(1, signer.version);
EXPECT_EQ(kUser1NoCnSerialNumber, CBSToHex(signer.serial_number));
EXPECT_EQ(kTestIssuer, CBSToName(signer.issuer_name));
EXPECT_EQ("fb749fdbc8f3e5d6d11f02e2272048f90ad0035e",
CBSToHex(signer.message_digest));
EXPECT_EQ(
"0333cefa73f44054bd78f55f6a6af9aeac546e09d52bf069a471afb0a7facc9e048e8bec"
"29a32a88eecc6bda7bc3abeeb9d2e8632971dde6f24387ae59dd9aeeca3f99dbb955fbf8"
"24ebba7a7d160af8ca650d53373af61a12da042adbd3b0ec432aa6578e49ece7ce1fe5af"
"8897b26a30e23f3f09cfb66997f07ec5b4b2a7b762f8bae38cf80ec056f788c4ae3c28b8"
"71e8fa5e7109807552ed36bb00fd65abb71e48e5af9aea9c7d888004a413e8212bb9f1b2"
"f3162a9f37e3664ffa6c4e500cbac0f09e362023f54cd1c02cfe05ef3bed677d9cfae8c8"
"9f682a531491fa8b9cb6980c4755b5c4de7b1b46c31ba5336db260538a1b109d0bf48540"
"709f795e",
CBSToHex(signer.signature_value));
EXPECT_EQ(kCmsDataOid, CBSToHex(signer.content_type_signed));
EXPECT_EQ(kSha1Oid, CBSToHex(signer.digest_algorithm.algorithm_oid));
EXPECT_EQ(kRsaEncryptionOid,
CBSToHex(signer.signature_algorithm.algorithm_oid));
EXPECT_EQ(CBSToHex(signer.encryption_key_preference.serial_number), "1235");
EXPECT_EQ(CBSToName(signer.encryption_key_preference.issuer_name),
kTestIssuer);
}
TEST(ParseCms, OutlookMac2011Signed) {
std::string cms = GetCms(GetFileContents("signed-outlook-mac-2011.msg"));
std::string ca_cert = PemToDer(GetFileContents("ca.pem"));
std::string user_cert = PemToDer(GetFileContents("user1_no_cn.pem"));
Content signed_data;
std::string error_message;
EXPECT_EQ(ErrorCode::OK,
ParseCms(cms.data(), cms.length(), &signed_data, &error_message));
EXPECT_EQ(ContentType::SIGNED_DATA, signed_data.type);
EXPECT_EQ(1, signed_data.version);
ASSERT_EQ(2, signed_data.certificates.size());
EXPECT_EQ(user_cert, CBSToString(signed_data.certificates[0]));
EXPECT_EQ(ca_cert, CBSToString(signed_data.certificates[1]));
EXPECT_EQ(0, signed_data.content.size());
// The message is not encrypted.
EXPECT_EQ("", CBSToString(signed_data.encryption_algorithm.algorithm_oid));
EXPECT_EQ("", CBSToString(signed_data.encryption_algorithm.parameter));
// No recipient info in signed data.
EXPECT_EQ(0, signed_data.recipients.size());
// One signer.
ASSERT_EQ(1, signed_data.signers.size());
const auto& signer = signed_data.signers[0];
EXPECT_EQ(1, signer.version);
EXPECT_EQ(kUser1NoCnSerialNumber, CBSToHex(signer.serial_number));
EXPECT_EQ("33fafcff3de4de6ca57218fc6c6befd038956ab4dd7346c1deebecc86a22fb86",
CBSToHex(signer.message_digest));
EXPECT_EQ(
"b50d1583b44d9f96efaced02496976cec63df0b40026d19198d513e5d6ed4010b7a8fe26"
"95df5ab31bbb69faf59a91fe081a59676f0dffee406fcfa1dafb6191dec6b76a089df449"
"e41f4005e56433581b5630034edded3cefb4deb151d85a6a2e2a60dce8a24432a8838116"
"b69aff8126ae7005329727fd5764830f5bbab6e6f372e60bbf14ee772d6f4a46761bbe84"
"05958eb594515bc285954270c8e16e575b42a7b1dfdd3d26a65362bccda7130c36064893"
"9664713d88b943bf379fe94b180f6bf0fd3d3b3cae96ef43787722430542ada19162a85f"
"2f167695ff77f646d7c5d42202656245ff24d2ed1b6c95ad2c3e530c3733195c5ba7d831"
"431ec31d",
CBSToHex(signer.signature_value));
EXPECT_EQ(kCmsDataOid, CBSToHex(signer.content_type_signed));
EXPECT_EQ(kSha256Oid, CBSToHex(signer.digest_algorithm.algorithm_oid));
EXPECT_EQ(kRsaEncryptionOid,
CBSToHex(signer.signature_algorithm.algorithm_oid));
}
// Testing signed message produced by DoD that previously triggers a parsing
// error due to the BER encoding of a element length <127 in the 2-byte long
// form.
TEST(ParseCms, DoDCustomSingerSigned) {
// a signed message produced by DoD
std::string cms = GetCms(GetFileContents("dod_signed.msg"));
Content signed_data;
std::string error_message;
EXPECT_EQ(ErrorCode::OK,
ParseCms(cms.data(), cms.length(), &signed_data, &error_message))
<< error_message;
EXPECT_EQ(ContentType::SIGNED_DATA, signed_data.type);
EXPECT_EQ(1, signed_data.version);
ASSERT_EQ(4, signed_data.certificates.size());
ASSERT_EQ(1, signed_data.signers.size());
ASSERT_EQ(6, signed_data.signers[0].signed_attributes.size());
}
TEST(ParseCms, DoDCustomSingerSignedLong) {
// a signed message produced by DoD
std::string cms = GetCms(GetFileContents("dod_signed_long.msg"));
Content signed_data;
std::string error_message;
EXPECT_EQ(ErrorCode::OK,
ParseCms(cms.data(), cms.length(), &signed_data, &error_message))
<< error_message;
EXPECT_EQ(ContentType::SIGNED_DATA, signed_data.type);
EXPECT_EQ(1, signed_data.version);
ASSERT_EQ(5, signed_data.certificates.size());
ASSERT_EQ(1, signed_data.signers.size());
ASSERT_EQ(6, signed_data.signers[0].signed_attributes.size());
}
TEST(ParseCms, OutlookMac2011Encrypted) {
std::string cms = GetCms(GetFileContents("encrypted-outlook-mac-2011.msg"));
std::string ca_cert = PemToDer(GetFileContents("ca.pem"));
std::string user_cert = PemToDer(GetFileContents("user1_no_cn.pem"));
Content enveloped_data;
std::string error_message;
EXPECT_EQ(ErrorCode::OK,
ParseCms(cms.data(), cms.length(), &enveloped_data, &error_message))
<< error_message;
EXPECT_EQ(ContentType::ENVELOPED_DATA, enveloped_data.type);
EXPECT_EQ(0, enveloped_data.version);
ASSERT_EQ(0, enveloped_data.certificates.size());
// Two recipients: the actual recipient and the sender so they can read
// what was sent.
ASSERT_EQ(2, enveloped_data.recipients.size());
const auto& signer = enveloped_data.recipients[0];
ASSERT_EQ(0, signer.version);
EXPECT_EQ(kTestIssuer, CBSToName(signer.encrypted_keys[0].issuer_name));
EXPECT_EQ(kUser2BothSerialNumber,
CBSToHex(signer.encrypted_keys[0].serial_number));
ASSERT_EQ(kRsaEncryptionOid,
CBSToHex(signer.key_encryption_algorithm.algorithm_oid));
ASSERT_EQ(
"928a924ca3f4777ac22271e6ec89a773d909ba6d79ac4961ee3692c87c2654fdcbc1b3dd"
"355a7ac0e2d231b8b3fc6de5a89a9d1a9408339bf711e2e8a91054e6681d8668c28f1e0f"
"73ec68db6a63beab6873058fa7d180510fa109be99a21b33ea014185df3b13a15adfa19d"
"7872805037d171cc04f663233b0d350fecd890347d59f7ba445d13ecf51154dd2252f0e7"
"b1ed0ae663cabc3c4d77bc6f081302adfceafc29da768d9e1e29bb8e81e909ec93e65f99"
"c8a1224b977693c0e62dc92155abdbd38bd198b1a99167e39534817cb5dac41b4f893bf9"
"52c0be07f122210399b6e3ebdd3fe56d0d4dbb27d048ec11fef3580da91470e5bd026d55"
"6aeac0ae",
CBSToHex(signer.encrypted_keys[0].encrypted_key));
const auto& recipient2 = enveloped_data.recipients[1];
ASSERT_EQ(0, recipient2.version);
EXPECT_EQ(kTestIssuer, CBSToName(recipient2.encrypted_keys[0].issuer_name));
EXPECT_EQ(kUser1NoCnSerialNumber,
CBSToHex(recipient2.encrypted_keys[0].serial_number));
ASSERT_EQ(kRsaEncryptionOid,
CBSToHex(recipient2.key_encryption_algorithm.algorithm_oid));
ASSERT_EQ(
"8deec1a3c6ce33deaff7560b6f4a1844a34aada30459af5357ffb85cce07c284a189af18"
"ffa693553c8629b39b019e27fee0573c73f6cf2f100c7af28402d8d81d83028d45141d40"
"4be04b62e83d2fc4f1893f7f51068d330e1755a66618d604df63e58069c5eaf11de7b5c2"
"590481973cecbb2725707f5c03da360832573ebbf6d180f20d3ac475648d7b67456eae6b"
"264b72e75c390116bf8ad09335529b9f11cb61bb52a32c0d6b65fffe218dc429e038b5ab"
"0f1d102b6277da27b1bc2e708707299605d7ce3e1941e364c35568b6c1eff7c7d488b496"
"5b3d53fa038d82904327c4669dfccdded733ac887e00857600be875302bbce72b67020f1"
"9276f06e",
CBSToHex(recipient2.encrypted_keys[0].encrypted_key));
std::string content = CBSChunksToString(enveloped_data.content);
EXPECT_EQ(4576, content.size());
// Check the first 16 and last 16 bytes for the encrypted payload.
std::string hex_content = absl::BytesToHexString(content);
EXPECT_EQ(0, hex_content.find("75d46c500858ac2476c0b36916d49f3b"));
EXPECT_EQ(4560 * 2, hex_content.find("966d5b525e9a81f737e439537b74cd21"));
EXPECT_EQ(kAes256CbcOid,
CBSToHex(enveloped_data.encryption_algorithm.algorithm_oid));
EXPECT_EQ("0410d86dc5375a13ba12c48d3a0da6100316",
CBSToHex(enveloped_data.encryption_algorithm.parameter));
// No signer on the envelope.
ASSERT_EQ(0, enveloped_data.signers.size());
}
TEST(ParseCms, ThunderbirdEncrypted) {
std::string cms = GetCms(GetFileContents("encrypted-thunderbird.msg"));
Content enveloped_data;
std::string error_message;
EXPECT_EQ(ErrorCode::OK,
ParseCms(cms.data(), cms.length(), &enveloped_data, &error_message))
<< error_message;
EXPECT_EQ(ContentType::ENVELOPED_DATA, enveloped_data.type);
EXPECT_EQ(0, enveloped_data.version);
ASSERT_EQ(0, enveloped_data.certificates.size());
// Two recipients: the actual recipient and the sender so they can read
// what was sent.
ASSERT_EQ(2, enveloped_data.recipients.size());
const auto& recipient1 = enveloped_data.recipients[0];
ASSERT_EQ(0, recipient1.version);
EXPECT_EQ(kTestIssuer, CBSToName(recipient1.encrypted_keys[0].issuer_name));
EXPECT_EQ(kUser1NoCnSerialNumber,
CBSToHex(recipient1.encrypted_keys[0].serial_number));
ASSERT_EQ(kRsaEncryptionOid,
CBSToHex(recipient1.key_encryption_algorithm.algorithm_oid));
ASSERT_EQ(
"c7e9154370787a7980e60e7f7ea4152be93d2247518d62091df2206d0ab12125c35dc078"
"74e75b9b365fc50f71f3fce8e75caac42eda81517a9a3844fdc270d99ff77468bac370dc"
"bbccd4f3efa859a87064b79ecfd7ed857091e150e364853942ac5f9644df0278a5984f98"
"12948c4e22ab00f931473cc8f2a087d22d40f5c8c188171d6829113a76f0b98cf9f7d67a"
"96bd5f567c28ae3eb6e7b689ce4ce5e878d6a9808b3f23cb1929e2291d63c3ac0dfac69a"
"d85225930b694f4023a63adda4b5be3cc8554e506d973ccf945011052cfe04e21ed8a949"
"b9ad0810a6f3c394a9bf34a43923f08d077c7b8c4d1e726b90d0e673fbcca8e47f1fce68"
"2c1df1e5",
CBSToHex(recipient1.encrypted_keys[0].encrypted_key));
const auto& recipient2 = enveloped_data.recipients[1];
ASSERT_EQ(0, recipient2.version);
EXPECT_EQ(kTestIssuer, CBSToName(recipient2.encrypted_keys[0].issuer_name));
EXPECT_EQ(kUser2NoCnSerialNumber,
CBSToHex(recipient2.encrypted_keys[0].serial_number));
ASSERT_EQ(kRsaEncryptionOid,
CBSToHex(recipient2.key_encryption_algorithm.algorithm_oid));
ASSERT_EQ(
"835cdd7a9e5a92541d0ed18e6c66dc9528a3ad0b6d34193da464606798c8a875b24afda6"
"e5dfcf181a6f4b6160f983cbd6f2964bccd66bca7d1a345c37ef5beeef573888877e5a0c"
"09669666bec111da66e46f3c363d6cbcc69cf67ad6ce6e2f35f1e855d4a7eb692bbe3cfa"
"a4d2a50872d2ff5305cb8ad60bb8947761ba7255289acdc43fb688aabaaaad658f7cb5aa"
"58251217608450f547a94259f1ff154d57fdc53464189aba1691cc0d8772bf2fd0775abd"
"4160db52ade680cf1472f87140ab90efe6be48b0179acc2f475daad745afa9fb4ea09b80"
"8b2947624659bf6708852c55e5354c9e2ec162f2eeb8d20e7abe64c0496c095f02e3ec95"
"c60e1d79",
CBSToHex(recipient2.encrypted_keys[0].encrypted_key));
EXPECT_EQ(
"107a12c35ea7c2c7ffa427256869243f5e61693d6a4b0c3bc2168c97d2dfa64d78ae09a4"
"acb014b99c97f3a6169336420ff25e6c0ccec1dfc147b5ba7517c786e7106a910147334b"
"6b8e9873624c839051a27cc6242efb8a5c6773d74b7b8748189f98bf1e124aba7df35cb2"
"c1cf73bf8b1267b57c673e3119405e29530e44a76c5353540ed8829c5dc553cb6f17a97f"
"2f1d44b538ca4ddd0f7d34c8cdb3bc7b23fdd2b380dfaa134fa9432ef8e13cb6",
absl::BytesToHexString(CBSChunksToString(enveloped_data.content)));
EXPECT_EQ(kDesEde3Cbc,
CBSToHex(enveloped_data.encryption_algorithm.algorithm_oid));
EXPECT_EQ("04085a0d5623a1e7a4a0",
CBSToHex(enveloped_data.encryption_algorithm.parameter));
// No signer on the envelope.
ASSERT_EQ(0, enveloped_data.signers.size());
}
TEST(ParseCms, OpenSslEncrypted) {
std::string cms = GetCms(GetFileContents("encrypted-openssl.msg"));
std::string ca_cert = PemToDer(GetFileContents("ca.pem"));
std::string user_cert = PemToDer(GetFileContents("user1_no_cn.pem"));
Content enveloped_data;
std::string error_message;
EXPECT_EQ(ErrorCode::OK,
ParseCms(cms.data(), cms.length(), &enveloped_data, &error_message))
<< error_message;
EXPECT_EQ(ContentType::ENVELOPED_DATA, enveloped_data.type);
EXPECT_EQ(0, enveloped_data.version);
ASSERT_EQ(0, enveloped_data.certificates.size());
// Two recipients: the actual recipient and the sender so they can read
// what was sent.
ASSERT_EQ(1, enveloped_data.recipients.size());
const auto& recipient = enveloped_data.recipients[0];
ASSERT_EQ(0, recipient.version);
EXPECT_EQ(kTestIssuer, CBSToName(recipient.encrypted_keys[0].issuer_name));
EXPECT_EQ(kUser2NoCnSerialNumber,
CBSToHex(recipient.encrypted_keys[0].serial_number));
ASSERT_EQ(kRsaEncryptionOid,
CBSToHex(recipient.key_encryption_algorithm.algorithm_oid));
ASSERT_EQ(
"a873be9a23303b91769f8dcf641eb8ee95164710401d76db97f444fe73df1197110b1b7d"
"1d5c6bec413793d4c86371e9a96bd9a7313e02eb7bf6f94fcc9249237bc037df63ed4681"
"d7dd2ae27da31e5da4c42737516ecde4e7c61691508b4f9544348f7a5df646bf619466a1"
"4730d37d452bf98058b522ecd02138f2746f4b4745157753ed8d3998c88682424d19016d"
"11aaa0c20b8b3c2865deb2bf0f7b436bb82df10908ba8f7dc719ec11b7bae892a6e86d59"
"16895017b302e2cdcf9504e1271ac0b23bcf270c4d3d75c41ed24f308149f118a0e8ac29"
"4910ce67ff43fc379959e25f5ead1fb5354bb24ee5287d1a6d7327424fa11f0509f0f374"
"ebf9ecd3",
CBSToHex(recipient.encrypted_keys[0].encrypted_key));
EXPECT_EQ(
"0ad0fb4894c4c73c6d255e1c59f24b331000b0ce058cc1d2367d73a7a870eea5cfd46740"
"ad3fcf65f4e9acb1982937237b067692b9aa7731d39f28b0e5ad58682a3614ba3caf794e"
"1a71025642cb01cf401be16761d8cfbdcb41c998ad5d3704",
absl::BytesToHexString(CBSChunksToString(enveloped_data.content)));
EXPECT_EQ(kAes256CbcOid,
CBSToHex(enveloped_data.encryption_algorithm.algorithm_oid));
EXPECT_EQ("0410b0b6a6406c1a6a006c567400d664bfe9",
CBSToHex(enveloped_data.encryption_algorithm.parameter));
// No signer on the envelope.
ASSERT_EQ(0, enveloped_data.signers.size());
}
TEST(ParseCms, OpensslEncryptedEcc) {
std::string cms = GetCms(GetFileContents("encrypted-openssl-ecdh.msg"));
Content enveloped_data;
std::string error_message;
EXPECT_EQ(ErrorCode::OK,
ParseCms(cms.data(), cms.length(), &enveloped_data, &error_message))
<< error_message;
EXPECT_EQ(ContentType::ENVELOPED_DATA, enveloped_data.type);
EXPECT_EQ(2, enveloped_data.version);
ASSERT_EQ(0, enveloped_data.certificates.size());
ASSERT_EQ(1, enveloped_data.recipients.size());
const auto& recipient = enveloped_data.recipients[0];
EXPECT_EQ(3, recipient.version);
EXPECT_EQ(kTestIssuer, CBSToName(recipient.encrypted_keys[0].issuer_name));
EXPECT_EQ(kUser1EccSerialNumber,
CBSToHex(recipient.encrypted_keys[0].serial_number));
EXPECT_EQ(kEcdhStdSha1KdfOid,
CBSToHex(recipient.key_encryption_algorithm.algorithm_oid));
EXPECT_EQ("300f060b2a864886f70d01091003060500",
CBSToHex(recipient.key_encryption_algorithm.parameter));
EXPECT_EQ(
"a9ad0552a61f549516be729006670369449181fc845fd1647445d40c967062f2bc2c0a58"
"edb12c65",
CBSToHex(recipient.encrypted_keys[0].encrypted_key));
EXPECT_EQ(
"0004df6ea880dc4db5952082d0f1a19c61019f1d34b6dfde586d4515e7d43622949b4cc0"
"d63c6725615b5ecd0a421a8f144b53bbd53bc249abef0c2af1adc11c5e41",
CBSToHex(recipient.originator_identifier_or_key.public_key_value));
EXPECT_EQ("", CBSToHex(recipient.user_keying_material));
EXPECT_EQ("6854c40ae91385c43d593cb8576341f322ecb3691e5a219ee911aa28d4512a86",
absl::BytesToHexString(CBSChunksToString(enveloped_data.content))
.substr(0, 64));
EXPECT_EQ(kDesEde3Cbc,
CBSToHex(enveloped_data.encryption_algorithm.algorithm_oid));
EXPECT_EQ("301406082a864886f70d03070408a74c3bb3a5d47c6e",
CBSToHex(enveloped_data.encryption_algorithm.raw_value));
// No signer on the envelope.
ASSERT_EQ(0, enveloped_data.signers.size());
}
TEST(ParseCms, SimpleChecks) {
std::vector<std::string> files = {"encrypted-openssl-oaep.msg",
"signed-openssl-pss.msg"};
for (const auto& filename : files) {
std::string cms = GetCms(GetFileContents(filename));
Content contents;
std::string error_message;
auto status = ParseCms(cms.data(), cms.length(), &contents, &error_message);
EXPECT_EQ(ErrorCode::OK, status)
<< "File tested: " << filename << " error: " << error_message;
}
}
TEST(ParseCms, RFC4134_47) {
std::string cms = GetCms(GetFileContents("rfc4134-4.7.msg"));
Content signed_data;
std::string error_message;
EXPECT_EQ(ErrorCode::OK,
ParseCms(cms.data(), cms.length(), &signed_data, &error_message))
<< error_message;
EXPECT_EQ(ContentType::SIGNED_DATA, signed_data.type);
EXPECT_EQ(3, signed_data.version);
ASSERT_EQ(1, signed_data.certificates.size());
ASSERT_EQ(1, signed_data.signers.size());
const auto& signer = signed_data.signers[0];
EXPECT_EQ(3, signer.version);
EXPECT_EQ("be6ca1b3e3c1f7ed4370a4ce1301e2fde397fecd",
CBSToHex(signer.subject_key_identifier));
}
TEST(ParseCms, SignedOffice365Owa) {
std::string cms = GetCms(GetFileContents("signed-office365-owa.msg"));
Content signed_data;
std::string error_message;
EXPECT_EQ(ErrorCode::OK,
ParseCms(cms.data(), cms.length(), &signed_data, &error_message))
<< error_message;
EXPECT_EQ(ContentType::SIGNED_DATA, signed_data.type);
EXPECT_EQ(1, signed_data.version);
ASSERT_EQ(3, signed_data.certificates.size());
ASSERT_EQ(1, signed_data.signers.size());
const auto& signer = signed_data.signers[0];
EXPECT_EQ(1, signer.version);
}
TEST(ParseCms, ParseCertificateOnly) {
// This is a certificates only file (degenerate signature).
std::string cms = GetCms(GetFileContents("rfc4134-4.11.msg"));
Content signed_data;
std::string error_message;
EXPECT_EQ(ErrorCode::OK,
ParseCms(cms.data(), cms.length(), &signed_data, &error_message))
<< error_message;
EXPECT_EQ(ContentType::SIGNED_DATA, signed_data.type);
EXPECT_EQ(1, signed_data.version);
ASSERT_EQ(2, signed_data.certificates.size());
ASSERT_EQ(0, signed_data.signers.size());
}
TEST(ParseCms, ParseEnvelopedDataWithOriginatorInfo) {
std::string b64 = GetFileContents("encrypted-with-originator-info.msg");
std::string cms;
CHECK(absl::Base64Unescape(b64, &cms));
Content enveloped_data;
std::string error_message;
EXPECT_EQ(ErrorCode::OK,
ParseCms(cms.data(), cms.length(), &enveloped_data, &error_message))
<< error_message;
ASSERT_EQ(4, enveloped_data.certificates.size());
// Spot check the certificates.
EXPECT_THAT(CBSToString(enveloped_data.certificates[0]),
HasSubstr("AddTrust External CA Root"));
EXPECT_THAT(CBSToString(enveloped_data.certificates[1]),
HasSubstr("COMODO SHA-256 Client Authentication"));
EXPECT_THAT(CBSToString(enveloped_data.certificates[2]),
HasSubstr("tererqa3"));
EXPECT_THAT(CBSToString(enveloped_data.certificates[3]),
HasSubstr("ns-ex13.gtestexchange.com"));
ASSERT_EQ(2, enveloped_data.recipients.size());
// Check the first recipient.
const auto& recipient = enveloped_data.recipients[0];
ASSERT_EQ(0, recipient.version);
EXPECT_EQ(
"C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO "
"SHA-256 Client Authentication and Secure Email CA",
CBSToName(recipient.encrypted_keys[0].issuer_name));
EXPECT_EQ("4dc1e7496c5320405df99a567dde046f",
CBSToHex(recipient.encrypted_keys[0].serial_number));
}
} // namespace
} // namespace credentio_cms