| // 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 |