| // 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 <stdint.h> | |
| #include <string> | |
| #include <vector> | |
| #include "crypto/default/cms/ber_parser.h" | |
| #include "crypto/default/cms/cms_error_code.h" | |
| #include "crypto/default/cms/oids.h" | |
| #include "openssl/base.h" | |
| #include "openssl/bytestring.h" | |
| namespace credentio_cms { | |
| namespace { | |
| constexpr unsigned ContextSpecificConstructed(int n) { | |
| return CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | n; | |
| } | |
| constexpr unsigned ContextSpecific(int n) { | |
| return CBS_ASN1_CONTEXT_SPECIFIC | n; | |
| } | |
| // Scoped class making it easier to keep track of the constructed tag by | |
| // calling EndConstructed on destruction. | |
| class ScopedConstructedElement { | |
| public: | |
| // Only calls EndConstructed when destroyed, use it when the constructed tag | |
| // was already read (usually with GetOptionalTag). | |
| explicit ScopedConstructedElement(BerParser* parser, const char* hint) | |
| : parser_(parser) { | |
| parser->SetElementName(hint); | |
| } | |
| // Reads expected_tag, provides a scope name for the error messages and calls | |
| // EndConstructed when destroyed. | |
| ScopedConstructedElement(unsigned expected_tag, const char* hint, | |
| BerParser* parser) | |
| : parser_(parser) { | |
| parser->GetTag(expected_tag); | |
| parser->SetElementName(hint); | |
| } | |
| ~ScopedConstructedElement() { parser_->EndConstructed(); } | |
| private: | |
| BerParser* parser_; // Not owned. | |
| }; | |
| // Some applications encode OCTET STRING as a constructed element composed | |
| // of octet strings. | |
| void ParseBerOctetString(int tag, BerParser* parser, | |
| std::vector<ByteString>* data) { | |
| data->clear(); | |
| if (parser->Peek(tag)) { | |
| data->push_back(ByteString()); | |
| parser->GetElementData(tag, data->back().cbs_ptr()); | |
| return; | |
| } | |
| ScopedConstructedElement constructed_string(tag | CBS_ASN1_CONSTRUCTED, | |
| "BerOctetString", parser); | |
| for (; !parser->IsNullOrEmpty();) { | |
| data->push_back(ByteString()); | |
| parser->GetElementData(CBS_ASN1_OCTETSTRING, data->back().cbs_ptr()); | |
| } | |
| } | |
| // Expect a sequence with a single OID in it. | |
| void ParseAlgorithmIdentifier(const char* hint, BerParser* parser, | |
| AlgorithmIdentifier* id) { | |
| parser->GetAnyElementAndSkipChildren(id->raw_value.cbs_ptr()); | |
| // Extract the algorithm identifier parts. | |
| // https://tools.ietf.org/html/rfc5280#section-4.1.1.2 | |
| BerParser id_parser(CBS_data(id->raw_value.cbs_ptr()), | |
| CBS_len(id->raw_value.cbs_ptr())); | |
| id_parser.GetTag(CBS_ASN1_SEQUENCE); | |
| id_parser.GetElementData(CBS_ASN1_OBJECT, id->algorithm_oid.cbs_ptr()); | |
| if (!id_parser.IsNullOrEmpty()) { | |
| id_parser.GetAnyElementAndSkipChildren(id->parameter.cbs_ptr()); | |
| } | |
| id_parser.EndConstructed(); | |
| if (!id_parser.ok()) { | |
| parser->SetError(id_parser.status(), hint); | |
| } | |
| } | |
| template <typename T> | |
| void GetIssuerAndSerialNumber(BerParser* parser, T* ids) { | |
| ScopedConstructedElement issuer_and_serial_number( | |
| CBS_ASN1_SEQUENCE, "Issuer and serial number", parser); | |
| parser->GetAnyElementAndSkipChildren(ids->issuer_name.cbs_ptr()); | |
| parser->GetElementData(CBS_ASN1_INTEGER, ids->serial_number.cbs_ptr()); | |
| } | |
| // Sn = Serial Number | |
| // Ski = Subject Key Identifier | |
| template <typename T> | |
| void GetIssuerAndSnOrSki(BerParser* parser, T* ids) { | |
| if (parser->Peek(CBS_ASN1_SEQUENCE)) { | |
| GetIssuerAndSerialNumber(parser, ids); | |
| } else if (parser->Peek(ContextSpecific(0))) { | |
| parser->GetElementData(ContextSpecific(0), | |
| ids->subject_key_identifier.cbs_ptr()); | |
| } | |
| } | |
| // http://tools.ietf.org/html/rfc5652#section-6.2.1 | |
| void ParseKeyTransRecipientInfo(BerParser* parser, RecipientInfo* recipient) { | |
| ScopedConstructedElement key_agree(parser, "KeyTransRecipientInfo"); | |
| parser->SetElementName("RecipientInfo"); | |
| recipient->version = parser->GetInt(); | |
| if (recipient->version != 0 && recipient->version != 2) { | |
| parser->SetError(static_cast<ErrorCode>(ErrorCode::UNSUPPORTED), | |
| "Unsupported recipient version"); | |
| return; | |
| } | |
| recipient->encrypted_keys.push_back(RecipientEncryptedKey()); | |
| GetIssuerAndSnOrSki(parser, &recipient->encrypted_keys.back()); | |
| ParseAlgorithmIdentifier("Key Encryption Algorithm", parser, | |
| &recipient->key_encryption_algorithm); | |
| parser->GetElementData( | |
| CBS_ASN1_OCTETSTRING, | |
| recipient->encrypted_keys.back().encrypted_key.cbs_ptr()); | |
| } | |
| void ParseRecipientEncryptedKey(BerParser* parser, RecipientEncryptedKey* key) { | |
| ScopedConstructedElement recipient_key(CBS_ASN1_SEQUENCE, | |
| "RecicientEncryptedKey", parser); | |
| if (parser->Peek(CBS_ASN1_SEQUENCE)) { | |
| GetIssuerAndSerialNumber(parser, key); | |
| } else { | |
| ScopedConstructedElement rid(ContextSpecificConstructed(0), | |
| "RecipientKeyIdentifier", parser); | |
| GetIssuerAndSerialNumber(parser, key); | |
| parser->GetOptionalElementData(CBS_ASN1_GENERALIZEDTIME, | |
| key->date.cbs_ptr()); | |
| if (!parser->IsNullOrEmpty()) { | |
| parser->GetAnyElementAndSkipChildren(key->other.cbs_ptr()); | |
| } | |
| } | |
| parser->GetElementData(CBS_ASN1_OCTETSTRING, key->encrypted_key.cbs_ptr()); | |
| } | |
| // http://tools.ietf.org/html/rfc5652#section-6.2.2 | |
| void ParseOriginatorPublicKey(BerParser* parser, | |
| OriginatorIdentifierOrKey* oik) { | |
| ScopedConstructedElement opk(parser, "OriginatorPublicKey"); | |
| ParseAlgorithmIdentifier("Public Key Algorithm", parser, | |
| &oik->public_key_algorithm); | |
| parser->GetElementData(CBS_ASN1_BITSTRING, oik->public_key_value.cbs_ptr()); | |
| } | |
| // http://tools.ietf.org/html/rfc5652#section-6.2.2 | |
| void ParseOriginatorIdentifierOrKey(BerParser* parser, | |
| OriginatorIdentifierOrKey* oik) { | |
| ScopedConstructedElement originator(ContextSpecificConstructed(0), | |
| "OriginatorIdentifierOrKey", parser); | |
| if (parser->Peek(CBS_ASN1_SEQUENCE)) { | |
| GetIssuerAndSerialNumber(parser, oik); | |
| } else if (parser->GetOptionalTag(ContextSpecificConstructed(0))) { | |
| parser->GetElementData(CBS_ASN1_OCTETSTRING, | |
| oik->subject_key_identifier.cbs_ptr()); | |
| parser->EndConstructed(); | |
| } else if (parser->GetOptionalTag(ContextSpecificConstructed(1))) { | |
| ParseOriginatorPublicKey(parser, oik); | |
| } else { | |
| parser->SetError(static_cast<ErrorCode>(ErrorCode::UNSUPPORTED), | |
| "Unsupported originator tag"); | |
| } | |
| } | |
| // http://tools.ietf.org/html/rfc5652#section-6.2.2 | |
| void ParseKeyAgreeRecipientInfo(BerParser* parser, RecipientInfo* recipient) { | |
| ScopedConstructedElement key_agree(parser, "KeyAgreeRecipientInfo"); | |
| recipient->version = parser->GetInt(); | |
| if (recipient->version != 3) { | |
| parser->SetError(static_cast<ErrorCode>(ErrorCode::UNSUPPORTED), | |
| "Unsupported key agree recipient version"); | |
| return; | |
| } | |
| ParseOriginatorIdentifierOrKey(parser, | |
| &recipient->originator_identifier_or_key); | |
| // User Key Material (UKM) | |
| if (parser->GetOptionalTag(ContextSpecificConstructed(1))) { | |
| parser->GetElementData(CBS_ASN1_OCTETSTRING, | |
| recipient->user_keying_material.cbs_ptr()); | |
| parser->EndConstructed(); | |
| } | |
| ParseAlgorithmIdentifier("Key Encryption Algorithm", parser, | |
| &recipient->key_encryption_algorithm); | |
| ScopedConstructedElement recipient_keys(CBS_ASN1_SEQUENCE, | |
| "RecipientEncryptedKeys", parser); | |
| for (; !parser->IsNullOrEmpty();) { | |
| RecipientEncryptedKey key; | |
| ParseRecipientEncryptedKey(parser, &key); | |
| recipient->encrypted_keys.push_back(key); | |
| } | |
| } | |
| // https://tools.ietf.org/html/rfc5751#section-2.5.3 | |
| bool ExtractSmimeEncryptionKeyPreference(SignerInfo* signer, | |
| ByteString key_preference) { | |
| SmimeEncryptionKeyPreference& preference = signer->encryption_key_preference; | |
| BerParser parser(CBS_data(key_preference.cbs_ptr()), | |
| CBS_len(key_preference.cbs_ptr())); | |
| if (parser.Peek(ContextSpecificConstructed(0))) { | |
| ScopedConstructedElement issuer_sn(ContextSpecificConstructed(0), | |
| "IssuerAndSerialNumber", &parser); | |
| parser.GetAnyElementAndSkipChildren(preference.issuer_name.cbs_ptr()); | |
| parser.GetElementData(CBS_ASN1_INTEGER, preference.serial_number.cbs_ptr()); | |
| } else if (parser.Peek(ContextSpecificConstructed(1))) { | |
| ScopedConstructedElement issuer_sn(ContextSpecificConstructed(1), | |
| "RecipientKeyIdentifier", &parser); | |
| parser.GetElementData(CBS_ASN1_OCTETSTRING, | |
| preference.subject_key_identifier.cbs_ptr()); | |
| parser.GetOptionalElementData(CBS_ASN1_GENERALIZEDTIME, | |
| preference.date.cbs_ptr()); | |
| parser.GetAnyElementAndSkipChildren(preference.other.cbs_ptr()); | |
| } else if (parser.Peek(ContextSpecificConstructed(2))) { | |
| parser.GetElementData(ContextSpecific(2), | |
| preference.subject_key_identifier.cbs_ptr()); | |
| } else { | |
| return false; | |
| } | |
| return parser.ok(); | |
| } | |
| bool ExtractSingleValue(const Attribute& attribute, unsigned expected_tag, | |
| CBS* out) { | |
| if (attribute.values.size() != 1) { | |
| return false; | |
| } | |
| CBS copy; | |
| CBS_init(©, CBS_data(attribute.values[0].cbs_ptr()), | |
| CBS_len(attribute.values[0].cbs_ptr())); | |
| return CBS_get_asn1(©, out, expected_tag) == 1; | |
| } | |
| bool ExtractCommonAttributes(SignerInfo* signer) { | |
| bool status = true; | |
| for (const auto& attribute : signer->signed_attributes) { | |
| if (CompareOid(attribute.type.cbs(), kContentAttributeType, | |
| sizeof(kContentAttributeType))) { | |
| status &= ExtractSingleValue(attribute, CBS_ASN1_OBJECT, | |
| signer->content_type_signed.cbs_ptr()); | |
| } else if (CompareOid(attribute.type.cbs(), kContentAttributeMessageDigest, | |
| sizeof(kContentAttributeMessageDigest))) { | |
| status &= ExtractSingleValue(attribute, CBS_ASN1_OCTETSTRING, | |
| signer->message_digest.cbs_ptr()); | |
| } else if (CompareOid(attribute.type.cbs(), | |
| kSmimeEncryptionKeyPreferenceOid, | |
| sizeof(kSmimeEncryptionKeyPreferenceOid))) { | |
| status &= (attribute.values.size() == 1); | |
| if (status) { | |
| status &= | |
| ExtractSmimeEncryptionKeyPreference(signer, attribute.values[0]); | |
| } | |
| } | |
| } | |
| return status; | |
| } | |
| // Extracts the message digest and content type from the signed attributes. | |
| // http://tools.ietf.org/html/rfc5652#section-5.3 | |
| void ParseSignedAttributes(BerParser* parser, SignerInfo* signer) { | |
| // Keep the raw signed attributes to make the signature check easier. | |
| parser->GetAnyElementAndSkipChildren(signer->raw_signed_attributes.cbs_ptr()); | |
| // Extract the content type and content digest. | |
| BerParser att_parser(CBS_data(signer->raw_signed_attributes.cbs_ptr()), | |
| CBS_len(signer->raw_signed_attributes.cbs_ptr())); | |
| att_parser.GetTag(ContextSpecificConstructed(0)); | |
| for (; !att_parser.IsNullOrEmpty();) { | |
| ScopedConstructedElement seq(CBS_ASN1_SEQUENCE, "Authenticated Attributes", | |
| &att_parser); | |
| signer->signed_attributes.push_back(Attribute()); | |
| Attribute& attribute = signer->signed_attributes.back(); | |
| att_parser.GetElementData(CBS_ASN1_OBJECT, attribute.type.cbs_ptr()); | |
| ScopedConstructedElement s(CBS_ASN1_SET, "Signed attribute values", | |
| &att_parser); | |
| for (; !att_parser.IsNullOrEmpty();) { | |
| attribute.values.push_back(ByteString()); | |
| att_parser.GetAnyElementAndSkipChildren( | |
| attribute.values.back().cbs_ptr()); | |
| } | |
| } | |
| att_parser.EndConstructed(); | |
| if (!att_parser.ok()) { | |
| parser->SetError(att_parser.status(), | |
| "Failed to parse the signed attributes"); | |
| return; | |
| } | |
| if (!ExtractCommonAttributes(signer)) { | |
| parser->SetError(att_parser.status(), | |
| "Invalid content type or digest attributes"); | |
| } | |
| } | |
| // Extract the unsigned attributes for anybody who needs to look at them. | |
| // http://tools.ietf.org/html/rfc5652#section-5.3 | |
| void ParseUnsignedAttributes(BerParser* parser, SignerInfo* signer) { | |
| parser->GetAnyElementAndSkipChildren( | |
| signer->raw_unsigned_attributes.cbs_ptr()); | |
| BerParser att_parser(CBS_data(signer->raw_unsigned_attributes.cbs_ptr()), | |
| CBS_len(signer->raw_unsigned_attributes.cbs_ptr())); | |
| att_parser.GetTag(ContextSpecificConstructed(1)); | |
| for (; !att_parser.IsNullOrEmpty();) { | |
| ScopedConstructedElement seq(CBS_ASN1_SEQUENCE, "Unsigned Attributes", | |
| &att_parser); | |
| signer->unsigned_attributes.push_back(Attribute()); | |
| Attribute& attribute = signer->unsigned_attributes.back(); | |
| att_parser.GetElementData(CBS_ASN1_OBJECT, attribute.type.cbs_ptr()); | |
| ScopedConstructedElement s(CBS_ASN1_SET, "Unsigned attribute values", | |
| &att_parser); | |
| for (; !att_parser.IsNullOrEmpty();) { | |
| attribute.values.push_back(ByteString()); | |
| att_parser.GetAnyElementAndSkipChildren( | |
| attribute.values.back().cbs_ptr()); | |
| } | |
| } | |
| att_parser.EndConstructed(); | |
| if (!att_parser.ok()) { | |
| parser->SetError(att_parser.status(), | |
| "Failed to parse the unsigned attributes"); | |
| } | |
| } | |
| // http://tools.ietf.org/html/rfc5652#section-5.3 | |
| static void ParseSignerInfoInternal(BerParser* parser, SignerInfo* signer) { | |
| ScopedConstructedElement signer_info(CBS_ASN1_SEQUENCE, "SignerInfo", parser); | |
| signer->version = parser->GetInt(); | |
| if (signer->version != 1 && signer->version != 3) { | |
| parser->SetError(ErrorCode::UNSUPPORTED, "Unsupported signer version"); | |
| return; | |
| } | |
| GetIssuerAndSnOrSki(parser, signer); | |
| ParseAlgorithmIdentifier("Digest Algorithm", parser, | |
| &signer->digest_algorithm); | |
| // Authenticated attributes | |
| if (parser->Peek(ContextSpecificConstructed(0))) { | |
| ParseSignedAttributes(parser, signer); | |
| } | |
| ParseAlgorithmIdentifier("Signature Algorithm", parser, | |
| &signer->signature_algorithm); | |
| parser->GetElementData(CBS_ASN1_OCTETSTRING, | |
| signer->signature_value.cbs_ptr()); | |
| // Implicit tag 1: Unauthenticated attributes | |
| if (parser->Peek(ContextSpecificConstructed(1))) { | |
| ParseUnsignedAttributes(parser, signer); | |
| } | |
| } | |
| void ParseCertificates(BerParser* parser, | |
| std::vector<ByteString>* certificates) { | |
| ScopedConstructedElement s(ContextSpecificConstructed(0), "Certificates", | |
| parser); | |
| for (; !parser->IsNullOrEmpty();) { | |
| ByteString out; | |
| parser->GetAnyElementAndSkipChildren(out.cbs_ptr()); | |
| certificates->push_back(out); | |
| } | |
| } | |
| // Certificates Revocation Lists are ignored. | |
| void ParseCrls(BerParser* parser) { | |
| ScopedConstructedElement s(ContextSpecificConstructed(1), "CRL", parser); | |
| for (; !parser->IsNullOrEmpty();) { | |
| ByteString out; | |
| parser->GetAnyElementAndSkipChildren(out.cbs_ptr()); | |
| } | |
| } | |
| // https://tools.ietf.org/html/rfc5652#section-5.1 | |
| void ParseSignedData(BerParser* parser, Content* content) { | |
| content->type = ContentType::SIGNED_DATA; | |
| ScopedConstructedElement signed_data(CBS_ASN1_SEQUENCE, "SignedData", parser); | |
| content->version = parser->GetInt(); | |
| { | |
| ScopedConstructedElement algorigthm_set(CBS_ASN1_SET, "AlgorithmSet", | |
| parser); | |
| for (; !parser->IsNullOrEmpty();) { | |
| content->digest_algorithms.push_back(AlgorithmIdentifier()); | |
| ParseAlgorithmIdentifier("DigestAlgorithm", parser, | |
| &content->digest_algorithms.back()); | |
| } | |
| } | |
| { | |
| ScopedConstructedElement contentInfo(CBS_ASN1_SEQUENCE, "Content", parser); | |
| parser->GetElementData(CBS_ASN1_OBJECT, content->content_type.cbs_ptr()); | |
| content->content_tag = 0; | |
| if (parser->GetOptionalTag(ContextSpecificConstructed(0))) { | |
| if (parser->Peek(CBS_ASN1_OCTETSTRING) || | |
| parser->Peek(CBS_ASN1_OCTETSTRING | CBS_ASN1_CONSTRUCTED)) { | |
| content->content_offsets.first = parser->GetCurrentOffset(); | |
| ParseBerOctetString(CBS_ASN1_OCTETSTRING, parser, &content->content); | |
| content->content_offsets.second = parser->GetCurrentOffset(); | |
| content->content_tag = CBS_ASN1_OCTETSTRING; | |
| } else if (parser->Peek(CBS_ASN1_SEQUENCE)) { | |
| // PKCS#7 compatibility. | |
| // See https://tools.ietf.org/html/rfc5652#section-5.2.1 | |
| content->content_offsets.first = parser->GetCurrentOffset(); | |
| ByteString bytes; | |
| parser->GetAnyElementAndSkipChildren(bytes.cbs_ptr()); | |
| content->content_offsets.second = parser->GetCurrentOffset(); | |
| content->content.push_back(bytes); | |
| content->content_tag = CBS_ASN1_SEQUENCE; | |
| } else { | |
| parser->SetError(ErrorCode::UNSUPPORTED, "Unexpected type"); | |
| } | |
| parser->EndConstructed(); // Content | |
| } | |
| } | |
| // Certificates | |
| if (parser->Peek(ContextSpecificConstructed(0))) { | |
| ParseCertificates(parser, &content->certificates); | |
| } | |
| // Certificate Revocation List are ignored. | |
| if (parser->Peek(ContextSpecificConstructed(1))) { | |
| ParseCrls(parser); | |
| } | |
| // Signers | |
| { | |
| ScopedConstructedElement signers(CBS_ASN1_SET, "Signers", parser); | |
| for (; !parser->IsNullOrEmpty();) { | |
| content->signers.push_back(SignerInfo()); | |
| ParseSignerInfoInternal(parser, &content->signers.back()); | |
| } | |
| } | |
| } | |
| // http://tools.ietf.org/html/rfc5652#section-6.1 | |
| void ParseEncryptedContentInfo(BerParser* parser, Content* content) { | |
| ScopedConstructedElement s(CBS_ASN1_SEQUENCE, "EncryptedContentInfo", parser); | |
| CBS oid; | |
| parser->GetElementData(CBS_ASN1_OBJECT, &oid); | |
| if (!CompareOid(oid, kDataOid, sizeof(kDataOid))) { | |
| parser->SetError(ErrorCode::UNSUPPORTED, "Unexpected content type."); | |
| return; | |
| } | |
| ParseAlgorithmIdentifier("content encryption algorithm", parser, | |
| &content->encryption_algorithm); | |
| if (parser->Peek(CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED) || | |
| parser->Peek(CBS_ASN1_CONTEXT_SPECIFIC)) { | |
| content->content_offsets.first = parser->GetCurrentOffset(); | |
| ParseBerOctetString(CBS_ASN1_CONTEXT_SPECIFIC, parser, &content->content); | |
| content->content_offsets.second = parser->GetCurrentOffset(); | |
| } else { | |
| content->content.clear(); | |
| } | |
| } | |
| // http://tools.ietf.org/html/rfc5652#section-6.2 | |
| void ParseRecipientsInfo(BerParser* parser, Content* content) { | |
| ScopedConstructedElement recipients(CBS_ASN1_SET, "Recipients", parser); | |
| for (; !parser->IsNullOrEmpty();) { | |
| content->recipients.push_back(RecipientInfo()); | |
| if (parser->GetOptionalTag(CBS_ASN1_SEQUENCE)) { | |
| // http://tools.ietf.org/html/rfc2630#section-6.2.1 | |
| ParseKeyTransRecipientInfo(parser, &content->recipients.back()); | |
| } else if (parser->GetOptionalTag(ContextSpecificConstructed(1))) { | |
| // http://tools.ietf.org/html/rfc2630#section-6.2.2 | |
| ParseKeyAgreeRecipientInfo(parser, &content->recipients.back()); | |
| } else { | |
| // Unsupported: | |
| // KEKRecipientInfo, PasswordRecipientInfo, OtherRecipientInfo | |
| parser->SetError(ErrorCode::UNSUPPORTED, "Unsupported recipient type."); | |
| return; | |
| } | |
| } | |
| } | |
| // https://tools.ietf.org/html/rfc5652#section-6.1 | |
| void ParseEnvelopedData(BerParser* parser, Content* content) { | |
| content->type = ContentType::ENVELOPED_DATA; | |
| ScopedConstructedElement enveloped_data(CBS_ASN1_SEQUENCE, "EnvelopedData", | |
| parser); | |
| content->version = parser->GetInt(); | |
| if (content->version != 0 && content->version != 2) { | |
| parser->SetError(ErrorCode::UNSUPPORTED, "Unsupported envelope version."); | |
| return; | |
| } | |
| if (parser->Peek(ContextSpecificConstructed(0))) { | |
| ScopedConstructedElement s(ContextSpecificConstructed(0), "OriginatorInfo", | |
| parser); | |
| if (parser->Peek(ContextSpecificConstructed(0))) { | |
| ParseCertificates(parser, &content->certificates); | |
| } | |
| if (parser->Peek(ContextSpecificConstructed(1))) { | |
| ParseCrls(parser); | |
| } | |
| } | |
| ParseRecipientsInfo(parser, content); | |
| ParseEncryptedContentInfo(parser, content); | |
| // UnprotectedAttributes are not used in this implementation. | |
| if (parser->Peek(ContextSpecificConstructed(1))) { | |
| CBS dummy; | |
| parser->GetAnyElementAndSkipChildren(&dummy); | |
| } | |
| } | |
| } // namespace | |
| // RFC 5652 (partial support), RFC 2633 SMIME and RFC 2315 PKCS #7 | |
| ErrorCode ParseCms(const char* data, size_t size, Content* contents, | |
| std::string* error_message) { | |
| BerParser parser(reinterpret_cast<const uint8_t*>(data), size); | |
| parser.SetElementName("ContentInfo"); | |
| parser.GetTag(CBS_ASN1_SEQUENCE); | |
| CBS oid; | |
| parser.GetElementData(CBS_ASN1_OBJECT, &oid); | |
| parser.GetTag(ContextSpecificConstructed(0)); | |
| if (CompareOid(oid, kSignedDataOid, sizeof(kSignedDataOid))) { | |
| ParseSignedData(&parser, contents); | |
| } else if (CompareOid(oid, kEnvelopedDataOid, sizeof(kEnvelopedDataOid))) { | |
| ParseEnvelopedData(&parser, contents); | |
| } else { | |
| parser.SetError(ErrorCode::UNSUPPORTED, "Unsupported data type."); | |
| } | |
| parser.EndConstructed(); | |
| *error_message = parser.error_message(); | |
| return parser.status(); | |
| } | |
| } // namespace credentio_cms |