/* * (C) 2024 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include #include #include #include #include #include #include namespace Botan { void AlternativeName::add_uri(std::string_view uri) { if(!uri.empty()) { m_uri.insert(std::string(uri)); } } void AlternativeName::add_email(std::string_view addr) { if(!addr.empty()) { m_email.insert(std::string(addr)); } } void AlternativeName::add_dns(std::string_view dns) { if(!dns.empty()) { m_dns.insert(tolower_string(dns)); } } void AlternativeName::add_other_name(const OID& oid, const ASN1_String& value) { m_othernames.insert(std::make_pair(oid, value)); } void AlternativeName::add_dn(const X509_DN& dn) { m_dn_names.insert(dn); } void AlternativeName::add_ipv4_address(uint32_t ip) { m_ipv4_addr.insert(ip); } size_t AlternativeName::count() const { const auto sum = checked_add( m_dns.size(), m_uri.size(), m_email.size(), m_ipv4_addr.size(), m_dn_names.size(), m_othernames.size()); return BOTAN_ASSERT_IS_SOME(sum); } bool AlternativeName::has_items() const { return this->count() > 0; } void AlternativeName::encode_into(DER_Encoder& der) const { der.start_sequence(); /* GeneralName ::= CHOICE { otherName [0] OtherName, rfc822Name [1] IA5String, dNSName [2] IA5String, x400Address [3] ORAddress, directoryName [4] Name, ediPartyName [5] EDIPartyName, uniformResourceIdentifier [6] IA5String, iPAddress [7] OCTET STRING, registeredID [8] OBJECT IDENTIFIER } */ for(const auto& othername : m_othernames) { der.start_explicit(0) .encode(othername.first) .start_explicit(0) .encode(othername.second) .end_explicit() .end_explicit(); } for(const auto& name : m_email) { ASN1_String str(name, ASN1_Type::Ia5String); der.add_object(ASN1_Type(1), ASN1_Class::ContextSpecific, str.value()); } for(const auto& name : m_dns) { ASN1_String str(name, ASN1_Type::Ia5String); der.add_object(ASN1_Type(2), ASN1_Class::ContextSpecific, str.value()); } for(const auto& name : m_dn_names) { der.add_object(ASN1_Type(4), ASN1_Class::ExplicitContextSpecific, name.DER_encode()); } for(const auto& name : m_uri) { ASN1_String str(name, ASN1_Type::Ia5String); der.add_object(ASN1_Type(6), ASN1_Class::ContextSpecific, str.value()); } for(uint32_t ip : m_ipv4_addr) { auto ip_buf = store_be(ip); // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) der.add_object(ASN1_Type(7), ASN1_Class::ContextSpecific, ip_buf.data(), 4); } der.end_cons(); } void AlternativeName::decode_from(BER_Decoder& source) { BER_Decoder names = source.start_sequence(); while(names.more_items()) { BER_Object obj = names.get_next_object(); if(obj.is_a(0, ASN1_Class::ExplicitContextSpecific)) { BER_Decoder othername(obj); OID oid; othername.decode(oid); if(othername.more_items()) { BER_Object othername_value_outer = othername.get_next_object(); othername.verify_end(); if(!othername_value_outer.is_a(0, ASN1_Class::ExplicitContextSpecific)) { throw Decoding_Error("Invalid tags on otherName value"); } BER_Decoder othername_value_inner(othername_value_outer); BER_Object value = othername_value_inner.get_next_object(); othername_value_inner.verify_end(); if(ASN1_String::is_string_type(value.type()) && value.get_class() == ASN1_Class::Universal) { add_othername(oid, ASN1::to_string(value), value.type()); } } } else if(obj.is_a(1, ASN1_Class::ContextSpecific)) { add_email(ASN1::to_string(obj)); } else if(obj.is_a(2, ASN1_Class::ContextSpecific)) { m_dns.insert(check_and_canonicalize_dns_name(ASN1::to_string(obj))); } else if(obj.is_a(4, ASN1_Class::ContextSpecific | ASN1_Class::Constructed)) { BER_Decoder dec(obj); X509_DN dn; dec.decode(dn); this->add_dn(dn); } else if(obj.is_a(6, ASN1_Class::ContextSpecific)) { this->add_uri(ASN1::to_string(obj)); } else if(obj.is_a(7, ASN1_Class::ContextSpecific)) { if(obj.length() == 4) { const uint32_t ip = load_be(obj.bits(), 0); this->add_ipv4_address(ip); } else if(obj.length() != 16) { throw Decoding_Error("Invalid IP constraint neither IPv4 or IPv6"); } } } } } // namespace Botan