Skip to content

Commit

Permalink
Root certs on demand (#1672)
Browse files Browse the repository at this point in the history
  • Loading branch information
Erik Corry authored Jun 28, 2023
1 parent 805f8c7 commit 8f76fd8
Show file tree
Hide file tree
Showing 21 changed files with 1,052 additions and 100 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ else()
# pressure, but means the TLS connection often breaks after a while when the
# counterpart makes the packets larger. This ...IN_CONTENT_LEN define is
# overriddden in sdkconfig files for devices.
set(MBEDTLS_C_FLAGS "-DMBEDTLS_SSL_IN_CONTENT_LEN=7800 -DMBEDTLS_SSL_OUT_CONTENT_LEN=3700 -DMBEDTLS_PLATFORM_MEMORY=1")
set(MBEDTLS_C_FLAGS "-DMBEDTLS_SSL_IN_CONTENT_LEN=7800 -DMBEDTLS_SSL_OUT_CONTENT_LEN=3700 -DMBEDTLS_PLATFORM_MEMORY=1 -DMBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK=1")
endif()

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${TOIT_GENERIC_FLAGS} ${TOIT_LWIP_C_FLAGS} ${MBEDTLS_C_FLAGS}")
Expand Down
27 changes: 27 additions & 0 deletions lib/tls/certificate.toit
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,30 @@ class Certificate:
An optional password can be given, to unlock the private key if needed.
*/
constructor .certificate .private_key --.password="":

/**
Add a trusted root certificate that can be used for all TLS connections.
This function is an alternative to adding root certificates to individual TLS
sockets, or using the --root_certificates argument on the HTTP client.
If you add root certificates to a specific connection then these global
certificates are not consulted for that connection, not even as a fallback.
The trusted roots added with this function have a "subject" field that is used
to match with the "issuer" field that the server provides. Only matching
roots are tried when attempting to verify a server certificate. The
"AuthorityKeyIdentifier" and "SubjectKeyIdentifer" extensions are not
supported.
Certificates, the $cert argument, are added here in unparsed form, ie either in
PEM (ASCII) format or in DER format. Usually you would use a byte array
constant in DER format, which will stay in flash on embedded platforms, using
very little memory until it is needed to complete a TLS handshake. Trying to
add an instance of $Certificate or $x509.Certificate with this function will
throw an error.
Returns the hash of the added certificate, which can be used to add it more
efficiently, without parsing the certificate at startup time.
*/
add_global_root_certificate cert hash/int?=null -> int:
#primitive.tls.add_global_root_certificate
4 changes: 2 additions & 2 deletions lib/tls/session.toit
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ class Session:
else if state == TOIT_TLS_WANT_WRITE_:
// This is already handled above with flush_outgoing_
else:
tls_error_ tls_state.group state
tls_error_ tls_ state

extract_key_data_ -> none:
if reads_encrypted_ and writes_encrypted_:
Expand Down Expand Up @@ -1126,7 +1126,7 @@ tls_create_ group hostname:
tls_add_root_certificate_ group cert:
#primitive.tls.add_root_certificate

tls_error_ group error:
tls_error_ socket error:
#primitive.tls.error

tls_init_socket_ tls_socket transport_id:
Expand Down
1 change: 1 addition & 0 deletions src/compiler/propagation/type_primitive_tls.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ TYPE_PRIMITIVE_ANY(close_write)
TYPE_PRIMITIVE_ANY(read)
TYPE_PRIMITIVE_ANY(write)
TYPE_PRIMITIVE_ANY(add_root_certificate)
TYPE_PRIMITIVE_INT(add_global_root_certificate)
TYPE_PRIMITIVE_ANY(add_certificate)
TYPE_PRIMITIVE_ANY(error)
TYPE_PRIMITIVE_ANY(get_internals)
Expand Down
2 changes: 1 addition & 1 deletion src/objects.h
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ class HeapObject : public Object {
return reinterpret_cast<HeapObject*>(address + HEAP_TAG);
}

inline bool on_program_heap(Process* process);
inline bool on_program_heap(Process* process) const;

static int allocation_size() { return _align(SIZE); }
static void allocation_size(int* word_count, int* extra_bytes) {
Expand Down
2 changes: 1 addition & 1 deletion src/objects_inline.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ T* ByteArray::as_external() {
return 0;
}

inline bool HeapObject::on_program_heap(Process* process) {
inline bool HeapObject::on_program_heap(Process* process) const {
return process->on_program_heap(this);
}

Expand Down
20 changes: 12 additions & 8 deletions src/primitive.h
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ namespace toit {
PRIMITIVE(read, 1) \
PRIMITIVE(write, 4) \
PRIMITIVE(add_root_certificate, 2) \
PRIMITIVE(add_global_root_certificate, 2) \
PRIMITIVE(add_certificate, 4) \
PRIMITIVE(error, 2) \
PRIMITIVE(get_internals, 1) \
Expand Down Expand Up @@ -791,17 +792,20 @@ namespace toit {
if (_value_##name < INT32_MIN || _value_##name > INT32_MAX) FAIL(OUT_OF_RANGE); \
int32 name = (int32) _value_##name;

#define _A_T_uint32(N, name) \
Object* _raw_##name = __args[-(N)]; \
int64 _value_##name; \
if (is_smi(_raw_##name)) { \
_value_##name = Smi::value(_raw_##name); \
} else if (is_large_integer(_raw_##name)) { \
_value_##name = LargeInteger::cast(_raw_##name)->value(); \
#define GET_UINT32(raw, result) \
int64 result; \
if (is_smi(raw)) { \
result = Smi::value(raw); \
} else if (is_large_integer(raw)) { \
result = LargeInteger::cast(raw)->value(); \
} else { \
FAIL(WRONG_OBJECT_TYPE); \
} \
if (_value_##name < 0 || _value_##name > UINT32_MAX) FAIL(OUT_OF_RANGE); \
if (result < 0 || result > UINT32_MAX) FAIL(OUT_OF_RANGE);

#define _A_T_uint32(N, name) \
Object* _raw_##name = __args[-(N)]; \
GET_UINT32(_raw_##name, _value_##name); \
uint32 name = (uint32) _value_##name;

#define INT64_VALUE_OR_WRONG_TYPE(destination, raw) \
Expand Down
2 changes: 1 addition & 1 deletion src/primitive_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ int AeadContext::finish(uint8* output_data, int size) {
class MbedTlsResourceGroup;

// From resources/tls.cc.
extern Object* tls_error(MbedTlsResourceGroup* group, Process* process, int err);
extern Object* tls_error(BaseMbedTlsSocket* group, Process* process, int err);

AeadContext::~AeadContext(){
switch (cipher_id_) {
Expand Down
25 changes: 25 additions & 0 deletions src/process.cc
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ Process::~Process() {
while (has_messages()) {
remove_first_message();
}

Locker locker(OS::scheduler_mutex());
while (auto certificate = root_certificates_.remove_first()) {
delete certificate;
}
}

void Process::set_main_arguments(uint8* arguments) {
Expand Down Expand Up @@ -407,4 +412,24 @@ String* Process::allocate_string(const wchar_t* content) {

#endif

bool Process::already_has_root_certificate(const uint8* data, size_t length, const Locker& locker) {
for (auto root : root_certificates_) {
if (root->matches(data, length)) return true;
}
return false;
}

UnparsedRootCertificate::UnparsedRootCertificate(const uint8* data, size_t length, bool needs_delete)
: data_(data), length_(length), needs_delete_(needs_delete) {}

UnparsedRootCertificate::~UnparsedRootCertificate() {
if (needs_delete_) delete(data_);
data_ = null;
}

bool UnparsedRootCertificate::matches(const uint8* data, size_t length) const {
if (length != length_) return false;
return memcmp(data, data_, length) == 0;
}

}
37 changes: 36 additions & 1 deletion src/process.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,28 @@

namespace toit {

class UnparsedRootCertificate;
typedef LinkedFifo<UnparsedRootCertificate> UnparsedRootCertificateList;

class UnparsedRootCertificate: public UnparsedRootCertificateList::Element {
public:
UnparsedRootCertificate(const uint8* data, size_t length, bool needs_delete);
~UnparsedRootCertificate();
bool matches(const uint8* data, size_t length) const;

const uint8* data() const { return data_; }
size_t length() const { return length_; }

void set_subject_hash(uint32 hash) { subject_hash_ = hash; }
uint32 subject_hash() const { return subject_hash_; }

private:
const uint8* data_;
size_t length_;
uint32 subject_hash_ = 0;
bool needs_delete_;
};

// Process is linked into two different linked lists, so we have to make
// use of the arbitrary N template argument to distinguish the two.
typedef LinkedList<Process, 1> ProcessListFromProcessGroup;
Expand Down Expand Up @@ -233,7 +255,7 @@ class Process : public ProcessListFromProcessGroup::Element,
delete p;
}

inline bool on_program_heap(HeapObject* object) {
inline bool on_program_heap(const HeapObject* object) {
uword address = reinterpret_cast<uword>(object);
return address - program_heap_address_ < program_heap_size_;
}
Expand All @@ -242,6 +264,17 @@ class Process : public ProcessListFromProcessGroup::Element,
inline HeapObject* true_object() const { return true_object_; }
inline HeapObject* null_object() const { return null_; }

// These root certificate functions should be guarded by the scheduler mutex.
void add_root_certificate(UnparsedRootCertificate* certificate, const Locker& locker) {
root_certificates_.append(certificate);
}

bool already_has_root_certificate(const uint8* data, size_t length, const Locker& locker);

UnparsedRootCertificateList& root_certificates(const Locker& locker) {
return root_certificates_;
}

private:
Process(Program* program, ProcessRunner* runner, ProcessGroup* group, SystemMessage* termination, InitialMemoryManager* initial_memory);
void _append_message(Message* message);
Expand Down Expand Up @@ -292,6 +325,8 @@ class Process : public ProcessListFromProcessGroup::Element,
bool construction_failed_ = false;
bool idle_since_gc_ = true;

UnparsedRootCertificateList root_certificates_;

Profiler* profiler_ = null;

HeapObject* false_object_;
Expand Down
Loading

0 comments on commit 8f76fd8

Please sign in to comment.