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