2020-04-21 01:57:23 +04:30
/*
* Copyright ( c ) 2020 , Ali Mohammad Pur < ali . mpfard @ gmail . com >
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
*
* 1. Redistributions of source code must retain the above copyright notice , this
* list of conditions and the following disclaimer .
*
* 2. Redistributions in binary form must reproduce the above copyright notice ,
* this list of conditions and the following disclaimer in the documentation
* and / or other materials provided with the distribution .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS "
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
* DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY ,
* OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
2021-01-17 16:57:17 +01:00
# include <AK/Debug.h>
2020-08-25 15:11:15 +02:00
# include <AK/Endian.h>
2020-10-30 11:56:31 +03:30
# include <LibCore/ConfigFile.h>
2020-05-06 21:43:27 +04:30
# include <LibCore/DateTime.h>
2021-04-18 13:46:17 +04:30
# include <LibCore/File.h>
# include <LibCore/FileStream.h>
2020-04-21 01:57:23 +04:30
# include <LibCore/Timer.h>
2021-04-18 13:46:17 +04:30
# include <LibCrypto/ASN1/ASN1.h>
2020-04-21 01:57:23 +04:30
# include <LibCrypto/ASN1/DER.h>
2020-08-02 05:27:42 +04:30
# include <LibCrypto/ASN1/PEM.h>
2020-04-21 01:57:23 +04:30
# include <LibCrypto/PK/Code/EMSA_PSS.h>
# include <LibTLS/TLSv12.h>
2021-03-12 17:29:37 +01:00
# include <errno.h>
2020-04-21 01:57:23 +04:30
2020-05-29 22:22:01 +02:00
# ifndef SOCK_NONBLOCK
# include <sys / ioctl.h>
# endif
2020-04-21 01:57:23 +04:30
namespace TLS {
2021-04-18 13:46:17 +04:30
constexpr static Array < int , 4 >
common_name_oid { 2 , 5 , 4 , 3 } ,
country_name_oid { 2 , 5 , 4 , 6 } ,
locality_name_oid { 2 , 5 , 4 , 7 } ,
organization_name_oid { 2 , 5 , 4 , 10 } ,
organizational_unit_name_oid { 2 , 5 , 4 , 11 } ;
2020-04-21 01:57:23 +04:30
2021-04-18 13:46:17 +04:30
constexpr static Array < int , 7 >
rsa_encryption_oid { 1 , 2 , 840 , 113549 , 1 , 1 , 1 } ,
rsa_md5_encryption_oid { 1 , 2 , 840 , 113549 , 1 , 1 , 4 } ,
rsa_sha1_encryption_oid { 1 , 2 , 840 , 113549 , 1 , 1 , 5 } ,
rsa_sha256_encryption_oid { 1 , 2 , 840 , 113549 , 1 , 1 , 11 } ,
rsa_sha512_encryption_oid { 1 , 2 , 840 , 113549 , 1 , 1 , 13 } ;
2020-04-21 01:57:23 +04:30
2021-04-18 13:46:17 +04:30
constexpr static Array < int , 4 >
subject_alternative_name_oid { 2 , 5 , 29 , 17 } ;
2020-04-21 01:57:23 +04:30
2021-04-18 13:46:17 +04:30
Optional < Certificate > TLSv12 : : parse_asn1 ( ReadonlyBytes buffer , bool ) const
2020-12-13 22:25:09 +03:30
{
2021-04-18 13:46:17 +04:30
# define ENTER_SCOPE_WITHOUT_TYPECHECK(scope) \
do { \
if ( auto result = decoder . enter ( ) ; result . has_value ( ) ) { \
dbgln_if ( TLS_DEBUG , " Failed to enter object ( " scope " ): {} " , result . value ( ) ) ; \
return { } ; \
} \
} while ( 0 )
# define ENTER_SCOPE_OR_FAIL(kind_name, scope) \
do { \
if ( auto tag = decoder . peek ( ) ; tag . is_error ( ) | | tag . value ( ) . kind ! = Crypto : : ASN1 : : Kind : : kind_name ) { \
if constexpr ( TLS_DEBUG ) { \
if ( tag . is_error ( ) ) \
dbgln ( scope " data was invalid: {} " , tag . error ( ) ) ; \
else \
dbgln ( scope " data was not of kind " # kind_name ) ; \
} \
return { } ; \
} \
ENTER_SCOPE_WITHOUT_TYPECHECK ( scope ) ; \
} while ( 0 )
# define EXIT_SCOPE(scope) \
do { \
if ( auto error = decoder . leave ( ) ; error . has_value ( ) ) { \
dbgln_if ( TLS_DEBUG , " Error while exiting scope " scope " : {} " , error . value ( ) ) ; \
return { } ; \
} \
} while ( 0 )
# define ENSURE_OBJECT_KIND(_kind_name, scope) \
do { \
if ( auto tag = decoder . peek ( ) ; tag . is_error ( ) | | tag . value ( ) . kind ! = Crypto : : ASN1 : : Kind : : _kind_name ) { \
if constexpr ( TLS_DEBUG ) { \
if ( tag . is_error ( ) ) \
dbgln ( scope " data was invalid: {} " , tag . error ( ) ) ; \
else \
dbgln ( scope " data was not of kind " # _kind_name " , it was {} " , Crypto : : ASN1 : : kind_name ( tag . value ( ) . kind ) ) ; \
} \
return { } ; \
} \
} while ( 0 )
# define READ_OBJECT_OR_FAIL(kind_name, type_name, value_name, scope) \
auto value_name # # _result = decoder . read < type_name > ( Crypto : : ASN1 : : Class : : Universal , Crypto : : ASN1 : : Kind : : kind_name ) ; \
if ( value_name # # _result . is_error ( ) ) { \
dbgln_if ( TLS_DEBUG , scope " read of kind " # kind_name " failed: {} " , value_name # # _result . error ( ) ) ; \
return { } ; \
} \
auto value_name = value_name # # _result . release_value ( ) ;
# define DROP_OBJECT_OR_FAIL(scope) \
do { \
if ( auto error = decoder . drop ( ) ; error . has_value ( ) ) { \
dbgln_if ( TLS_DEBUG , scope " read failed: {} " , error . value ( ) ) ; \
} \
} while ( 0 )
Certificate certificate ;
Crypto : : ASN1 : : Decoder decoder { buffer } ;
// Certificate ::= Sequence {
// certificate TBSCertificate,
// signature_algorithm AlgorithmIdentifier,
// signature_value BitString
// }
ENTER_SCOPE_OR_FAIL ( Sequence , " Certificate " ) ;
// TBSCertificate ::= Sequence {
// version (0) EXPLICIT Version DEFAULT v1,
// serial_number CertificateSerialNumber,
// signature AlgorithmIdentifier,
// issuer Name,
// validity Validity,
// subject Name,
// subject_public_key_info SubjectPublicKeyInfo,
// issuer_unique_id (1) IMPLICIT UniqueIdentifer OPTIONAL (if present, version > v1),
// subject_unique_id (2) IMPLICIT UniqueIdentiier OPTIONAL (if present, version > v1),
// extensions (3) EXPLICIT Extensions OPTIONAL (if present, version > v2)
// }
ENTER_SCOPE_OR_FAIL ( Sequence , " Certificate::TBSCertificate " ) ;
// version
{
// Version :: Integer { v1(0), v2(1), v3(2) } (Optional)
if ( auto tag = decoder . peek ( ) ; ! tag . is_error ( ) & & tag . value ( ) . type = = Crypto : : ASN1 : : Type : : Constructed ) {
ENTER_SCOPE_WITHOUT_TYPECHECK ( " Certificate::version " ) ;
READ_OBJECT_OR_FAIL ( Integer , Crypto : : UnsignedBigInteger , value , " Certificate::version " ) ;
if ( ! ( value < 3 ) ) {
dbgln_if ( TLS_DEBUG , " Certificate::version Invalid value for version: {} " , value . to_base10 ( ) ) ;
return { } ;
}
certificate . version = value . words ( ) [ 0 ] ;
EXIT_SCOPE ( " Certificate::version " ) ;
} else {
certificate . version = 0 ;
2020-12-13 22:25:09 +03:30
}
2021-04-18 13:46:17 +04:30
}
// serial_number
{
// CertificateSerialNumber :: Integer
READ_OBJECT_OR_FAIL ( Integer , Crypto : : UnsignedBigInteger , value , " Certificate::serial_number " ) ;
certificate . serial_number = move ( value ) ;
}
auto parse_algorithm_identifier = [ & ] ( CertificateKeyAlgorithm & field ) - > Optional < bool > {
// AlgorithmIdentifier ::= Sequence {
// algorithm ObjectIdentifier,
// parameters ANY OPTIONAL
// }
ENTER_SCOPE_OR_FAIL ( Sequence , " AlgorithmIdentifier " ) ;
READ_OBJECT_OR_FAIL ( ObjectIdentifier , Vector < int > , identifier , " AlgorithmIdentifier::algorithm " ) ;
if ( identifier = = rsa_encryption_oid )
field = CertificateKeyAlgorithm : : RSA_RSA ;
else if ( identifier = = rsa_md5_encryption_oid )
field = CertificateKeyAlgorithm : : RSA_MD5 ;
else if ( identifier = = rsa_sha1_encryption_oid )
field = CertificateKeyAlgorithm : : RSA_SHA1 ;
else if ( identifier = = rsa_sha256_encryption_oid )
field = CertificateKeyAlgorithm : : RSA_SHA256 ;
else if ( identifier = = rsa_sha512_encryption_oid )
field = CertificateKeyAlgorithm : : RSA_SHA512 ;
else
return { } ;
EXIT_SCOPE ( " AlgorithmIdentifier " ) ;
2020-12-13 22:25:09 +03:30
return true ;
} ;
2021-04-18 13:46:17 +04:30
// signature
{
if ( ! parse_algorithm_identifier ( certificate . algorithm ) . has_value ( ) )
return { } ;
}
auto parse_name = [ & ] ( auto & name_struct ) - > Optional < bool > {
// Name ::= Choice {
// rdn_sequence RDNSequence
// } // NOTE: since this is the only alternative, there's no index
// RDNSequence ::= Sequence OF RelativeDistinguishedName
ENTER_SCOPE_OR_FAIL ( Sequence , " Certificate::TBSCertificate::issuer/subject " ) ;
// RelativeDistinguishedName ::= Set OF AttributeTypeAndValue
// AttributeTypeAndValue ::= Sequence {
// type AttributeType,
// value AttributeValue
// }
// AttributeType ::= ObjectIdentifier
// AttributeValue ::= Any
while ( ! decoder . eof ( ) ) {
// Parse only the the required fields, and ignore the rest.
ENTER_SCOPE_OR_FAIL ( Set , " Certificate::TBSCertificate::issuer/subject::$::RelativeDistinguishedName " ) ;
while ( ! decoder . eof ( ) ) {
ENTER_SCOPE_OR_FAIL ( Sequence , " Certificate::TBSCertificate::issuer/subject::$::RelativeDistinguishedName::$::AttributeTypeAndValue " ) ;
ENSURE_OBJECT_KIND ( ObjectIdentifier , " Certificate::TBSCertificate::issuer/subject::$::RelativeDistinguishedName::$::AttributeTypeAndValue::type " ) ;
if ( auto type_identifier_or_error = decoder . read < Vector < int > > ( ) ; ! type_identifier_or_error . is_error ( ) ) {
// Figure out what type of identifier this is
auto & identifier = type_identifier_or_error . value ( ) ;
if ( identifier = = common_name_oid ) {
READ_OBJECT_OR_FAIL ( PrintableString , StringView , name ,
" Certificate::TBSCertificate::issuer/subject::$::RelativeDistinguishedName::$::AttributeTypeAndValue::Value " ) ;
name_struct . subject = name ;
} else if ( identifier = = country_name_oid ) {
READ_OBJECT_OR_FAIL ( PrintableString , StringView , name ,
" Certificate::TBSCertificate::issuer/subject::$::RelativeDistinguishedName::$::AttributeTypeAndValue::Value " ) ;
name_struct . country = name ;
} else if ( identifier = = locality_name_oid ) {
READ_OBJECT_OR_FAIL ( PrintableString , StringView , name ,
" Certificate::TBSCertificate::issuer/subject::$::RelativeDistinguishedName::$::AttributeTypeAndValue::Value " ) ;
name_struct . location = name ;
} else if ( identifier = = organization_name_oid ) {
READ_OBJECT_OR_FAIL ( PrintableString , StringView , name ,
" Certificate::TBSCertificate::issuer/subject::$::RelativeDistinguishedName::$::AttributeTypeAndValue::Value " ) ;
name_struct . entity = name ;
} else if ( identifier = = organizational_unit_name_oid ) {
READ_OBJECT_OR_FAIL ( PrintableString , StringView , name ,
" Certificate::TBSCertificate::issuer/subject::$::RelativeDistinguishedName::$::AttributeTypeAndValue::Value " ) ;
name_struct . unit = name ;
}
} else {
dbgln_if ( TLS_DEBUG , " Certificate::TBSCertificate::issuer/subject::$::RelativeDistinguishedName::$::AttributeTypeAndValue::type data was invalid: {} " , type_identifier_or_error . error ( ) ) ;
return { } ;
}
2020-05-06 21:43:27 +04:30
2021-04-18 13:46:17 +04:30
EXIT_SCOPE ( " Certificate::TBSCertificate::issuer/subject::$::RelativeDistinguishedName::$::AttributeTypeAndValue " ) ;
}
EXIT_SCOPE ( " Certificate::TBSCertificate::issuer/subject::$::RelativeDistinguishedName " ) ;
}
2020-05-06 21:43:27 +04:30
2021-04-18 13:46:17 +04:30
EXIT_SCOPE ( " Certificate::TBSCertificate::issuer/subject " ) ;
return true ;
} ;
2020-05-06 21:43:27 +04:30
2021-04-18 13:46:17 +04:30
// issuer
{
if ( ! parse_name ( certificate . issuer ) . has_value ( ) )
return { } ;
}
// validity
{
ENTER_SCOPE_OR_FAIL ( Sequence , " Certificate::TBSCertificate::Validity " ) ;
auto parse_time = [ & ] ( Core : : DateTime & datetime ) - > Optional < bool > {
// Time ::= Choice {
// utc_time UTCTime,
// general_time GeneralizedTime
// }
auto tag = decoder . peek ( ) ;
if ( tag . is_error ( ) ) {
dbgln_if ( 1 , " Certificate::TBSCertificate::Validity::$::Time failed to read tag: {} " , tag . error ( ) ) ;
return { } ;
} ;
if ( tag . value ( ) . kind = = Crypto : : ASN1 : : Kind : : UTCTime ) {
READ_OBJECT_OR_FAIL ( UTCTime , StringView , time , " Certificate::TBSCertificate::Validity::$ " ) ;
auto result = Crypto : : ASN1 : : parse_utc_time ( time ) ;
if ( ! result . has_value ( ) ) {
dbgln_if ( 1 , " Certificate::TBSCertificate::Validity::$::Time Invalid UTC Time: {} " , time ) ;
return { } ;
}
datetime = result . release_value ( ) ;
return true ;
}
2020-05-06 21:43:27 +04:30
2021-04-18 13:46:17 +04:30
if ( tag . value ( ) . kind = = Crypto : : ASN1 : : Kind : : GeneralizedTime ) {
READ_OBJECT_OR_FAIL ( UTCTime , StringView , time , " Certificate::TBSCertificate::Validity::$ " ) ;
auto result = Crypto : : ASN1 : : parse_generalized_time ( time ) ;
if ( ! result . has_value ( ) ) {
dbgln_if ( 1 , " Certificate::TBSCertificate::Validity::$::Time Invalid Generalized Time: {} " , time ) ;
return { } ;
}
datetime = result . release_value ( ) ;
return true ;
}
2020-05-06 21:43:27 +04:30
2021-04-18 13:46:17 +04:30
dbgln_if ( 1 , " Unrecognised Time format {} " , Crypto : : ASN1 : : kind_name ( tag . value ( ) . kind ) ) ;
return { } ;
} ;
2020-04-21 01:57:23 +04:30
2021-04-18 13:46:17 +04:30
if ( ! parse_time ( certificate . not_before ) . has_value ( ) )
return { } ;
2020-05-06 21:43:27 +04:30
2021-04-18 13:46:17 +04:30
if ( ! parse_time ( certificate . not_after ) . has_value ( ) )
return { } ;
EXIT_SCOPE ( " Certificate::TBSCertificate::Validity " ) ;
2020-05-06 21:43:27 +04:30
}
2021-04-18 13:46:17 +04:30
// subject
{
if ( ! parse_name ( certificate . subject ) . has_value ( ) )
return { } ;
2020-05-06 21:43:27 +04:30
}
2021-04-18 13:46:17 +04:30
// subject_public_key_info
{
// SubjectPublicKeyInfo ::= Sequence {
// algorithm AlgorithmIdentifier,
// subject_public_key BitString
// }
ENTER_SCOPE_OR_FAIL ( Sequence , " Certificate::TBSCertificate::subject_public_key_info " ) ;
2020-04-21 01:57:23 +04:30
2021-04-18 13:46:17 +04:30
if ( ! parse_algorithm_identifier ( certificate . key_algorithm ) . has_value ( ) )
return { } ;
2020-04-23 02:53:11 +04:30
2021-04-18 13:46:17 +04:30
READ_OBJECT_OR_FAIL ( BitString , const BitmapView , value , " Certificate::TBSCertificate::subject_public_key_info::subject_public_key_info " ) ;
// Note: Once we support other kinds of keys, make sure to check the kind here!
auto key = Crypto : : PK : : RSA : : parse_rsa_key ( { value . data ( ) , value . size_in_bytes ( ) } ) ;
if ( ! key . public_key . length ( ) ) {
dbgln_if ( TLS_DEBUG , " Certificate::TBSCertificate::subject_public_key_info::subject_public_key_info: Invalid key " ) ;
return { } ;
2020-04-23 02:53:11 +04:30
}
2021-04-18 13:46:17 +04:30
certificate . public_key = move ( key . public_key ) ;
EXIT_SCOPE ( " Certificate::TBSCertificate::subject_public_key_info " ) ;
2020-04-21 01:57:23 +04:30
}
2021-04-18 13:46:17 +04:30
auto parse_unique_identifier = [ & ] ( ) - > Optional < bool > {
if ( certificate . version = = 0 )
return true ;
2020-04-21 01:57:23 +04:30
2021-04-18 13:46:17 +04:30
auto tag = decoder . peek ( ) ;
if ( tag . is_error ( ) ) {
dbgln_if ( TLS_DEBUG , " Certificate::TBSCertificate::*::UniqueIdentifier could not read tag: {} " , tag . error ( ) ) ;
return { } ;
2020-04-23 02:53:11 +04:30
}
2020-04-21 01:57:23 +04:30
2021-04-18 13:46:17 +04:30
// The spec says to just ignore these.
if ( static_cast < u8 > ( tag . value ( ) . kind ) = = 1 | | static_cast < u8 > ( tag . value ( ) . kind ) = = 2 )
DROP_OBJECT_OR_FAIL ( " UniqueIdentifier " ) ;
2020-04-21 01:57:23 +04:30
2021-04-18 13:46:17 +04:30
return true ;
} ;
2020-04-21 01:57:23 +04:30
2021-04-18 13:46:17 +04:30
// issuer_unique_identifier
{
if ( ! parse_unique_identifier ( ) . has_value ( ) )
return { } ;
}
2020-04-21 01:57:23 +04:30
2021-04-18 13:46:17 +04:30
// subject_unique_identifier
{
if ( ! parse_unique_identifier ( ) . has_value ( ) )
return { } ;
}
2020-04-23 02:53:11 +04:30
2021-04-18 13:46:17 +04:30
// extensions
{
if ( certificate . version = = 2 ) {
auto tag = decoder . peek ( ) ;
if ( tag . is_error ( ) ) {
dbgln_if ( TLS_DEBUG , " Certificate::TBSCertificate::*::UniqueIdentifier could not read tag: {} " , tag . error ( ) ) ;
return { } ;
2020-04-23 02:53:11 +04:30
}
2021-04-18 13:46:17 +04:30
if ( static_cast < u8 > ( tag . value ( ) . kind ) = = 3 ) {
// Extensions ::= Sequence OF Extension
// Extension ::= Sequence {
// extension_id ObjectIdentifier,
// critical Boolean DEFAULT false,
// extension_value OctetString (DER-encoded)
// }
ENTER_SCOPE_WITHOUT_TYPECHECK ( " Certificate::TBSCertificate::Extensions(IMPLICIT) " ) ;
ENTER_SCOPE_OR_FAIL ( Sequence , " Certificate::TBSCertificate::Extensions " ) ;
while ( ! decoder . eof ( ) ) {
ENTER_SCOPE_OR_FAIL ( Sequence , " Certificate::TBSCertificate::Extensions::$::Extension " ) ;
READ_OBJECT_OR_FAIL ( ObjectIdentifier , Vector < int > , extension_id , " Certificate::TBSCertificate::Extensions::$::Extension::extension_id " ) ;
bool is_critical = false ;
if ( auto tag = decoder . peek ( ) ; ! tag . is_error ( ) & & tag . value ( ) . kind = = Crypto : : ASN1 : : Kind : : Boolean ) {
// Read the 'critical' property
READ_OBJECT_OR_FAIL ( Boolean , bool , critical , " Certificate::TBSCertificate::Extensions::$::Extension::critical " ) ;
is_critical = critical ;
2020-04-21 01:57:23 +04:30
}
2021-04-18 13:46:17 +04:30
READ_OBJECT_OR_FAIL ( OctetString , StringView , extension_value , " Certificate::TBSCertificate::Extensions::$::Extension::extension_value " ) ;
// Figure out what this extension is.
if ( extension_id = = subject_alternative_name_oid ) {
Crypto : : ASN1 : : Decoder decoder { extension_value . bytes ( ) } ;
// SubjectAlternativeName ::= GeneralNames
// GeneralNames ::= Sequence OF GeneralName
// GeneralName ::= CHOICE {
// other_name (0) OtherName,
// rfc_822_name (1) IA5String,
// dns_name (2) IA5String,
// x400Address (3) ORAddress,
// directory_name (4) Name,
// edi_party_name (5) EDIPartyName,
// uri (6) IA5String,
// ip_address (7) OctetString,
// registered_id (8) ObjectIdentifier,
// }
ENTER_SCOPE_OR_FAIL ( Sequence , " Certificate::TBSCertificate::Extensions::$::Extension::extension_value::SubjectAlternativeName " ) ;
while ( ! decoder . eof ( ) ) {
auto tag = decoder . peek ( ) ;
if ( tag . is_error ( ) ) {
dbgln_if ( TLS_DEBUG , " Certificate::TBSCertificate::Extensions::$::Extension::extension_value::SubjectAlternativeName::$ could not read tag: {} " , tag . error ( ) ) ;
return { } ;
}
auto tag_value = static_cast < u8 > ( tag . value ( ) . kind ) ;
switch ( tag_value ) {
case 0 :
// OtherName
// We don't know how to use this.
DROP_OBJECT_OR_FAIL ( " Certificate::TBSCertificate::Extensions::$::Extension::extension_value::SubjectAlternativeName::$::OtherName " ) ;
break ;
case 1 :
// RFC 822 name
// We don't know how to use this.
DROP_OBJECT_OR_FAIL ( " Certificate::TBSCertificate::Extensions::$::Extension::extension_value::SubjectAlternativeName::$::RFC822Name " ) ;
break ;
case 2 : {
// DNS Name
READ_OBJECT_OR_FAIL ( IA5String , StringView , name , " Certificate::TBSCertificate::Extensions::$::Extension::extension_value::SubjectAlternativeName::$::DNSName " ) ;
certificate . SAN . append ( name ) ;
break ;
}
case 3 :
// x400Address
// We don't know how to use this.
DROP_OBJECT_OR_FAIL ( " Certificate::TBSCertificate::Extensions::$::Extension::extension_value::SubjectAlternativeName::$::X400Adress " ) ;
break ;
case 4 :
// Directory name
// We don't know how to use this.
DROP_OBJECT_OR_FAIL ( " Certificate::TBSCertificate::Extensions::$::Extension::extension_value::SubjectAlternativeName::$::DirectoryName " ) ;
break ;
case 5 :
// edi party name
// We don't know how to use this.
DROP_OBJECT_OR_FAIL ( " Certificate::TBSCertificate::Extensions::$::Extension::extension_value::SubjectAlternativeName::$::EDIPartyName " ) ;
break ;
case 6 : {
// URI
READ_OBJECT_OR_FAIL ( IA5String , StringView , name , " Certificate::TBSCertificate::Extensions::$::Extension::extension_value::SubjectAlternativeName::$::URI " ) ;
certificate . SAN . append ( name ) ;
break ;
}
case 7 :
// IP Address
// We can't handle these.
DROP_OBJECT_OR_FAIL ( " Certificate::TBSCertificate::Extensions::$::Extension::extension_value::SubjectAlternativeName::$::IPAddress " ) ;
break ;
case 8 :
// Registered ID
// We can't handle these.
DROP_OBJECT_OR_FAIL ( " Certificate::TBSCertificate::Extensions::$::Extension::extension_value::SubjectAlternativeName::$::RegisteredID " ) ;
break ;
default :
dbgln_if ( TLS_DEBUG , " Unknown tag in SAN choice {} " , tag_value ) ;
if ( is_critical )
return { } ;
else
DROP_OBJECT_OR_FAIL ( " Certificate::TBSCertificate::Extensions::$::Extension::extension_value::SubjectAlternativeName::$::??? " ) ;
}
}
2020-04-21 01:57:23 +04:30
}
2021-04-18 13:46:17 +04:30
EXIT_SCOPE ( " Certificate::TBSCertificate::Extensions::$::Extension " ) ;
2020-04-21 01:57:23 +04:30
}
2021-04-18 13:46:17 +04:30
EXIT_SCOPE ( " Certificate::TBSCertificate::Extensions " ) ;
EXIT_SCOPE ( " Certificate::TBSCertificate::Extensions(IMPLICIT) " ) ;
2020-04-21 01:57:23 +04:30
}
}
}
2021-04-18 13:46:17 +04:30
// Just ignore the rest of the data for now.
EXIT_SCOPE ( " Certificate::TBSCertificate " ) ;
EXIT_SCOPE ( " Certificate " ) ;
2020-04-21 01:57:23 +04:30
2021-04-18 13:46:17 +04:30
dbgln_if ( TLS_DEBUG , " Certificate issued for {} by {} " , certificate . subject . subject , certificate . issuer . subject ) ;
2020-04-21 01:57:23 +04:30
2021-04-18 13:46:17 +04:30
return certificate ;
2020-04-21 01:57:23 +04:30
2021-04-18 13:46:17 +04:30
# undef DROP_OBJECT_OR_FAIL
# undef ENSURE_OBJECT_KIND
# undef ENTER_SCOPE_OR_FAIL
# undef ENTER_SCOPE_WITHOUT_TYPECHECK
# undef EXIT_SCOPE
# undef READ_OBJECT_OR_FAIL
2020-04-21 01:57:23 +04:30
}
2020-12-19 15:07:09 +01:00
ssize_t TLSv12 : : handle_certificate ( ReadonlyBytes buffer )
2020-04-21 01:57:23 +04:30
{
ssize_t res = 0 ;
if ( buffer . size ( ) < 3 ) {
2021-01-23 23:29:11 +01:00
# if TLS_DEBUG
2021-01-09 18:51:44 +01:00
dbgln ( " not enough certificate header data " ) ;
2020-05-26 23:46:28 +02:00
# endif
2020-04-21 01:57:23 +04:30
return ( i8 ) Error : : NeedMoreData ;
}
u32 certificate_total_length = buffer [ 0 ] * 0x10000 + buffer [ 1 ] * 0x100 + buffer [ 2 ] ;
2021-02-07 15:33:24 +03:30
dbgln_if ( TLS_DEBUG , " total length: {} " , certificate_total_length ) ;
2020-04-21 01:57:23 +04:30
if ( certificate_total_length < = 4 )
return 3 * certificate_total_length ;
res + = 3 ;
if ( certificate_total_length > buffer . size ( ) - res ) {
2021-01-23 23:29:11 +01:00
# if TLS_DEBUG
2021-01-09 18:51:44 +01:00
dbgln ( " not enough data for claimed total cert length " ) ;
2020-05-26 23:46:28 +02:00
# endif
2020-04-21 01:57:23 +04:30
return ( i8 ) Error : : NeedMoreData ;
}
size_t size = certificate_total_length ;
size_t index = 0 ;
bool valid_certificate = false ;
while ( size > 0 ) {
+ + index ;
if ( buffer . size ( ) - res < 3 ) {
2021-01-23 23:29:11 +01:00
# if TLS_DEBUG
2021-01-09 18:51:44 +01:00
dbgln ( " not enough data for certificate length " ) ;
2020-05-26 23:46:28 +02:00
# endif
2020-04-21 01:57:23 +04:30
return ( i8 ) Error : : NeedMoreData ;
}
size_t certificate_size = buffer [ res ] * 0x10000 + buffer [ res + 1 ] * 0x100 + buffer [ res + 2 ] ;
res + = 3 ;
if ( buffer . size ( ) - res < certificate_size ) {
2021-01-23 23:29:11 +01:00
# if TLS_DEBUG
2021-01-09 18:51:44 +01:00
dbgln ( " not enough data for certificate body " ) ;
2020-05-26 23:46:28 +02:00
# endif
2020-04-21 01:57:23 +04:30
return ( i8 ) Error : : NeedMoreData ;
}
auto res_cert = res ;
auto remaining = certificate_size ;
size_t certificates_in_chain = 0 ;
do {
2020-05-06 21:43:27 +04:30
if ( remaining < = 3 ) {
2021-01-09 18:51:44 +01:00
dbgln ( " Ran out of data " ) ;
2020-04-21 01:57:23 +04:30
break ;
2020-05-06 21:43:27 +04:30
}
2020-04-21 01:57:23 +04:30
+ + certificates_in_chain ;
2020-05-06 21:43:27 +04:30
if ( buffer . size ( ) < ( size_t ) res_cert + 3 ) {
2021-01-17 16:57:17 +01:00
dbgln ( " not enough data to read cert size ({} < {}) " , buffer . size ( ) , res_cert + 3 ) ;
2020-04-21 01:57:23 +04:30
break ;
2020-05-06 21:43:27 +04:30
}
2020-04-21 01:57:23 +04:30
size_t certificate_size_specific = buffer [ res_cert ] * 0x10000 + buffer [ res_cert + 1 ] * 0x100 + buffer [ res_cert + 2 ] ;
res_cert + = 3 ;
remaining - = 3 ;
if ( certificate_size_specific > remaining ) {
2021-01-17 16:57:17 +01:00
dbgln ( " invalid certificate size (expected {} but got {}) " , remaining , certificate_size_specific ) ;
2020-04-21 01:57:23 +04:30
break ;
}
remaining - = certificate_size_specific ;
2020-12-19 15:07:09 +01:00
auto certificate = parse_asn1 ( buffer . slice ( res_cert , certificate_size_specific ) , false ) ;
2020-04-21 01:57:23 +04:30
if ( certificate . has_value ( ) ) {
2020-10-30 11:56:31 +03:30
if ( certificate . value ( ) . is_valid ( ) ) {
m_context . certificates . append ( certificate . value ( ) ) ;
valid_certificate = true ;
}
2020-04-21 01:57:23 +04:30
}
2020-05-06 21:43:27 +04:30
res_cert + = certificate_size_specific ;
2020-04-21 01:57:23 +04:30
} while ( remaining > 0 ) ;
if ( remaining ) {
2021-01-17 16:57:17 +01:00
dbgln ( " extraneous {} bytes left over after parsing certificates " , remaining ) ;
2020-04-21 01:57:23 +04:30
}
size - = certificate_size + 3 ;
res + = certificate_size ;
}
if ( ! valid_certificate )
return ( i8 ) Error : : UnsupportedCertificate ;
if ( ( size_t ) res ! = buffer . size ( ) )
2021-01-17 16:57:17 +01:00
dbgln ( " some data left unread: {} bytes out of {} " , res , buffer . size ( ) ) ;
2020-04-21 01:57:23 +04:30
return res ;
}
2020-12-19 15:07:09 +01:00
void TLSv12 : : consume ( ReadonlyBytes record )
2020-04-21 01:57:23 +04:30
{
if ( m_context . critical_error ) {
2021-01-17 16:57:17 +01:00
dbgln ( " There has been a critical error ({}), refusing to continue " , ( i8 ) m_context . critical_error ) ;
2020-04-21 01:57:23 +04:30
return ;
}
if ( record . size ( ) = = 0 ) {
return ;
}
2021-02-07 15:33:24 +03:30
dbgln_if ( TLS_DEBUG , " Consuming {} bytes " , record . size ( ) ) ;
2020-04-21 01:57:23 +04:30
m_context . message_buffer . append ( record . data ( ) , record . size ( ) ) ;
size_t index { 0 } ;
size_t buffer_length = m_context . message_buffer . size ( ) ;
size_t size_offset { 3 } ; // read the common record header
size_t header_size { 5 } ;
2021-01-17 16:57:17 +01:00
2021-02-07 15:33:24 +03:30
dbgln_if ( TLS_DEBUG , " message buffer length {} " , buffer_length ) ;
2021-01-17 16:57:17 +01:00
2020-04-21 01:57:23 +04:30
while ( buffer_length > = 5 ) {
2020-08-25 15:11:15 +02:00
auto length = AK : : convert_between_host_and_network_endian ( * ( u16 * ) m_context . message_buffer . offset_pointer ( index + size_offset ) ) + header_size ;
2020-04-21 01:57:23 +04:30
if ( length > buffer_length ) {
2021-02-07 15:33:24 +03:30
dbgln_if ( TLS_DEBUG , " Need more data: {} > {} " , length , buffer_length ) ;
2020-04-21 01:57:23 +04:30
break ;
}
2020-12-19 18:19:15 +01:00
auto consumed = handle_message ( m_context . message_buffer . bytes ( ) . slice ( index , length ) ) ;
2020-05-03 14:28:40 +02:00
2021-01-23 23:59:27 +01:00
if constexpr ( TLS_DEBUG ) {
2021-01-17 16:57:17 +01:00
if ( consumed > 0 )
dbgln ( " consumed {} bytes " , consumed ) ;
else
dbgln ( " error: {} " , consumed ) ;
}
2020-04-21 01:57:23 +04:30
if ( consumed ! = ( i8 ) Error : : NeedMoreData ) {
if ( consumed < 0 ) {
2021-01-17 16:57:17 +01:00
dbgln ( " Consumed an error: {} " , consumed ) ;
2020-04-21 01:57:23 +04:30
if ( ! m_context . critical_error )
m_context . critical_error = ( i8 ) consumed ;
m_context . error_code = ( Error ) consumed ;
break ;
}
} else {
continue ;
}
index + = length ;
buffer_length - = length ;
if ( m_context . critical_error ) {
2021-01-09 18:51:44 +01:00
dbgln ( " Broken connection " ) ;
2020-04-21 01:57:23 +04:30
m_context . error_code = Error : : BrokenConnection ;
break ;
}
}
if ( m_context . error_code ! = Error : : NoError & & m_context . error_code ! = Error : : NeedMoreData ) {
2021-01-17 16:57:17 +01:00
dbgln ( " consume error: {} " , ( i8 ) m_context . error_code ) ;
2020-04-21 01:57:23 +04:30
m_context . message_buffer . clear ( ) ;
return ;
}
if ( index ) {
m_context . message_buffer = m_context . message_buffer . slice ( index , m_context . message_buffer . size ( ) - index ) ;
}
}
2020-04-25 23:44:27 +04:30
void TLSv12 : : ensure_hmac ( size_t digest_size , bool local )
2020-04-21 01:57:23 +04:30
{
2020-04-25 23:44:27 +04:30
if ( local & & m_hmac_local )
return ;
2020-04-21 01:57:23 +04:30
2020-04-25 23:44:27 +04:30
if ( ! local & & m_hmac_remote )
return ;
2020-04-21 01:57:23 +04:30
2020-04-25 23:44:27 +04:30
auto hash_kind = Crypto : : Hash : : HashKind : : None ;
2020-04-21 01:57:23 +04:30
2020-04-25 23:44:27 +04:30
switch ( digest_size ) {
case Crypto : : Hash : : SHA1 : : DigestSize :
hash_kind = Crypto : : Hash : : HashKind : : SHA1 ;
2020-04-21 01:57:23 +04:30
break ;
2020-04-25 23:44:27 +04:30
case Crypto : : Hash : : SHA256 : : DigestSize :
hash_kind = Crypto : : Hash : : HashKind : : SHA256 ;
2020-04-21 01:57:23 +04:30
break ;
2020-04-25 23:44:27 +04:30
case Crypto : : Hash : : SHA512 : : DigestSize :
hash_kind = Crypto : : Hash : : HashKind : : SHA512 ;
2020-04-21 01:57:23 +04:30
break ;
default :
2021-01-17 16:57:17 +01:00
dbgln ( " Failed to find a suitable hash for size {} " , digest_size ) ;
2020-04-25 23:44:27 +04:30
break ;
2020-04-21 01:57:23 +04:30
}
2020-12-19 15:56:15 +01:00
auto hmac = make < Crypto : : Authentication : : HMAC < Crypto : : Hash : : Manager > > ( ReadonlyBytes { local ? m_context . crypto . local_mac : m_context . crypto . remote_mac , digest_size } , hash_kind ) ;
2020-04-25 23:44:27 +04:30
if ( local )
m_hmac_local = move ( hmac ) ;
else
m_hmac_remote = move ( hmac ) ;
2020-04-21 01:57:23 +04:30
}
2020-05-06 21:43:27 +04:30
bool Certificate : : is_valid ( ) const
{
auto now = Core : : DateTime : : now ( ) ;
2021-04-18 13:46:17 +04:30
if ( now < not_before ) {
dbgln ( " certificate expired (not yet valid, signed for {}) " , not_before . to_string ( ) ) ;
return false ;
2020-05-06 21:43:27 +04:30
}
2021-04-18 13:46:17 +04:30
if ( not_after < now ) {
dbgln ( " certificate expired (expiry date {}) " , not_after . to_string ( ) ) ;
return false ;
2020-05-06 21:43:27 +04:30
}
return true ;
}
2020-05-05 13:24:00 +04:30
void TLSv12 : : try_disambiguate_error ( ) const
{
2021-01-09 18:51:44 +01:00
dbgln ( " Possible failure cause(s): " ) ;
2020-05-05 13:24:00 +04:30
switch ( ( AlertDescription ) m_context . critical_error ) {
case AlertDescription : : HandshakeFailure :
if ( ! m_context . cipher_spec_set ) {
2021-02-07 07:19:34 +03:30
dbgln ( " - No cipher suite in common with {} " , m_context . extensions . SNI ) ;
2020-05-05 13:24:00 +04:30
} else {
2021-01-09 18:51:44 +01:00
dbgln ( " - Unknown internal issue " ) ;
2020-05-05 13:24:00 +04:30
}
break ;
case AlertDescription : : InsufficientSecurity :
2021-02-07 07:19:34 +03:30
dbgln ( " - No cipher suite in common with {} (the server is oh so secure) " , m_context . extensions . SNI ) ;
2020-05-05 13:24:00 +04:30
break ;
2020-05-26 23:27:41 +01:00
case AlertDescription : : ProtocolVersion :
2021-01-09 18:51:44 +01:00
dbgln ( " - The server refused to negotiate with TLS 1.2 :( " ) ;
2020-05-26 23:27:41 +01:00
break ;
case AlertDescription : : UnexpectedMessage :
2021-01-09 18:51:44 +01:00
dbgln ( " - We sent an invalid message for the state we're in. " ) ;
2020-05-26 23:27:41 +01:00
break ;
case AlertDescription : : BadRecordMAC :
2021-01-09 18:51:44 +01:00
dbgln ( " - Bad MAC record from our side. " ) ;
dbgln ( " - Ciphertext wasn't an even multiple of the block length. " ) ;
dbgln ( " - Bad block cipher padding. " ) ;
dbgln ( " - If both sides are compliant, the only cause is messages being corrupted in the network. " ) ;
2020-05-26 23:27:41 +01:00
break ;
case AlertDescription : : RecordOverflow :
2021-01-09 18:51:44 +01:00
dbgln ( " - Sent a ciphertext record which has a length bigger than 18432 bytes. " ) ;
dbgln ( " - Sent record decrypted to a compressed record that has a length bigger than 18432 bytes. " ) ;
dbgln ( " - If both sides are compliant, the only cause is messages being corrupted in the network. " ) ;
2020-05-26 23:27:41 +01:00
break ;
case AlertDescription : : DecompressionFailure :
2021-01-09 18:51:44 +01:00
dbgln ( " - We sent invalid input for decompression (e.g. data that would expand to excessive length) " ) ;
2020-05-26 23:27:41 +01:00
break ;
case AlertDescription : : IllegalParameter :
2021-01-09 18:51:44 +01:00
dbgln ( " - We sent a parameter in the handshake that is out of range or inconsistent with the other parameters. " ) ;
2020-05-26 23:27:41 +01:00
break ;
case AlertDescription : : DecodeError :
2021-01-09 18:51:44 +01:00
dbgln ( " - The message we sent cannot be decoded because a field was out of range or the length was incorrect. " ) ;
dbgln ( " - If both sides are compliant, the only cause is messages being corrupted in the network. " ) ;
2020-05-26 23:27:41 +01:00
break ;
case AlertDescription : : DecryptError :
2021-01-09 18:51:44 +01:00
dbgln ( " - A handshake crypto operation failed. This includes signature verification and validating Finished. " ) ;
2020-05-26 23:27:41 +01:00
break ;
case AlertDescription : : AccessDenied :
2021-01-09 18:51:44 +01:00
dbgln ( " - The certificate is valid, but once access control was applied, the sender decided to stop negotiation. " ) ;
2020-05-26 23:27:41 +01:00
break ;
case AlertDescription : : InternalError :
2021-01-09 18:51:44 +01:00
dbgln ( " - No one knows, but it isn't a protocol failure. " ) ;
2020-05-26 23:27:41 +01:00
break ;
2020-05-05 13:24:00 +04:30
case AlertDescription : : DecryptionFailed :
2020-05-26 23:27:41 +01:00
case AlertDescription : : NoCertificate :
case AlertDescription : : ExportRestriction :
2021-01-09 18:51:44 +01:00
dbgln ( " - No one knows, the server sent a non-compliant alert. " ) ;
2020-05-05 13:24:00 +04:30
break ;
default :
2021-01-09 18:51:44 +01:00
dbgln ( " - No one knows. " ) ;
2020-05-05 13:24:00 +04:30
break ;
}
}
2020-05-18 20:16:52 +02:00
2020-10-30 11:56:31 +03:30
void TLSv12 : : set_root_certificates ( Vector < Certificate > certificates )
{
if ( ! m_context . root_ceritificates . is_empty ( ) )
2021-01-09 18:51:44 +01:00
dbgln ( " TLS warn: resetting root certificates! " ) ;
2020-10-30 11:56:31 +03:30
for ( auto & cert : certificates ) {
if ( ! cert . is_valid ( ) )
2021-04-18 13:46:17 +04:30
dbgln ( " Certificate for {} by {} is invalid, things may or may not work! " , cert . subject . subject , cert . issuer . subject ) ;
2020-10-30 11:56:31 +03:30
// FIXME: Figure out what we should do when our root certs are invalid.
}
m_context . root_ceritificates = move ( certificates ) ;
}
bool Context : : verify_chain ( ) const
{
2021-02-07 07:21:32 +03:30
if ( ! options . validate_certificates )
return true ;
2020-10-30 11:56:31 +03:30
const Vector < Certificate > * local_chain = nullptr ;
if ( is_server ) {
2021-01-09 18:51:44 +01:00
dbgln ( " Unsupported: Server mode " ) ;
2020-10-30 11:56:31 +03:30
TODO ( ) ;
} else {
local_chain = & certificates ;
}
// FIXME: Actually verify the signature, instead of just checking the name.
HashMap < String , String > chain ;
HashTable < String > roots ;
// First, walk the root certs.
for ( auto & cert : root_ceritificates ) {
2021-04-18 13:46:17 +04:30
roots . set ( cert . subject . subject ) ;
chain . set ( cert . subject . subject , cert . issuer . subject ) ;
2020-10-30 11:56:31 +03:30
}
// Then, walk the local certs.
for ( auto & cert : * local_chain ) {
2021-04-18 13:46:17 +04:30
auto & issuer_unique_name = cert . issuer . unit . is_empty ( ) ? cert . issuer . subject : cert . issuer . unit ;
chain . set ( cert . subject . subject , issuer_unique_name ) ;
2020-10-30 11:56:31 +03:30
}
// Then verify the chain.
for ( auto & it : chain ) {
if ( it . key = = it . value ) { // Allow self-signed certificates.
if ( ! roots . contains ( it . key ) )
2021-01-17 16:57:17 +01:00
dbgln ( " Self-signed warning: Certificate for {} is self-signed " , it . key ) ;
2020-10-30 11:56:31 +03:30
continue ;
}
auto ref = chain . get ( it . value ) ;
if ( ! ref . has_value ( ) ) {
2021-01-17 16:57:17 +01:00
dbgln ( " Certificate for {} is not signed by anyone we trust ({}) " , it . key , it . value ) ;
2020-10-30 11:56:31 +03:30
return false ;
}
if ( ref . value ( ) = = it . key ) // Allow (but warn about) mutually recursively signed cert A <-> B.
2021-01-17 16:57:17 +01:00
dbgln ( " Co-dependency warning: Certificate for {} is issued by {}, which itself is issued by {} " , ref . value ( ) , it . key , ref . value ( ) ) ;
2020-10-30 11:56:31 +03:30
}
return true ;
}
static bool wildcard_matches ( const StringView & host , const StringView & subject )
{
if ( host . matches ( subject ) )
return true ;
if ( subject . starts_with ( " *. " ) )
return wildcard_matches ( host , subject . substring_view ( 2 ) ) ;
return false ;
}
Optional < size_t > TLSv12 : : verify_chain_and_get_matching_certificate ( const StringView & host ) const
{
if ( m_context . certificates . is_empty ( ) | | ! m_context . verify_chain ( ) )
return { } ;
if ( host . is_empty ( ) )
return 0 ;
for ( size_t i = 0 ; i < m_context . certificates . size ( ) ; + + i ) {
auto & cert = m_context . certificates [ i ] ;
2021-04-18 13:46:17 +04:30
if ( wildcard_matches ( host , cert . subject . subject ) )
2020-10-30 11:56:31 +03:30
return i ;
2020-12-13 22:25:09 +03:30
for ( auto & san : cert . SAN ) {
if ( wildcard_matches ( host , san ) )
return i ;
}
2020-10-30 11:56:31 +03:30
}
return { } ;
}
2021-02-07 07:21:32 +03:30
TLSv12 : : TLSv12 ( Core : : Object * parent , Options options )
2020-05-18 20:16:52 +02:00
: Core : : Socket ( Core : : Socket : : Type : : TCP , parent )
{
2021-02-07 07:21:32 +03:30
m_context . options = move ( options ) ;
2020-05-18 20:16:52 +02:00
m_context . is_server = false ;
m_context . tls_buffer = ByteBuffer : : create_uninitialized ( 0 ) ;
2020-05-29 22:22:01 +02:00
# ifdef SOCK_NONBLOCK
2020-05-18 20:16:52 +02:00
int fd = socket ( AF_INET , SOCK_STREAM | SOCK_NONBLOCK , 0 ) ;
2020-05-29 22:22:01 +02:00
# else
int fd = socket ( AF_INET , SOCK_STREAM , 0 ) ;
int option = 1 ;
ioctl ( fd , FIONBIO , & option ) ;
# endif
2020-05-18 20:16:52 +02:00
if ( fd < 0 ) {
set_error ( errno ) ;
} else {
set_fd ( fd ) ;
set_mode ( IODevice : : ReadWrite ) ;
set_error ( 0 ) ;
}
}
2020-12-19 15:07:09 +01:00
bool TLSv12 : : add_client_key ( ReadonlyBytes certificate_pem_buffer , ReadonlyBytes rsa_key ) // FIXME: This should not be bound to RSA
2020-08-02 05:27:42 +04:30
{
if ( certificate_pem_buffer . is_empty ( ) | | rsa_key . is_empty ( ) ) {
return true ;
}
2021-02-14 14:50:42 +03:30
auto decoded_certificate = Crypto : : decode_pem ( certificate_pem_buffer ) ;
2020-08-02 05:27:42 +04:30
if ( decoded_certificate . is_empty ( ) ) {
2021-01-09 18:51:44 +01:00
dbgln ( " Certificate not PEM " ) ;
2020-08-02 05:27:42 +04:30
return false ;
}
auto maybe_certificate = parse_asn1 ( decoded_certificate ) ;
if ( ! maybe_certificate . has_value ( ) ) {
2021-01-09 18:51:44 +01:00
dbgln ( " Invalid certificate " ) ;
2020-08-02 05:27:42 +04:30
return false ;
}
Crypto : : PK : : RSA rsa ( rsa_key ) ;
auto certificate = maybe_certificate . value ( ) ;
certificate . private_key = rsa . private_key ( ) ;
return add_client_key ( certificate ) ;
}
2020-10-30 11:56:31 +03:30
AK : : Singleton < DefaultRootCACertificates > DefaultRootCACertificates : : s_the ;
DefaultRootCACertificates : : DefaultRootCACertificates ( )
{
// FIXME: This might not be the best format, find a better way to represent CA certificates.
auto config = Core : : ConfigFile : : get_for_system ( " ca_certs " ) ;
2021-04-18 13:46:17 +04:30
auto now = Core : : DateTime : : now ( ) ;
auto last_year = Core : : DateTime : : create ( now . year ( ) - 1 ) ;
auto next_year = Core : : DateTime : : create ( now . year ( ) + 1 ) ;
2020-10-30 11:56:31 +03:30
for ( auto & entity : config - > groups ( ) ) {
Certificate cert ;
2021-04-18 13:46:17 +04:30
cert . subject . subject = entity ;
cert . issuer . subject = config - > read_entry ( entity , " issuer_subject " , entity ) ;
cert . subject . country = config - > read_entry ( entity , " country " ) ;
cert . not_before = Crypto : : ASN1 : : parse_generalized_time ( config - > read_entry ( entity , " not_before " , " " ) ) . value_or ( last_year ) ;
cert . not_after = Crypto : : ASN1 : : parse_generalized_time ( config - > read_entry ( entity , " not_after " , " " ) ) . value_or ( next_year ) ;
2020-10-30 11:56:31 +03:30
m_ca_certificates . append ( move ( cert ) ) ;
}
}
2020-04-21 01:57:23 +04:30
}