diff options
Diffstat (limited to 'packages/bun-usockets/src')
-rw-r--r-- | packages/bun-usockets/src/bsd.c | 738 | ||||
-rw-r--r-- | packages/bun-usockets/src/context.c | 575 | ||||
-rw-r--r-- | packages/bun-usockets/src/crypto/openssl.c | 1732 | ||||
-rw-r--r-- | packages/bun-usockets/src/crypto/root_certs.h | 3438 | ||||
-rw-r--r-- | packages/bun-usockets/src/crypto/sni_tree.cpp | 218 | ||||
-rw-r--r-- | packages/bun-usockets/src/eventing/epoll_kqueue.c | 568 | ||||
-rw-r--r-- | packages/bun-usockets/src/internal/eventing/epoll_kqueue.h | 72 | ||||
-rw-r--r-- | packages/bun-usockets/src/internal/internal.h | 245 | ||||
-rw-r--r-- | packages/bun-usockets/src/internal/loop_data.h | 38 | ||||
-rw-r--r-- | packages/bun-usockets/src/internal/networking/bsd.h | 108 | ||||
-rw-r--r-- | packages/bun-usockets/src/libusockets.h | 408 | ||||
-rw-r--r-- | packages/bun-usockets/src/loop.c | 364 | ||||
-rw-r--r-- | packages/bun-usockets/src/quic.c | 1071 | ||||
-rw-r--r-- | packages/bun-usockets/src/quic.h | 68 | ||||
-rw-r--r-- | packages/bun-usockets/src/socket.c | 295 | ||||
-rw-r--r-- | packages/bun-usockets/src/udp.c | 147 |
16 files changed, 10085 insertions, 0 deletions
diff --git a/packages/bun-usockets/src/bsd.c b/packages/bun-usockets/src/bsd.c new file mode 100644 index 000000000..7683acd7d --- /dev/null +++ b/packages/bun-usockets/src/bsd.c @@ -0,0 +1,738 @@ +/* + * Authored by Alex Hultman, 2018-2021. + * Intellectual property of third-party. + + * 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 + + * http://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. + */ + +/* Todo: this file should lie in networking/bsd.c */ + +#define __APPLE_USE_RFC_3542 + +#include "libusockets.h" +#include "internal/internal.h" + +#include <stdio.h> +#include <stdlib.h> + +#ifndef _WIN32 +//#define _GNU_SOURCE +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#endif + +/* Internal structure of packet buffer */ +struct us_internal_udp_packet_buffer { +#if defined(_WIN32) || defined(__APPLE__) + char *buf[LIBUS_UDP_MAX_NUM]; + size_t len[LIBUS_UDP_MAX_NUM]; + struct sockaddr_storage addr[LIBUS_UDP_MAX_NUM]; +#else + struct mmsghdr msgvec[LIBUS_UDP_MAX_NUM]; + struct iovec iov[LIBUS_UDP_MAX_NUM]; + struct sockaddr_storage addr[LIBUS_UDP_MAX_NUM]; + char control[LIBUS_UDP_MAX_NUM][256]; +#endif +}; + +/* We need to emulate sendmmsg, recvmmsg on platform who don't have it */ +int bsd_sendmmsg(LIBUS_SOCKET_DESCRIPTOR fd, void *msgvec, unsigned int vlen, int flags) { +#if defined(__APPLE__) + +struct mmsghdr { + struct msghdr msg_hdr; /* Message header */ + unsigned int msg_len; /* Number of bytes transmitted */ +}; + + struct mmsghdr *hdrs = (struct mmsghdr *) msgvec; + + for (int i = 0; i < vlen; i++) { + int ret = sendmsg(fd, &hdrs[i].msg_hdr, flags); + if (ret == -1) { + if (i) { + return i; + } else { + return -1; + } + } else { + hdrs[i].msg_len = ret; + } + } + + return vlen; + +#elif defined(_WIN32) + + struct us_internal_udp_packet_buffer *packet_buffer = (struct us_internal_udp_packet_buffer *) msgvec; + + /* Let's just use sendto here */ + /* Winsock does not have sendmsg, while macOS has, however, we simply use sendto since both macOS and Winsock has it. + * Besides, you should use Linux either way to get best performance with the sendmmsg */ + + + // while we do not get error, send next + + for (int i = 0; i < LIBUS_UDP_MAX_NUM; i++) { + // need to support ipv6 addresses also! + int ret = sendto(fd, packet_buffer->buf[i], packet_buffer->len[i], flags, (struct sockaddr *)&packet_buffer->addr[i], sizeof(struct sockaddr_in)); + + if (ret == -1) { + // if we fail then we need to buffer up, no that's not our problem + // we do need to register poll out though and have a callback for it + return i; + } + + //printf("sendto: %d\n", ret); + } + + return LIBUS_UDP_MAX_NUM; // one message +#else + return sendmmsg(fd, (struct mmsghdr *)msgvec, vlen, flags | MSG_NOSIGNAL); +#endif +} + +int bsd_recvmmsg(LIBUS_SOCKET_DESCRIPTOR fd, void *msgvec, unsigned int vlen, int flags, void *timeout) { +#if defined(_WIN32) || defined(__APPLE__) + struct us_internal_udp_packet_buffer *packet_buffer = (struct us_internal_udp_packet_buffer *) msgvec; + + + for (int i = 0; i < LIBUS_UDP_MAX_NUM; i++) { + socklen_t addr_len = sizeof(struct sockaddr_storage); + int ret = recvfrom(fd, packet_buffer->buf[i], LIBUS_UDP_MAX_SIZE, flags, (struct sockaddr *)&packet_buffer->addr[i], &addr_len); + + if (ret == -1) { + return i; + } + + packet_buffer->len[i] = ret; + } + + return LIBUS_UDP_MAX_NUM; +#else + // we need to set controllen for ip packet + for (int i = 0; i < vlen; i++) { + ((struct mmsghdr *)msgvec)[i].msg_hdr.msg_controllen = 256; + } + + return recvmmsg(fd, (struct mmsghdr *)msgvec, vlen, flags, 0); +#endif +} + +// this one is needed for knowing the destination addr of udp packet +// an udp socket can only bind to one port, and that port never changes +// this function returns ONLY the IP address, not any port +int bsd_udp_packet_buffer_local_ip(void *msgvec, int index, char *ip) { +#if defined(_WIN32) || defined(__APPLE__) + return 0; // not supported +#else + struct msghdr *mh = &((struct mmsghdr *) msgvec)[index].msg_hdr; + for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL; cmsg = CMSG_NXTHDR(mh, cmsg)) { + // ipv6 or ipv4 + if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { + struct in_pktinfo *pi = (struct in_pktinfo *) CMSG_DATA(cmsg); + memcpy(ip, &pi->ipi_addr, 4); + return 4; + } + + if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { + struct in6_pktinfo *pi6 = (struct in6_pktinfo *) CMSG_DATA(cmsg); + memcpy(ip, &pi6->ipi6_addr, 16); + return 16; + } + } + + return 0; // no length + +#endif +} + +char *bsd_udp_packet_buffer_peer(void *msgvec, int index) { +#if defined(_WIN32) || defined(__APPLE__) + struct us_internal_udp_packet_buffer *packet_buffer = (struct us_internal_udp_packet_buffer *) msgvec; + return (char *)&packet_buffer->addr[index]; +#else + return ((struct mmsghdr *) msgvec)[index].msg_hdr.msg_name; +#endif +} + +char *bsd_udp_packet_buffer_payload(void *msgvec, int index) { +#if defined(_WIN32) || defined(__APPLE__) + struct us_internal_udp_packet_buffer *packet_buffer = (struct us_internal_udp_packet_buffer *) msgvec; + return packet_buffer->buf[index]; +#else + return ((struct mmsghdr *) msgvec)[index].msg_hdr.msg_iov[0].iov_base; +#endif +} + +int bsd_udp_packet_buffer_payload_length(void *msgvec, int index) { +#if defined(_WIN32) || defined(__APPLE__) + struct us_internal_udp_packet_buffer *packet_buffer = (struct us_internal_udp_packet_buffer *) msgvec; + return packet_buffer->len[index]; +#else + return ((struct mmsghdr *) msgvec)[index].msg_len; +#endif +} + +void bsd_udp_buffer_set_packet_payload(struct us_udp_packet_buffer_t *send_buf, int index, int offset, void *payload, int length, void *peer_addr) { +#if defined(_WIN32) || defined(__APPLE__) + struct us_internal_udp_packet_buffer *packet_buffer = (struct us_internal_udp_packet_buffer *) send_buf; + + memcpy(packet_buffer->buf[index], payload, length); + memcpy(&packet_buffer->addr[index], peer_addr, sizeof(struct sockaddr_storage)); + + packet_buffer->len[index] = length; +#else + //printf("length: %d, offset: %d\n", length, offset); + + struct mmsghdr *ss = (struct mmsghdr *) send_buf; + + // copy the peer address + memcpy(ss[index].msg_hdr.msg_name, peer_addr, /*ss[index].msg_hdr.msg_namelen*/ sizeof(struct sockaddr_in)); + + // set control length to 0 + ss[index].msg_hdr.msg_controllen = 0; + + // copy the payload + + ss[index].msg_hdr.msg_iov->iov_len = length + offset; + + + memcpy(((char *) ss[index].msg_hdr.msg_iov->iov_base) + offset, payload, length); +#endif +} + +/* The maximum UDP payload size is 64kb, but in IPV6 you can have jumbopackets larger than so. + * We do not support those jumbo packets currently, but will safely ignore them. + * Any sane sender would assume we don't support them if we consistently drop them. + * Therefore a udp_packet_buffer_t will be 64 MB in size (64kb * 1024). */ +void *bsd_create_udp_packet_buffer() { +#if defined(_WIN32) || defined(__APPLE__) + struct us_internal_udp_packet_buffer *b = malloc(sizeof(struct us_internal_udp_packet_buffer) + LIBUS_UDP_MAX_SIZE * LIBUS_UDP_MAX_NUM); + + for (int i = 0; i < LIBUS_UDP_MAX_NUM; i++) { + b->buf[i] = ((char *) b) + sizeof(struct us_internal_udp_packet_buffer) + LIBUS_UDP_MAX_SIZE * i; + } + + return (struct us_udp_packet_buffer_t *) b; +#else + /* Allocate 64kb times 1024 */ + struct us_internal_udp_packet_buffer *b = malloc(sizeof(struct us_internal_udp_packet_buffer) + LIBUS_UDP_MAX_SIZE * LIBUS_UDP_MAX_NUM); + + for (int n = 0; n < LIBUS_UDP_MAX_NUM; ++n) { + + b->iov[n].iov_base = &((char *) (b + 1))[n * LIBUS_UDP_MAX_SIZE]; + b->iov[n].iov_len = LIBUS_UDP_MAX_SIZE; + + b->msgvec[n].msg_hdr = (struct msghdr) { + .msg_name = &b->addr, + .msg_namelen = sizeof (struct sockaddr_storage), + + .msg_iov = &b->iov[n], + .msg_iovlen = 1, + + .msg_control = b->control[n], + .msg_controllen = 256, + }; + } + + return (struct us_udp_packet_buffer_t *) b; +#endif +} + +LIBUS_SOCKET_DESCRIPTOR apple_no_sigpipe(LIBUS_SOCKET_DESCRIPTOR fd) { +#ifdef __APPLE__ + if (fd != LIBUS_SOCKET_ERROR) { + int no_sigpipe = 1; + setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *) &no_sigpipe, sizeof(int)); + } +#endif + return fd; +} + +LIBUS_SOCKET_DESCRIPTOR bsd_set_nonblocking(LIBUS_SOCKET_DESCRIPTOR fd) { +#ifdef _WIN32 + /* Libuv will set windows sockets as non-blocking */ +#else + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); +#endif + return fd; +} + +void bsd_socket_nodelay(LIBUS_SOCKET_DESCRIPTOR fd, int enabled) { + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *) &enabled, sizeof(enabled)); +} + +void bsd_socket_flush(LIBUS_SOCKET_DESCRIPTOR fd) { + // Linux TCP_CORK has the same underlying corking mechanism as with MSG_MORE +#ifdef TCP_CORK + int enabled = 0; + setsockopt(fd, IPPROTO_TCP, TCP_CORK, (void *) &enabled, sizeof(int)); +#endif +} + +LIBUS_SOCKET_DESCRIPTOR bsd_create_socket(int domain, int type, int protocol) { + // returns INVALID_SOCKET on error + int flags = 0; +#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) + flags = SOCK_CLOEXEC | SOCK_NONBLOCK; +#endif + + LIBUS_SOCKET_DESCRIPTOR created_fd = socket(domain, type | flags, protocol); + + return bsd_set_nonblocking(apple_no_sigpipe(created_fd)); +} + +void bsd_close_socket(LIBUS_SOCKET_DESCRIPTOR fd) { +#ifdef _WIN32 + closesocket(fd); +#else + close(fd); +#endif +} + +void bsd_shutdown_socket(LIBUS_SOCKET_DESCRIPTOR fd) { +#ifdef _WIN32 + shutdown(fd, SD_SEND); +#else + shutdown(fd, SHUT_WR); +#endif +} + +void bsd_shutdown_socket_read(LIBUS_SOCKET_DESCRIPTOR fd) { +#ifdef _WIN32 + shutdown(fd, SD_RECEIVE); +#else + shutdown(fd, SHUT_RD); +#endif +} + +void internal_finalize_bsd_addr(struct bsd_addr_t *addr) { + // parse, so to speak, the address + if (addr->mem.ss_family == AF_INET6) { + addr->ip = (char *) &((struct sockaddr_in6 *) addr)->sin6_addr; + addr->ip_length = sizeof(struct in6_addr); + addr->port = ntohs(((struct sockaddr_in6 *) addr)->sin6_port); + } else if (addr->mem.ss_family == AF_INET) { + addr->ip = (char *) &((struct sockaddr_in *) addr)->sin_addr; + addr->ip_length = sizeof(struct in_addr); + addr->port = ntohs(((struct sockaddr_in *) addr)->sin_port); + } else { + addr->ip_length = 0; + addr->port = -1; + } +} + +int bsd_local_addr(LIBUS_SOCKET_DESCRIPTOR fd, struct bsd_addr_t *addr) { + addr->len = sizeof(addr->mem); + if (getsockname(fd, (struct sockaddr *) &addr->mem, &addr->len)) { + return -1; + } + internal_finalize_bsd_addr(addr); + return 0; +} + +int bsd_remote_addr(LIBUS_SOCKET_DESCRIPTOR fd, struct bsd_addr_t *addr) { + addr->len = sizeof(addr->mem); + if (getpeername(fd, (struct sockaddr *) &addr->mem, &addr->len)) { + return -1; + } + internal_finalize_bsd_addr(addr); + return 0; +} + +char *bsd_addr_get_ip(struct bsd_addr_t *addr) { + return addr->ip; +} + +int bsd_addr_get_ip_length(struct bsd_addr_t *addr) { + return addr->ip_length; +} + +int bsd_addr_get_port(struct bsd_addr_t *addr) { + return addr->port; +} + +// called by dispatch_ready_poll +LIBUS_SOCKET_DESCRIPTOR bsd_accept_socket(LIBUS_SOCKET_DESCRIPTOR fd, struct bsd_addr_t *addr) { + LIBUS_SOCKET_DESCRIPTOR accepted_fd; + addr->len = sizeof(addr->mem); + +#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) + // Linux, FreeBSD + accepted_fd = accept4(fd, (struct sockaddr *) addr, &addr->len, SOCK_CLOEXEC | SOCK_NONBLOCK); +#else + // Windows, OS X + accepted_fd = accept(fd, (struct sockaddr *) addr, &addr->len); + +#endif + + /* We cannot rely on addr since it is not initialized if failed */ + if (accepted_fd == LIBUS_SOCKET_ERROR) { + return LIBUS_SOCKET_ERROR; + } + + internal_finalize_bsd_addr(addr); + + return bsd_set_nonblocking(apple_no_sigpipe(accepted_fd)); +} + +int bsd_recv(LIBUS_SOCKET_DESCRIPTOR fd, void *buf, int length, int flags) { + return recv(fd, buf, length, flags); +} + +int bsd_send(LIBUS_SOCKET_DESCRIPTOR fd, const char *buf, int length, int msg_more) { + + // MSG_MORE (Linux), MSG_PARTIAL (Windows), TCP_NOPUSH (BSD) + +#ifndef MSG_NOSIGNAL +#define MSG_NOSIGNAL 0 +#endif + +#ifdef MSG_MORE + + // for Linux we do not want signals + return send(fd, buf, length, ((msg_more != 0) * MSG_MORE) | MSG_NOSIGNAL); + +#else + + // use TCP_NOPUSH + + return send(fd, buf, length, MSG_NOSIGNAL); + +#endif +} + +int bsd_would_block() { +#ifdef _WIN32 + return WSAGetLastError() == WSAEWOULDBLOCK; +#else + return errno == EWOULDBLOCK;// || errno == EAGAIN; +#endif +} + +// return LIBUS_SOCKET_ERROR or the fd that represents listen socket +// listen both on ipv6 and ipv4 +LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket(const char *host, int port, int options) { + struct addrinfo hints, *result; + memset(&hints, 0, sizeof(struct addrinfo)); + + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + char port_string[16]; + snprintf(port_string, 16, "%d", port); + + if (getaddrinfo(host, port_string, &hints, &result)) { + return LIBUS_SOCKET_ERROR; + } + + LIBUS_SOCKET_DESCRIPTOR listenFd = LIBUS_SOCKET_ERROR; + struct addrinfo *listenAddr; + for (struct addrinfo *a = result; a && listenFd == LIBUS_SOCKET_ERROR; a = a->ai_next) { + if (a->ai_family == AF_INET6) { + listenFd = bsd_create_socket(a->ai_family, a->ai_socktype, a->ai_protocol); + listenAddr = a; + } + } + + for (struct addrinfo *a = result; a && listenFd == LIBUS_SOCKET_ERROR; a = a->ai_next) { + if (a->ai_family == AF_INET) { + listenFd = bsd_create_socket(a->ai_family, a->ai_socktype, a->ai_protocol); + listenAddr = a; + } + } + + if (listenFd == LIBUS_SOCKET_ERROR) { + freeaddrinfo(result); + return LIBUS_SOCKET_ERROR; + } + + if (port != 0) { + /* Otherwise, always enable SO_REUSEPORT and SO_REUSEADDR _unless_ options specify otherwise */ +#ifdef _WIN32 + if (options & LIBUS_LISTEN_EXCLUSIVE_PORT) { + int optval2 = 1; + setsockopt(listenFd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (void *) &optval2, sizeof(optval2)); + } else { + int optval3 = 1; + setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, (void *) &optval3, sizeof(optval3)); + } +#else + #if /*defined(__linux) &&*/ defined(SO_REUSEPORT) + if (!(options & LIBUS_LISTEN_EXCLUSIVE_PORT)) { + int optval = 1; + setsockopt(listenFd, SOL_SOCKET, SO_REUSEPORT, (void *) &optval, sizeof(optval)); + } + #endif + int enabled = 1; + setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, (void *) &enabled, sizeof(enabled)); +#endif + + } + +#ifdef IPV6_V6ONLY + int disabled = 0; + setsockopt(listenFd, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &disabled, sizeof(disabled)); +#endif + + if (bind(listenFd, listenAddr->ai_addr, (socklen_t) listenAddr->ai_addrlen) || listen(listenFd, 512)) { + bsd_close_socket(listenFd); + freeaddrinfo(result); + return LIBUS_SOCKET_ERROR; + } + + freeaddrinfo(result); + return listenFd; +} + +#ifndef _WIN32 +#include <sys/un.h> +#else +#include <afunix.h> +#include <io.h> +#endif +#include <sys/stat.h> +#include <stddef.h> +LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket_unix(const char *path, int options) { + + LIBUS_SOCKET_DESCRIPTOR listenFd = LIBUS_SOCKET_ERROR; + + listenFd = bsd_create_socket(AF_UNIX, SOCK_STREAM, 0); + + if (listenFd == LIBUS_SOCKET_ERROR) { + return LIBUS_SOCKET_ERROR; + } + +#ifndef _WIN32 + // 700 permission by default + fchmod(listenFd, S_IRWXU); +#else + _chmod(path, S_IREAD | S_IWRITE | S_IEXEC); +#endif + + struct sockaddr_un server_address; + memset(&server_address, 0, sizeof(server_address)); + server_address.sun_family = AF_UNIX; + strcpy(server_address.sun_path, path); + int size = offsetof(struct sockaddr_un, sun_path) + strlen(server_address.sun_path); +#ifdef _WIN32 + _unlink(path); +#else + unlink(path); +#endif + + if (bind(listenFd, (struct sockaddr *)&server_address, size) || listen(listenFd, 512)) { + bsd_close_socket(listenFd); + return LIBUS_SOCKET_ERROR; + } + + return listenFd; +} + +LIBUS_SOCKET_DESCRIPTOR bsd_create_udp_socket(const char *host, int port) { + struct addrinfo hints, *result; + memset(&hints, 0, sizeof(struct addrinfo)); + + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + + char port_string[16]; + snprintf(port_string, 16, "%d", port); + + if (getaddrinfo(host, port_string, &hints, &result)) { + return LIBUS_SOCKET_ERROR; + } + + LIBUS_SOCKET_DESCRIPTOR listenFd = LIBUS_SOCKET_ERROR; + struct addrinfo *listenAddr; + for (struct addrinfo *a = result; a && listenFd == LIBUS_SOCKET_ERROR; a = a->ai_next) { + if (a->ai_family == AF_INET6) { + listenFd = bsd_create_socket(a->ai_family, a->ai_socktype, a->ai_protocol); + listenAddr = a; + } + } + + for (struct addrinfo *a = result; a && listenFd == LIBUS_SOCKET_ERROR; a = a->ai_next) { + if (a->ai_family == AF_INET) { + listenFd = bsd_create_socket(a->ai_family, a->ai_socktype, a->ai_protocol); + listenAddr = a; + } + } + + if (listenFd == LIBUS_SOCKET_ERROR) { + freeaddrinfo(result); + return LIBUS_SOCKET_ERROR; + } + + if (port != 0) { + /* Should this also go for UDP? */ + int enabled = 1; + setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, (void *) &enabled, sizeof(enabled)); + } + +#ifdef IPV6_V6ONLY + int disabled = 0; + setsockopt(listenFd, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &disabled, sizeof(disabled)); +#endif + + /* We need destination address for udp packets in both ipv6 and ipv4 */ + +/* On FreeBSD this option seems to be called like so */ +#ifndef IPV6_RECVPKTINFO +#define IPV6_RECVPKTINFO IPV6_PKTINFO +#endif + + int enabled = 1; + if (setsockopt(listenFd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (void *) &enabled, sizeof(enabled)) == -1) { + if (errno == 92) { + if (setsockopt(listenFd, IPPROTO_IP, IP_PKTINFO, (void *) &enabled, sizeof(enabled)) != 0) { + printf("Error setting IPv4 pktinfo!\n"); + } + } else { + printf("Error setting IPv6 pktinfo!\n"); + } + } + + /* These are used for getting the ECN */ + if (setsockopt(listenFd, IPPROTO_IPV6, IPV6_RECVTCLASS, (void *) &enabled, sizeof(enabled)) == -1) { + if (errno == 92) { + if (setsockopt(listenFd, IPPROTO_IP, IP_RECVTOS, (void *) &enabled, sizeof(enabled)) != 0) { + printf("Error setting IPv4 ECN!\n"); + } + } else { + printf("Error setting IPv6 ECN!\n"); + } + } + + /* We bind here as well */ + if (bind(listenFd, listenAddr->ai_addr, (socklen_t) listenAddr->ai_addrlen)) { + bsd_close_socket(listenFd); + freeaddrinfo(result); + return LIBUS_SOCKET_ERROR; + } + + freeaddrinfo(result); + return listenFd; +} + +int bsd_udp_packet_buffer_ecn(void *msgvec, int index) { + +#if defined(_WIN32) || defined(__APPLE__) + printf("ECN not supported!\n"); +#else + // we should iterate all control messages once, after recvmmsg and then only fetch them with these functions + struct msghdr *mh = &((struct mmsghdr *) msgvec)[index].msg_hdr; + for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL; cmsg = CMSG_NXTHDR(mh, cmsg)) { + // do we need to get TOS from ipv6 also? + if (cmsg->cmsg_level == IPPROTO_IP) { + if (cmsg->cmsg_type == IP_TOS) { + uint8_t tos = *(uint8_t *)CMSG_DATA(cmsg); + return tos & 3; + } + } + + if (cmsg->cmsg_level == IPPROTO_IPV6) { + if (cmsg->cmsg_type == IPV6_TCLASS) { + // is this correct? + uint8_t tos = *(uint8_t *)CMSG_DATA(cmsg); + return tos & 3; + } + } + } +#endif + + printf("We got no ECN!\n"); + + return 0; // no ecn defaults to 0 +} + +static int bsd_do_connect(struct addrinfo *result, int fd) +{ + return connect(fd, result->ai_addr, (socklen_t) result->ai_addrlen); +} + +LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket(const char *host, int port, const char *source_host, int options) { + struct addrinfo hints, *result; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + char port_string[16]; + snprintf(port_string, 16, "%d", port); + + if (getaddrinfo(host, port_string, &hints, &result) != 0) { + return LIBUS_SOCKET_ERROR; + } + + LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_socket(result->ai_family, result->ai_socktype, result->ai_protocol); + if (fd == LIBUS_SOCKET_ERROR) { + freeaddrinfo(result); + return LIBUS_SOCKET_ERROR; + } + + if (source_host) { + struct addrinfo *interface_result; + if (!getaddrinfo(source_host, NULL, NULL, &interface_result)) { + int ret = bind(fd, interface_result->ai_addr, (socklen_t) interface_result->ai_addrlen); + freeaddrinfo(interface_result); + if (ret == LIBUS_SOCKET_ERROR) { + bsd_close_socket(fd); + freeaddrinfo(result); + return LIBUS_SOCKET_ERROR; + } + } + } + + do { + if (bsd_do_connect(result, fd) != 0 && errno != EINPROGRESS) { + bsd_close_socket(fd); + freeaddrinfo(result); + return LIBUS_SOCKET_ERROR; + } + } while (errno == EINTR); + + freeaddrinfo(result); + + return fd; +} + +LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket_unix(const char *server_path, int options) { + + struct sockaddr_un server_address; + memset(&server_address, 0, sizeof(server_address)); + server_address.sun_family = AF_UNIX; + strcpy(server_address.sun_path, server_path); + int size = offsetof(struct sockaddr_un, sun_path) + strlen(server_address.sun_path); + + LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_socket(AF_UNIX, SOCK_STREAM, 0); + + if (fd == LIBUS_SOCKET_ERROR) { + return LIBUS_SOCKET_ERROR; + } + + if (connect(fd, (struct sockaddr *)&server_address, size) != 0 && errno != EINPROGRESS) { + bsd_close_socket(fd); + return LIBUS_SOCKET_ERROR; + } + + return fd; +}
\ No newline at end of file diff --git a/packages/bun-usockets/src/context.c b/packages/bun-usockets/src/context.c new file mode 100644 index 000000000..a4d243e85 --- /dev/null +++ b/packages/bun-usockets/src/context.c @@ -0,0 +1,575 @@ +/* + * Authored by Alex Hultman, 2018-2019. + * Intellectual property of third-party. + + * 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 + + * http://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 "libusockets.h" +#include "internal/internal.h" +#include <stdlib.h> +#include <string.h> + +int default_is_low_prio_handler(struct us_socket_t *s) { + return 0; +} + +/* Shared with SSL */ + +unsigned short us_socket_context_timestamp(int ssl, struct us_socket_context_t *context) { + return context->timestamp; +} +int us_raw_root_certs(struct us_cert_string_t**out){ + return us_internal_raw_root_certs(out); +} + +void us_listen_socket_close(int ssl, struct us_listen_socket_t *ls) { + /* us_listen_socket_t extends us_socket_t so we close in similar ways */ + if (!us_socket_is_closed(0, &ls->s)) { + us_internal_socket_context_unlink_listen_socket(ls->s.context, ls); + us_poll_stop((struct us_poll_t *) &ls->s, ls->s.context->loop); + bsd_close_socket(us_poll_fd((struct us_poll_t *) &ls->s)); + + /* Link this socket to the close-list and let it be deleted after this iteration */ + ls->s.next = ls->s.context->loop->data.closed_head; + ls->s.context->loop->data.closed_head = &ls->s; + + /* Any socket with prev = context is marked as closed */ + ls->s.prev = (struct us_socket_t *) ls->s.context; + } + + /* We cannot immediately free a listen socket as we can be inside an accept loop */ +} + +void us_socket_context_close(int ssl, struct us_socket_context_t *context) { + /* Begin by closing all listen sockets */ + struct us_listen_socket_t *ls = context->head_listen_sockets; + while (ls) { + struct us_listen_socket_t *nextLS = (struct us_listen_socket_t *) ls->s.next; + us_listen_socket_close(ssl, ls); + ls = nextLS; + } + + /* Then close all regular sockets */ + struct us_socket_t *s = context->head_sockets; + while (s) { + struct us_socket_t *nextS = s->next; + us_socket_close(ssl, s, 0, 0); + s = nextS; + } +} + +void us_internal_socket_context_unlink_listen_socket(struct us_socket_context_t *context, struct us_listen_socket_t *ls) { + /* We have to properly update the iterator used to sweep sockets for timeouts */ + if (ls == (struct us_listen_socket_t *) context->iterator) { + context->iterator = ls->s.next; + } + + if (ls->s.prev == ls->s.next) { + context->head_listen_sockets = 0; + } else { + if (ls->s.prev) { + ls->s.prev->next = ls->s.next; + } else { + context->head_listen_sockets = (struct us_listen_socket_t *) ls->s.next; + } + if (ls->s.next) { + ls->s.next->prev = ls->s.prev; + } + } +} + +void us_internal_socket_context_unlink_socket(struct us_socket_context_t *context, struct us_socket_t *s) { + /* We have to properly update the iterator used to sweep sockets for timeouts */ + if (s == context->iterator) { + context->iterator = s->next; + } + + if (s->prev == s->next) { + context->head_sockets = 0; + } else { + if (s->prev) { + s->prev->next = s->next; + } else { + context->head_sockets = s->next; + } + if (s->next) { + s->next->prev = s->prev; + } + } +} + +/* We always add in the top, so we don't modify any s.next */ +void us_internal_socket_context_link_listen_socket(struct us_socket_context_t *context, struct us_listen_socket_t *ls) { + ls->s.context = context; + ls->s.next = (struct us_socket_t *) context->head_listen_sockets; + ls->s.prev = 0; + if (context->head_listen_sockets) { + context->head_listen_sockets->s.prev = &ls->s; + } + context->head_listen_sockets = ls; +} + +/* We always add in the top, so we don't modify any s.next */ +void us_internal_socket_context_link_socket(struct us_socket_context_t *context, struct us_socket_t *s) { + s->context = context; + s->next = context->head_sockets; + s->prev = 0; + if (context->head_sockets) { + context->head_sockets->prev = s; + } + context->head_sockets = s; +} + +struct us_loop_t *us_socket_context_loop(int ssl, struct us_socket_context_t *context) { + return context->loop; +} + +/* Not shared with SSL */ + +/* Lookup userdata by server name pattern */ +void *us_socket_context_find_server_name_userdata(int ssl, struct us_socket_context_t *context, const char *hostname_pattern) { +#ifndef LIBUS_NO_SSL + if (ssl) { + return us_internal_ssl_socket_context_find_server_name_userdata((struct us_internal_ssl_socket_context_t *) context, hostname_pattern); + } +#endif + return NULL; +} + +/* Get userdata attached to this SNI-routed socket, or nullptr if default */ +void *us_socket_server_name_userdata(int ssl, struct us_socket_t *s) { +#ifndef LIBUS_NO_SSL + if (ssl) { + return us_internal_ssl_socket_get_sni_userdata((struct us_internal_ssl_socket_t *) s); + } +#endif + return NULL; +} + +/* Add SNI context */ +void us_socket_context_add_server_name(int ssl, struct us_socket_context_t *context, const char *hostname_pattern, struct us_socket_context_options_t options, void *user) { +#ifndef LIBUS_NO_SSL + if (ssl) { + us_internal_ssl_socket_context_add_server_name((struct us_internal_ssl_socket_context_t *) context, hostname_pattern, options, user); + } +#endif +} +void us_bun_socket_context_add_server_name(int ssl, struct us_socket_context_t *context, const char *hostname_pattern, struct us_bun_socket_context_options_t options, void *user) { +#ifndef LIBUS_NO_SSL + if (ssl) { + us_bun_internal_ssl_socket_context_add_server_name((struct us_internal_ssl_socket_context_t *) context, hostname_pattern, options, user); + } +#endif +} + +/* Remove SNI context */ +void us_socket_context_remove_server_name(int ssl, struct us_socket_context_t *context, const char *hostname_pattern) { +#ifndef LIBUS_NO_SSL + if (ssl) { + us_internal_ssl_socket_context_remove_server_name((struct us_internal_ssl_socket_context_t *) context, hostname_pattern); + } +#endif +} + +/* I don't like this one - maybe rename it to on_missing_server_name? */ + +/* Called when SNI matching fails - not if a match could be made. + * You may modify the context by adding/removing names in this callback. + * If the correct name is added immediately in the callback, it will be used */ +void us_socket_context_on_server_name(int ssl, struct us_socket_context_t *context, void (*cb)(struct us_socket_context_t *, const char *hostname)) { +#ifndef LIBUS_NO_SSL + if (ssl) { + us_internal_ssl_socket_context_on_server_name((struct us_internal_ssl_socket_context_t *) context, (void (*)(struct us_internal_ssl_socket_context_t *, const char *hostname)) cb); + } +#endif +} + +/* Todo: get native context from SNI pattern */ + +void *us_socket_context_get_native_handle(int ssl, struct us_socket_context_t *context) { +#ifndef LIBUS_NO_SSL + if (ssl) { + return us_internal_ssl_socket_context_get_native_handle((struct us_internal_ssl_socket_context_t *) context); + } +#endif + + /* There is no native handle for a non-SSL socket context */ + return 0; +} + +/* Options is currently only applicable for SSL - this will change with time (prefer_low_memory is one example) */ +struct us_socket_context_t *us_create_socket_context(int ssl, struct us_loop_t *loop, int context_ext_size, struct us_socket_context_options_t options) { +#ifndef LIBUS_NO_SSL + if (ssl) { + /* This function will call us, again, with SSL = false and a bigger ext_size */ + return (struct us_socket_context_t *) us_internal_create_ssl_socket_context(loop, context_ext_size, options); + } +#endif + + /* This path is taken once either way - always BEFORE whatever SSL may do LATER. + * context_ext_size will however be modified larger in case of SSL, to hold SSL extensions */ + + struct us_socket_context_t *context = us_malloc(sizeof(struct us_socket_context_t) + context_ext_size); + context->loop = loop; + context->head_sockets = 0; + context->head_listen_sockets = 0; + context->iterator = 0; + context->next = 0; + context->is_low_prio = default_is_low_prio_handler; + + /* Begin at 0 */ + context->timestamp = 0; + context->long_timestamp = 0; + context->global_tick = 0; + + us_internal_loop_link(loop, context); + + /* If we are called from within SSL code, SSL code will make further changes to us */ + return context; +} + +struct us_socket_context_t *us_create_bun_socket_context(int ssl, struct us_loop_t *loop, int context_ext_size, struct us_bun_socket_context_options_t options) { +#ifndef LIBUS_NO_SSL + if (ssl) { + /* This function will call us, again, with SSL = false and a bigger ext_size */ + return (struct us_socket_context_t *) us_internal_bun_create_ssl_socket_context(loop, context_ext_size, options); + } +#endif + + /* This path is taken once either way - always BEFORE whatever SSL may do LATER. + * context_ext_size will however be modified larger in case of SSL, to hold SSL extensions */ + + struct us_socket_context_t *context = us_malloc(sizeof(struct us_socket_context_t) + context_ext_size); + context->loop = loop; + context->head_sockets = 0; + context->head_listen_sockets = 0; + context->iterator = 0; + context->next = 0; + context->is_low_prio = default_is_low_prio_handler; + + /* Begin at 0 */ + context->timestamp = 0; + context->long_timestamp = 0; + context->global_tick = 0; + + us_internal_loop_link(loop, context); + + /* If we are called from within SSL code, SSL code will make further changes to us */ + return context; +} + + +struct us_bun_verify_error_t us_socket_verify_error(int ssl, struct us_socket_t *socket) { + #ifndef LIBUS_NO_SSL + if (ssl) { + /* This function will call us again with SSL=false */ + return us_internal_verify_error((struct us_internal_ssl_socket_t *)socket); + } + #endif + + return (struct us_bun_verify_error_t) { .error = 0, .code = NULL, .reason = NULL }; +} + + +void us_socket_context_free(int ssl, struct us_socket_context_t *context) { +#ifndef LIBUS_NO_SSL + if (ssl) { + /* This function will call us again with SSL=false */ + us_internal_ssl_socket_context_free((struct us_internal_ssl_socket_context_t *) context); + return; + } +#endif + + /* This path is taken once either way - always AFTER whatever SSL may do BEFORE. + * This is the opposite order compared to when creating the context - SSL code is cleaning up before non-SSL */ + + us_internal_loop_unlink(context->loop, context); + us_free(context); +} + +struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_context_t *context, const char *host, int port, int options, int socket_ext_size) { +#ifndef LIBUS_NO_SSL + if (ssl) { + return us_internal_ssl_socket_context_listen((struct us_internal_ssl_socket_context_t *) context, host, port, options, socket_ext_size); + } +#endif + + LIBUS_SOCKET_DESCRIPTOR listen_socket_fd = bsd_create_listen_socket(host, port, options); + + if (listen_socket_fd == LIBUS_SOCKET_ERROR) { + return 0; + } + + struct us_poll_t *p = us_create_poll(context->loop, 0, sizeof(struct us_listen_socket_t)); + us_poll_init(p, listen_socket_fd, POLL_TYPE_SEMI_SOCKET); + us_poll_start(p, context->loop, LIBUS_SOCKET_READABLE); + + struct us_listen_socket_t *ls = (struct us_listen_socket_t *) p; + + ls->s.context = context; + ls->s.timeout = 255; + ls->s.long_timeout = 255; + ls->s.low_prio_state = 0; + ls->s.next = 0; + us_internal_socket_context_link_listen_socket(context, ls); + + ls->socket_ext_size = socket_ext_size; + + return ls; +} + +struct us_listen_socket_t *us_socket_context_listen_unix(int ssl, struct us_socket_context_t *context, const char *path, int options, int socket_ext_size) { +#ifndef LIBUS_NO_SSL + if (ssl) { + return us_internal_ssl_socket_context_listen_unix((struct us_internal_ssl_socket_context_t *) context, path, options, socket_ext_size); + } +#endif + + LIBUS_SOCKET_DESCRIPTOR listen_socket_fd = bsd_create_listen_socket_unix(path, options); + + if (listen_socket_fd == LIBUS_SOCKET_ERROR) { + return 0; + } + + struct us_poll_t *p = us_create_poll(context->loop, 0, sizeof(struct us_listen_socket_t)); + us_poll_init(p, listen_socket_fd, POLL_TYPE_SEMI_SOCKET); + us_poll_start(p, context->loop, LIBUS_SOCKET_READABLE); + + struct us_listen_socket_t *ls = (struct us_listen_socket_t *) p; + + ls->s.context = context; + ls->s.timeout = 255; + ls->s.long_timeout = 255; + ls->s.low_prio_state = 0; + ls->s.next = 0; + us_internal_socket_context_link_listen_socket(context, ls); + + ls->socket_ext_size = socket_ext_size; + + return ls; +} + +struct us_socket_t *us_socket_context_connect(int ssl, struct us_socket_context_t *context, const char *host, int port, const char *source_host, int options, int socket_ext_size) { +#ifndef LIBUS_NO_SSL + if (ssl) { + return (struct us_socket_t *) us_internal_ssl_socket_context_connect((struct us_internal_ssl_socket_context_t *) context, host, port, source_host, options, socket_ext_size); + } +#endif + + LIBUS_SOCKET_DESCRIPTOR connect_socket_fd = bsd_create_connect_socket(host, port, source_host, options); + if (connect_socket_fd == LIBUS_SOCKET_ERROR) { + return 0; + } + + /* Connect sockets are semi-sockets just like listen sockets */ + struct us_poll_t *p = us_create_poll(context->loop, 0, sizeof(struct us_socket_t) + socket_ext_size); + us_poll_init(p, connect_socket_fd, POLL_TYPE_SEMI_SOCKET); + us_poll_start(p, context->loop, LIBUS_SOCKET_WRITABLE); + + struct us_socket_t *connect_socket = (struct us_socket_t *) p; + + /* Link it into context so that timeout fires properly */ + connect_socket->context = context; + connect_socket->timeout = 255; + connect_socket->long_timeout = 255; + connect_socket->low_prio_state = 0; + us_internal_socket_context_link_socket(context, connect_socket); + + return connect_socket; +} + +struct us_socket_t *us_socket_context_connect_unix(int ssl, struct us_socket_context_t *context, const char *server_path, int options, int socket_ext_size) { +#ifndef LIBUS_NO_SSL + if (ssl) { + return (struct us_socket_t *) us_internal_ssl_socket_context_connect_unix((struct us_internal_ssl_socket_context_t *) context, server_path, options, socket_ext_size); + } +#endif + + LIBUS_SOCKET_DESCRIPTOR connect_socket_fd = bsd_create_connect_socket_unix(server_path, options); + if (connect_socket_fd == LIBUS_SOCKET_ERROR) { + return 0; + } + + /* Connect sockets are semi-sockets just like listen sockets */ + struct us_poll_t *p = us_create_poll(context->loop, 0, sizeof(struct us_socket_t) + socket_ext_size); + us_poll_init(p, connect_socket_fd, POLL_TYPE_SEMI_SOCKET); + us_poll_start(p, context->loop, LIBUS_SOCKET_WRITABLE); + + struct us_socket_t *connect_socket = (struct us_socket_t *) p; + + /* Link it into context so that timeout fires properly */ + connect_socket->context = context; + connect_socket->timeout = 255; + connect_socket->long_timeout = 255; + connect_socket->low_prio_state = 0; + us_internal_socket_context_link_socket(context, connect_socket); + + return connect_socket; +} + +struct us_socket_context_t *us_create_child_socket_context(int ssl, struct us_socket_context_t *context, int context_ext_size) { +#ifndef LIBUS_NO_SSL + if (ssl) { + return (struct us_socket_context_t *) us_internal_create_child_ssl_socket_context((struct us_internal_ssl_socket_context_t *) context, context_ext_size); + } +#endif + + /* For TCP we simply create a new context as nothing is shared */ + struct us_socket_context_options_t options = {0}; + return us_create_socket_context(ssl, context->loop, context_ext_size, options); +} + +/* Note: This will set timeout to 0 */ +struct us_socket_t *us_socket_context_adopt_socket(int ssl, struct us_socket_context_t *context, struct us_socket_t *s, int ext_size) { +#ifndef LIBUS_NO_SSL + if (ssl) { + return (struct us_socket_t *) us_internal_ssl_socket_context_adopt_socket((struct us_internal_ssl_socket_context_t *) context, (struct us_internal_ssl_socket_t *) s, ext_size); + } +#endif + + /* Cannot adopt a closed socket */ + if (us_socket_is_closed(ssl, s)) { + return s; + } + + if (s->low_prio_state != 1) { + /* This properly updates the iterator if in on_timeout */ + us_internal_socket_context_unlink_socket(s->context, s); + } + + struct us_socket_t *new_s = (struct us_socket_t *) us_poll_resize(&s->p, s->context->loop, sizeof(struct us_socket_t) + ext_size); + new_s->timeout = 255; + new_s->long_timeout = 255; + + if (new_s->low_prio_state == 1) { + /* update pointers in low-priority queue */ + if (!new_s->prev) new_s->context->loop->data.low_prio_head = new_s; + else new_s->prev->next = new_s; + + if (new_s->next) new_s->next->prev = new_s; + } else { + us_internal_socket_context_link_socket(context, new_s); + } + + return new_s; +} + + +void us_socket_context_on_open(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_open)(struct us_socket_t *s, int is_client, char *ip, int ip_length)) { +#ifndef LIBUS_NO_SSL + if (ssl) { + us_internal_ssl_socket_context_on_open((struct us_internal_ssl_socket_context_t *) context, (struct us_internal_ssl_socket_t * (*)(struct us_internal_ssl_socket_t *, int, char *, int)) on_open); + return; + } +#endif + + context->on_open = on_open; +} + +void us_socket_context_on_close(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_close)(struct us_socket_t *s, int code, void *reason)) { +#ifndef LIBUS_NO_SSL + if (ssl) { + us_internal_ssl_socket_context_on_close((struct us_internal_ssl_socket_context_t *) context, (struct us_internal_ssl_socket_t * (*)(struct us_internal_ssl_socket_t *, int code, void *reason)) on_close); + return; + } +#endif + + context->on_close = on_close; +} + +void us_socket_context_on_data(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_data)(struct us_socket_t *s, char *data, int length)) { +#ifndef LIBUS_NO_SSL + if (ssl) { + us_internal_ssl_socket_context_on_data((struct us_internal_ssl_socket_context_t *) context, (struct us_internal_ssl_socket_t * (*)(struct us_internal_ssl_socket_t *, char *, int)) on_data); + return; + } +#endif + + context->on_data = on_data; +} + +void us_socket_context_on_writable(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_writable)(struct us_socket_t *s)) { +#ifndef LIBUS_NO_SSL + if (ssl) { + us_internal_ssl_socket_context_on_writable((struct us_internal_ssl_socket_context_t *) context, (struct us_internal_ssl_socket_t * (*)(struct us_internal_ssl_socket_t *)) on_writable); + return; + } +#endif + + context->on_writable = on_writable; +} + +void us_socket_context_on_long_timeout(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_long_timeout)(struct us_socket_t *)) { +#ifndef LIBUS_NO_SSL + if (ssl) { + us_internal_ssl_socket_context_on_long_timeout((struct us_internal_ssl_socket_context_t *) context, (struct us_internal_ssl_socket_t * (*)(struct us_internal_ssl_socket_t *)) on_long_timeout); + return; + } +#endif + + context->on_socket_long_timeout = on_long_timeout; +} + +void us_socket_context_on_timeout(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_timeout)(struct us_socket_t *)) { +#ifndef LIBUS_NO_SSL + if (ssl) { + us_internal_ssl_socket_context_on_timeout((struct us_internal_ssl_socket_context_t *) context, (struct us_internal_ssl_socket_t * (*)(struct us_internal_ssl_socket_t *)) on_timeout); + return; + } +#endif + + context->on_socket_timeout = on_timeout; +} + +void us_socket_context_on_end(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_end)(struct us_socket_t *)) { +#ifndef LIBUS_NO_SSL + if (ssl) { + us_internal_ssl_socket_context_on_end((struct us_internal_ssl_socket_context_t *) context, (struct us_internal_ssl_socket_t * (*)(struct us_internal_ssl_socket_t *)) on_end); + return; + } +#endif + + context->on_end = on_end; +} + +void us_socket_context_on_connect_error(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_connect_error)(struct us_socket_t *s, int code)) { +#ifndef LIBUS_NO_SSL + if (ssl) { + us_internal_ssl_socket_context_on_connect_error((struct us_internal_ssl_socket_context_t *) context, (struct us_internal_ssl_socket_t * (*)(struct us_internal_ssl_socket_t *, int)) on_connect_error); + return; + } +#endif + + context->on_connect_error = on_connect_error; +} + +void *us_socket_context_ext(int ssl, struct us_socket_context_t *context) { +#ifndef LIBUS_NO_SSL + if (ssl) { + return us_internal_ssl_socket_context_ext((struct us_internal_ssl_socket_context_t *) context); + } +#endif + + return context + 1; +} + + +void us_socket_context_on_handshake(int ssl, struct us_socket_context_t *context, void (*on_handshake)(struct us_socket_context_t *, int success, struct us_bun_verify_error_t verify_error, void* custom_data), void* custom_data) { +#ifndef LIBUS_NO_SSL + if (ssl) { + us_internal_on_ssl_handshake((struct us_internal_ssl_socket_context_t *) context, on_handshake, custom_data); + return; + } +#endif +}
\ No newline at end of file diff --git a/packages/bun-usockets/src/crypto/openssl.c b/packages/bun-usockets/src/crypto/openssl.c new file mode 100644 index 000000000..b6466bcf9 --- /dev/null +++ b/packages/bun-usockets/src/crypto/openssl.c @@ -0,0 +1,1732 @@ +/* + * Authored by Alex Hultman, 2018-2019. + * Intellectual property of third-party. + + * 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 + + * http://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. + */ + +#if (defined(LIBUS_USE_OPENSSL) || defined(LIBUS_USE_WOLFSSL)) + +/* These are in sni_tree.cpp */ +void *sni_new(); +void sni_free(void *sni, void(*cb)(void *)); +int sni_add(void *sni, const char *hostname, void *user); +void *sni_remove(void *sni, const char *hostname); +void *sni_find(void *sni, const char *hostname); + +#include "libusockets.h" +#include "internal/internal.h" +#include <string.h> + +/* This module contains the entire OpenSSL implementation + * of the SSL socket and socket context interfaces. */ +#ifdef LIBUS_USE_OPENSSL +#include <openssl/ssl.h> +#include <openssl/bio.h> +#include <openssl/err.h> +#include <openssl/dh.h> +#elif LIBUS_USE_WOLFSSL +#include <wolfssl/options.h> +#include <wolfssl/openssl/ssl.h> +#include <wolfssl/openssl/bio.h> +#include <wolfssl/openssl/err.h> +#include <wolfssl/openssl/dh.h> +#endif + +#include "./root_certs.h" +#include <stdatomic.h> +static const root_certs_size = sizeof(root_certs) / sizeof(root_certs[0]); +static X509* root_cert_instances[root_certs_size] = {NULL}; +static atomic_flag root_cert_instances_lock = ATOMIC_FLAG_INIT; +static atomic_bool root_cert_instances_initialized = 0; + +struct loop_ssl_data { + char *ssl_read_input, *ssl_read_output; + unsigned int ssl_read_input_length; + unsigned int ssl_read_input_offset; + + struct us_socket_t *ssl_socket; + + int last_write_was_msg_more; + int msg_more; + + BIO *shared_rbio; + BIO *shared_wbio; + BIO_METHOD *shared_biom; +}; + +struct us_internal_ssl_socket_context_t { + struct us_socket_context_t sc; + + // this thing can be shared with other socket contexts via socket transfer! + // maybe instead of holding once you hold many, a vector or set + // when a socket that belongs to another socket context transfers to a new socket context + SSL_CTX *ssl_context; + int is_parent; + + /* These decorate the base implementation */ + struct us_internal_ssl_socket_t *(*on_open)(struct us_internal_ssl_socket_t *, int is_client, char *ip, int ip_length); + struct us_internal_ssl_socket_t *(*on_data)(struct us_internal_ssl_socket_t *, char *data, int length); + struct us_internal_ssl_socket_t *(*on_writable)(struct us_internal_ssl_socket_t *); + struct us_internal_ssl_socket_t *(*on_close)(struct us_internal_ssl_socket_t *, int code, void *reason); + + /* Called for missing SNI hostnames, if not NULL */ + void (*on_server_name)(struct us_internal_ssl_socket_context_t *, const char *hostname); + + /* Pointer to sni tree, created when the context is created and freed likewise when freed */ + void *sni; + + int pending_handshake; + void (*on_handshake)(struct us_internal_ssl_socket_t *, int success, struct us_bun_verify_error_t verify_error, void* custom_data); + void* handshake_data; +}; + +// same here, should or shouldn't it contain s? +struct us_internal_ssl_socket_t { + struct us_socket_t s; + SSL *ssl; + int ssl_write_wants_read; // we use this for now + int ssl_read_wants_write; +}; + +int passphrase_cb(char *buf, int size, int rwflag, void *u) { + const char *passphrase = (const char *) u; + size_t passphrase_length = strlen(passphrase); + memcpy(buf, passphrase, passphrase_length); + // put null at end? no? + return (int) passphrase_length; +} + +int BIO_s_custom_create(BIO *bio) { + BIO_set_init(bio, 1); + return 1; +} + +long BIO_s_custom_ctrl(BIO *bio, int cmd, long num, void *user) { + switch(cmd) { + case BIO_CTRL_FLUSH: + return 1; + default: + return 0; + } +} + + + +int BIO_s_custom_write(BIO *bio, const char *data, int length) { + struct loop_ssl_data *loop_ssl_data = (struct loop_ssl_data *) BIO_get_data(bio); + + loop_ssl_data->last_write_was_msg_more = loop_ssl_data->msg_more || length == 16413; + int written = us_socket_write(0, loop_ssl_data->ssl_socket, data, length, loop_ssl_data->last_write_was_msg_more); + + if (!written) { + BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY | BIO_FLAGS_WRITE); + return -1; + } + + // printf("BIO_s_custom_write returns: %d\n", written); + + return written; +} + +int BIO_s_custom_read(BIO *bio, char *dst, int length) { + struct loop_ssl_data *loop_ssl_data = (struct loop_ssl_data *) BIO_get_data(bio); + + //printf("BIO_s_custom_read\n"); + + if (!loop_ssl_data->ssl_read_input_length) { + BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY | BIO_FLAGS_READ); + return -1; + } + + if ((unsigned int) length > loop_ssl_data->ssl_read_input_length) { + length = loop_ssl_data->ssl_read_input_length; + } + + memcpy(dst, loop_ssl_data->ssl_read_input + loop_ssl_data->ssl_read_input_offset, length); + + loop_ssl_data->ssl_read_input_offset += length; + loop_ssl_data->ssl_read_input_length -= length; + return length; +} + + +struct us_internal_ssl_socket_t *ssl_on_open(struct us_internal_ssl_socket_t *s, int is_client, char *ip, int ip_length) { + + struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s); + + struct us_loop_t *loop = us_socket_context_loop(0, &context->sc); + struct loop_ssl_data *loop_ssl_data = (struct loop_ssl_data *) loop->data.ssl_data; + + s->ssl = SSL_new(context->ssl_context); + s->ssl_write_wants_read = 0; + s->ssl_read_wants_write = 0; + + SSL_set_bio(s->ssl, loop_ssl_data->shared_rbio, loop_ssl_data->shared_wbio); + + BIO_up_ref(loop_ssl_data->shared_rbio); + BIO_up_ref(loop_ssl_data->shared_wbio); + + if (is_client) { + SSL_set_connect_state(s->ssl); + } else { + SSL_set_accept_state(s->ssl); + } + + struct us_internal_ssl_socket_t * result = (struct us_internal_ssl_socket_t *) context->on_open(s, is_client, ip, ip_length); + + // Hello Message! + if(context->pending_handshake) { + us_internal_ssl_handshake(s, context->on_handshake, context->handshake_data); + } + + return result; +} + + +void us_internal_on_ssl_handshake(struct us_internal_ssl_socket_context_t * context, void (*on_handshake)(struct us_internal_ssl_socket_t *, int success, struct us_bun_verify_error_t verify_error, void* custom_data), void* custom_data) { + context->pending_handshake = 1; + context->on_handshake = on_handshake; + context->handshake_data = custom_data; +} + +void us_internal_ssl_handshake(struct us_internal_ssl_socket_t *s, void (*on_handshake)(struct us_internal_ssl_socket_t *, int success, struct us_bun_verify_error_t verify_error, void* custom_data), void* custom_data) { + struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s); + + // will start on_open, on_writable or on_data + if(!s->ssl) { + + context->pending_handshake = 1; + context->on_handshake = on_handshake; + context->handshake_data = custom_data; + return; + } + + struct us_loop_t *loop = us_socket_context_loop(0, &context->sc); + struct loop_ssl_data *loop_ssl_data = (struct loop_ssl_data *) loop->data.ssl_data; + + loop_ssl_data->ssl_socket = &s->s; + + if (us_socket_is_closed(0, &s->s) || us_internal_ssl_socket_is_shut_down(s)) { + context->pending_handshake = 0; + context->on_handshake = NULL; + context->handshake_data = NULL; + + struct us_bun_verify_error_t verify_error = (struct us_bun_verify_error_t) { .error = 0, .code = NULL, .reason = NULL }; + if(on_handshake != NULL) { + on_handshake(s, 0, verify_error, custom_data); + } + return; + } + + + int result = SSL_do_handshake(s->ssl); + + if (result <= 0) { + int err = SSL_get_error(s->ssl, result); + // as far as I know these are the only errors we want to handle + if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) { + context->pending_handshake = 0; + context->on_handshake = NULL; + context->handshake_data = NULL; + + struct us_bun_verify_error_t verify_error = us_internal_verify_error(s); + // clear per thread error queue if it may contain something + if (err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL) { + ERR_clear_error(); + } + + // error + if(on_handshake != NULL) { + on_handshake(s, 0, verify_error, custom_data); + } + return; + } else { + context->pending_handshake = 1; + context->on_handshake = on_handshake; + context->handshake_data = custom_data; + // Ensure that we'll cycle through internal openssl's state + if (!us_socket_is_closed(0, &s->s) && !us_internal_ssl_socket_is_shut_down(s)) { + us_socket_write(1, loop_ssl_data->ssl_socket, "\0", 0, 0); + } + + } + } else { + context->pending_handshake = 0; + context->on_handshake = NULL; + context->handshake_data = NULL; + + + struct us_bun_verify_error_t verify_error = us_internal_verify_error(s); + // success + if(on_handshake != NULL) { + on_handshake(s, 1, verify_error, custom_data); + } + // Ensure that we'll cycle through internal openssl's state + if (!us_socket_is_closed(0, &s->s) && !us_internal_ssl_socket_is_shut_down(s)) { + us_socket_write(1, loop_ssl_data->ssl_socket, "\0", 0, 0); + } + } + +} + + +struct us_internal_ssl_socket_t *us_internal_ssl_socket_close(struct us_internal_ssl_socket_t *s, int code, void *reason) { + struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s); + if (context->pending_handshake) { + context->pending_handshake = 0; + } + return (struct us_internal_ssl_socket_t *) us_socket_close(0, (struct us_socket_t *) s, code, reason); +} + +struct us_internal_ssl_socket_t *ssl_on_close(struct us_internal_ssl_socket_t *s, int code, void *reason) { + struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s); + if (context->pending_handshake) { + context->pending_handshake = 0; + } + SSL_free(s->ssl); + + return context->on_close(s, code, reason); +} + +struct us_internal_ssl_socket_t *ssl_on_end(struct us_internal_ssl_socket_t *s) { + if(&s->s) { + struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s); + if (context && context->pending_handshake) { + context->pending_handshake = 0; + } + } + // whatever state we are in, a TCP FIN is always an answered shutdown + + /* Todo: this should report CLEANLY SHUTDOWN as reason */ + return us_internal_ssl_socket_close(s, 0, NULL); +} + +// this whole function needs a complete clean-up +struct us_internal_ssl_socket_t *ssl_on_data(struct us_internal_ssl_socket_t *s, void *data, int length) { + + // note: this context can change when we adopt the socket! + struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s); + + + struct us_loop_t *loop = us_socket_context_loop(0, &context->sc); + struct loop_ssl_data *loop_ssl_data = (struct loop_ssl_data *) loop->data.ssl_data; + + if(context->pending_handshake) { + us_internal_ssl_handshake(s, context->on_handshake, context->handshake_data); + } + + // note: if we put data here we should never really clear it (not in write either, it still should be available for SSL_write to read from!) + loop_ssl_data->ssl_read_input = data; + loop_ssl_data->ssl_read_input_length = length; + loop_ssl_data->ssl_read_input_offset = 0; + loop_ssl_data->ssl_socket = &s->s; + loop_ssl_data->msg_more = 0; + + if (us_socket_is_closed(0, &s->s)) { + return s; + } + + if (us_internal_ssl_socket_is_shut_down(s)) { + + + int ret = 0; + if ((ret = SSL_shutdown(s->ssl)) == 1) { + // two phase shutdown is complete here + //printf("Two step SSL shutdown complete\n"); + + /* Todo: this should also report some kind of clean shutdown */ + return us_internal_ssl_socket_close(s, 0, NULL); + } else if (ret < 0) { + + int err = SSL_get_error(s->ssl, ret); + + if (err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL) { + // we need to clear the error queue in case these added to the thread local queue + ERR_clear_error(); + } + + } + + // no further processing of data when in shutdown state + return s; + } + + // bug checking: this loop needs a lot of attention and clean-ups and check-ups + int read = 0; + restart: + while (1) { + int just_read = SSL_read(s->ssl, loop_ssl_data->ssl_read_output + LIBUS_RECV_BUFFER_PADDING + read, LIBUS_RECV_BUFFER_LENGTH - read); + + if (just_read <= 0) { + int err = SSL_get_error(s->ssl, just_read); + + // as far as I know these are the only errors we want to handle + if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) { + + if (err == SSL_ERROR_ZERO_RETURN) { + // zero return can be EOF/FIN, if we have data just signal on_data and close + if (read) { + context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s); + + s = context->on_data(s, loop_ssl_data->ssl_read_output + LIBUS_RECV_BUFFER_PADDING, read); + if (us_socket_is_closed(0, &s->s)) { + return s; + } + } + // terminate connection here + return us_internal_ssl_socket_close(s, 0, NULL); + } + + if (err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL) { + // clear per thread error queue if it may contain something + + ERR_clear_error(); + } + + // terminate connection here + return us_internal_ssl_socket_close(s, 0, NULL); + } else { + // emit the data we have and exit + + if (err == SSL_ERROR_WANT_WRITE) { + // here we need to trigger writable event next ssl_read! + s->ssl_read_wants_write = 1; + } + + // assume we emptied the input buffer fully or error here as well! + if (loop_ssl_data->ssl_read_input_length) { + return us_internal_ssl_socket_close(s, 0, NULL); + } + + // cannot emit zero length to app + if (!read) { + break; + } + + context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s); + + s = context->on_data(s, loop_ssl_data->ssl_read_output + LIBUS_RECV_BUFFER_PADDING, read); + if (us_socket_is_closed(0, &s->s)) { + return s; + } + + break; + } + + } + + read += just_read; + + // at this point we might be full and need to emit the data to application and start over + if (read == LIBUS_RECV_BUFFER_LENGTH) { + + context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s); + + // emit data and restart + s = context->on_data(s, loop_ssl_data->ssl_read_output + LIBUS_RECV_BUFFER_PADDING, read); + if (us_socket_is_closed(0, &s->s)) { + return s; + } + + read = 0; + goto restart; + } + } + + // trigger writable if we failed last write with want read + if (s->ssl_write_wants_read) { + s->ssl_write_wants_read = 0; + + // make sure to update context before we call (context can change if the user adopts the socket!) + context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s); + + s = (struct us_internal_ssl_socket_t *) context->sc.on_writable(&s->s); // cast here! + // if we are closed here, then exit + if (us_socket_is_closed(0, &s->s)) { + return s; + } + } + + // check this then? + if (SSL_get_shutdown(s->ssl) & SSL_RECEIVED_SHUTDOWN) { + //printf("SSL_RECEIVED_SHUTDOWN\n"); + + //exit(-2); + + // not correct anyways! + s = us_internal_ssl_socket_close(s, 0, NULL); + + //us_ + } + + return s; +} + +struct us_internal_ssl_socket_t *ssl_on_writable(struct us_internal_ssl_socket_t *s) { + + struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s); + + if(context->pending_handshake) { + us_internal_ssl_handshake(s, context->on_handshake, context->handshake_data); + } + + // todo: cork here so that we efficiently output both from reading and from writing? + if (s->ssl_read_wants_write) { + s->ssl_read_wants_write = 0; + + // make sure to update context before we call (context can change if the user adopts the socket!) + context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s); + + // if this one fails to write data, it sets ssl_read_wants_write again + s = (struct us_internal_ssl_socket_t *) context->sc.on_data(&s->s, 0, 0); // cast here! + } + + // should this one come before we have read? should it come always? spurious on_writable is okay + s = context->on_writable(s); + + return s; +} + +/* Lazily inits loop ssl data first time */ +void us_internal_init_loop_ssl_data(struct us_loop_t *loop) { + if (!loop->data.ssl_data) { + struct loop_ssl_data *loop_ssl_data = us_malloc(sizeof(struct loop_ssl_data)); + loop_ssl_data->ssl_read_input_length = 0; + loop_ssl_data->ssl_read_input_offset = 0; + loop_ssl_data->last_write_was_msg_more = 0; + loop_ssl_data->msg_more = 0; + + loop_ssl_data->ssl_read_output = us_malloc(LIBUS_RECV_BUFFER_LENGTH + LIBUS_RECV_BUFFER_PADDING * 2); + + OPENSSL_init_ssl(0, NULL); + + loop_ssl_data->shared_biom = BIO_meth_new(BIO_TYPE_MEM, "µS BIO"); + BIO_meth_set_create(loop_ssl_data->shared_biom, BIO_s_custom_create); + BIO_meth_set_write(loop_ssl_data->shared_biom, BIO_s_custom_write); + BIO_meth_set_read(loop_ssl_data->shared_biom, BIO_s_custom_read); + BIO_meth_set_ctrl(loop_ssl_data->shared_biom, BIO_s_custom_ctrl); + + loop_ssl_data->shared_rbio = BIO_new(loop_ssl_data->shared_biom); + loop_ssl_data->shared_wbio = BIO_new(loop_ssl_data->shared_biom); + BIO_set_data(loop_ssl_data->shared_rbio, loop_ssl_data); + BIO_set_data(loop_ssl_data->shared_wbio, loop_ssl_data); + + loop->data.ssl_data = loop_ssl_data; + } +} + +/* Called by loop free, clears any loop ssl data */ +void us_internal_free_loop_ssl_data(struct us_loop_t *loop) { + struct loop_ssl_data *loop_ssl_data = (struct loop_ssl_data *) loop->data.ssl_data; + + if (loop_ssl_data) { + us_free(loop_ssl_data->ssl_read_output); + + BIO_free(loop_ssl_data->shared_rbio); + BIO_free(loop_ssl_data->shared_wbio); + + BIO_meth_free(loop_ssl_data->shared_biom); + + us_free(loop_ssl_data); + } +} + +// we throttle reading data for ssl sockets that are in init state. here we actually use +// the kernel buffering to our advantage +int ssl_is_low_prio(struct us_internal_ssl_socket_t *s) { + /* We use SSL_in_before() instead of SSL_in_init(), because only the first step is CPU intensive, and we want to + * speed up the rest of connection establishing if the CPU intensive work is already done, so fully established + * connections increase lineary over time under high load */ + return SSL_in_init(s->ssl); +} + +/* Per-context functions */ +void *us_internal_ssl_socket_context_get_native_handle(struct us_internal_ssl_socket_context_t *context) { + return context->ssl_context; +} + +struct us_internal_ssl_socket_context_t *us_internal_create_child_ssl_socket_context(struct us_internal_ssl_socket_context_t *context, int context_ext_size) { + /* Create a new non-SSL context */ + struct us_socket_context_options_t options = {0}; + struct us_internal_ssl_socket_context_t *child_context = (struct us_internal_ssl_socket_context_t *) us_create_socket_context(0, context->sc.loop, sizeof(struct us_internal_ssl_socket_context_t) + context_ext_size, options); + + /* The only thing we share is SSL_CTX */ + child_context->ssl_context = context->ssl_context; + child_context->is_parent = 0; + + return child_context; +} + +/* Common function for creating a context from options. + * We must NOT free a SSL_CTX with only SSL_CTX_free! Also free any password */ +void free_ssl_context(SSL_CTX *ssl_context) { + if (!ssl_context) { + return; + } + + /* If we have set a password string, free it here */ + void *password = SSL_CTX_get_default_passwd_cb_userdata(ssl_context); + /* OpenSSL returns NULL if we have no set password */ + us_free(password); + + SSL_CTX_free(ssl_context); +} + + + +// This callback is used to avoid the default passphrase callback in OpenSSL +// which will typically prompt for the passphrase. The prompting is designed +// for the OpenSSL CLI, but works poorly for this case because it involves +// synchronous interaction with the controlling terminal, something we never +// want, and use this function to avoid it. +int us_no_password_callback(char* buf, int size, int rwflag, void* u) { + return 0; +} + + +static X509 * us_ssl_ctx_get_X509_without_callback_from(struct us_cert_string_t content) { + X509 *x = NULL; + BIO *in; + + ERR_clear_error(); // clear error stack for SSL_CTX_use_certificate() + + in = BIO_new_mem_buf(content.str, content.len); + if (in == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); + goto end; + } + + x = PEM_read_bio_X509(in, NULL, us_no_password_callback, NULL); + if (x == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_PEM_LIB); + goto end; + } + + return x; + +end: + X509_free(x); + BIO_free(in); + return NULL; +} + +int us_internal_raw_root_certs(struct us_cert_string_t** out) { + *out = root_certs; + return root_certs_size; +} + +void us_internal_init_root_certs() { + if(atomic_load(&root_cert_instances_initialized) == 1) return; + + while(atomic_flag_test_and_set_explicit(&root_cert_instances_lock, memory_order_acquire)); + + // if some thread already created it after we acquired the lock we skip and release the lock + if(atomic_load(&root_cert_instances_initialized) == 0) { + for (size_t i = 0; i < root_certs_size; i++) { + root_cert_instances[i] = us_ssl_ctx_get_X509_without_callback_from(root_certs[i]); + } + + atomic_store(&root_cert_instances_initialized, 1); + } + + atomic_flag_clear_explicit(&root_cert_instances_lock, memory_order_release); +} + +X509_STORE* us_get_default_ca_store() { + X509_STORE *store = X509_STORE_new(); + if (store == NULL) { + return NULL; + } + + if (!X509_STORE_set_default_paths(store)) { + X509_STORE_free(store); + return NULL; + } + + us_internal_init_root_certs(); + + // load all root_cert_instances on the default ca store + for (size_t i = 0; i < root_certs_size; i++) { + X509* cert = root_cert_instances[i]; + if(cert == NULL) continue; + X509_up_ref(cert); + X509_STORE_add_cert(store, cert); + } + + return store; +} + + +/* This function should take any options and return SSL_CTX - which has to be free'd with + * our destructor function - free_ssl_context() */ +SSL_CTX *create_ssl_context_from_options(struct us_socket_context_options_t options) { + /* Create the context */ + SSL_CTX *ssl_context = SSL_CTX_new(TLS_method()); + + /* Default options we rely on - changing these will break our logic */ + SSL_CTX_set_read_ahead(ssl_context, 1); + SSL_CTX_set_mode(ssl_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + + /* Anything below TLS 1.2 is disabled */ + SSL_CTX_set_min_proto_version(ssl_context, TLS1_2_VERSION); + + /* The following are helpers. You may easily implement whatever you want by using the native handle directly */ + + /* Important option for lowering memory usage, but lowers performance slightly */ + if (options.ssl_prefer_low_memory_usage) { + SSL_CTX_set_mode(ssl_context, SSL_MODE_RELEASE_BUFFERS); + } + + if (options.passphrase) { + /* When freeing the CTX we need to check SSL_CTX_get_default_passwd_cb_userdata and + * free it if set */ + SSL_CTX_set_default_passwd_cb_userdata(ssl_context, (void *) strdup(options.passphrase)); + SSL_CTX_set_default_passwd_cb(ssl_context, passphrase_cb); + } + + /* This one most probably do not need the cert_file_name string to be kept alive */ + if (options.cert_file_name) { + if (SSL_CTX_use_certificate_chain_file(ssl_context, options.cert_file_name) != 1) { + free_ssl_context(ssl_context); + return NULL; + } + } + + /* Same as above - we can discard this string afterwards I suppose */ + if (options.key_file_name) { + if (SSL_CTX_use_PrivateKey_file(ssl_context, options.key_file_name, SSL_FILETYPE_PEM) != 1) { + free_ssl_context(ssl_context); + return NULL; + } + } + + if (options.ca_file_name) { + STACK_OF(X509_NAME) *ca_list; + ca_list = SSL_load_client_CA_file(options.ca_file_name); + if(ca_list == NULL) { + free_ssl_context(ssl_context); + return NULL; + } + SSL_CTX_set_client_CA_list(ssl_context, ca_list); + if (SSL_CTX_load_verify_locations(ssl_context, options.ca_file_name, NULL) != 1) { + free_ssl_context(ssl_context); + return NULL; + } + SSL_CTX_set_verify(ssl_context, SSL_VERIFY_PEER, NULL); + } + + if (options.dh_params_file_name) { + /* Set up ephemeral DH parameters. */ + DH *dh_2048 = NULL; + FILE *paramfile; + paramfile = fopen(options.dh_params_file_name, "r"); + + if (paramfile) { + dh_2048 = PEM_read_DHparams(paramfile, NULL, NULL, NULL); + fclose(paramfile); + } else { + free_ssl_context(ssl_context); + return NULL; + } + + if (dh_2048 == NULL) { + free_ssl_context(ssl_context); + return NULL; + } + + const long set_tmp_dh = SSL_CTX_set_tmp_dh(ssl_context, dh_2048); + DH_free(dh_2048); + + if (set_tmp_dh != 1) { + free_ssl_context(ssl_context); + return NULL; + } + + /* OWASP Cipher String 'A+' (https://www.owasp.org/index.php/TLS_Cipher_String_Cheat_Sheet) */ + if (SSL_CTX_set_cipher_list(ssl_context, "DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256") != 1) { + free_ssl_context(ssl_context); + return NULL; + } + } + + if (options.ssl_ciphers) { + if (SSL_CTX_set_cipher_list(ssl_context, options.ssl_ciphers) != 1) { + free_ssl_context(ssl_context); + return NULL; + } + } + + /* This must be free'd with free_ssl_context, not SSL_CTX_free */ + return ssl_context; +} + + + +int us_ssl_ctx_use_privatekey_content(SSL_CTX *ctx, const char *content, int type) { + int reason_code, ret = 0; + BIO *in; + EVP_PKEY *pkey = NULL; + in = BIO_new_mem_buf(content, strlen(content)); + if (in == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); + goto end; + } + + if (type == SSL_FILETYPE_PEM) { + reason_code = ERR_R_PEM_LIB; + pkey = PEM_read_bio_PrivateKey(in, NULL, SSL_CTX_get_default_passwd_cb(ctx), + SSL_CTX_get_default_passwd_cb_userdata(ctx)); + } else if (type == SSL_FILETYPE_ASN1) { + reason_code = ERR_R_ASN1_LIB; + pkey = d2i_PrivateKey_bio(in, NULL); + } else { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SSL_FILETYPE); + goto end; + } + + if (pkey == NULL) { + OPENSSL_PUT_ERROR(SSL, reason_code); + goto end; + } + ret = SSL_CTX_use_PrivateKey(ctx, pkey); + EVP_PKEY_free(pkey); + +end: + BIO_free(in); + return ret; +} + +X509 * us_ssl_ctx_get_X509_from(SSL_CTX *ctx, const char *content) { + X509 *x = NULL; + BIO *in; + + ERR_clear_error(); // clear error stack for SSL_CTX_use_certificate() + + in = BIO_new_mem_buf(content, strlen(content)); + if (in == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); + goto end; + } + + x = PEM_read_bio_X509(in, NULL, SSL_CTX_get_default_passwd_cb(ctx), + SSL_CTX_get_default_passwd_cb_userdata(ctx)); + if (x == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_PEM_LIB); + goto end; + } + + return x; + +end: + X509_free(x); + BIO_free(in); + return NULL; +} + +int us_ssl_ctx_use_certificate_chain(SSL_CTX *ctx, const char *content) { + BIO *in; + int ret = 0; + X509 *x = NULL; + + ERR_clear_error(); // clear error stack for SSL_CTX_use_certificate() + + in = BIO_new_mem_buf(content, strlen(content)); + if (in == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); + goto end; + } + + x = PEM_read_bio_X509_AUX(in, NULL, SSL_CTX_get_default_passwd_cb(ctx), + SSL_CTX_get_default_passwd_cb_userdata(ctx)); + if (x == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_PEM_LIB); + goto end; + } + + ret = SSL_CTX_use_certificate(ctx, x); + + if (ERR_peek_error() != 0) { + ret = 0; // Key/certificate mismatch doesn't imply ret==0 ... + } + + if (ret) { + // If we could set up our certificate, now proceed to the CA + // certificates. + X509 *ca; + int r; + uint32_t err; + + SSL_CTX_clear_chain_certs(ctx); + + while ((ca = PEM_read_bio_X509(in, NULL, SSL_CTX_get_default_passwd_cb(ctx), + SSL_CTX_get_default_passwd_cb_userdata(ctx))) != + NULL) { + r = SSL_CTX_add0_chain_cert(ctx, ca); + if (!r) { + X509_free(ca); + ret = 0; + goto end; + } + // Note that we must not free r if it was successfully added to the chain + // (while we must free the main certificate, since its reference count is + // increased by SSL_CTX_use_certificate). + } + + // When the while loop ends, it's usually just EOF. + err = ERR_peek_last_error(); + if (ERR_GET_LIB(err) == ERR_LIB_PEM && + ERR_GET_REASON(err) == PEM_R_NO_START_LINE) { + ERR_clear_error(); + } else { + ret = 0; // some real error + } + } + +end: + X509_free(x); + BIO_free(in); + return ret; +} + +const char* us_X509_error_code(long err) { // NOLINT(runtime/int) + const char* code = "UNSPECIFIED"; +#define CASE_X509_ERR(CODE) case X509_V_ERR_##CODE: code = #CODE; break; + switch (err) { + // if you modify anything in here, *please* update the respective section in + // doc/api/tls.md as well + CASE_X509_ERR(UNABLE_TO_GET_ISSUER_CERT) + CASE_X509_ERR(UNABLE_TO_GET_CRL) + CASE_X509_ERR(UNABLE_TO_DECRYPT_CERT_SIGNATURE) + CASE_X509_ERR(UNABLE_TO_DECRYPT_CRL_SIGNATURE) + CASE_X509_ERR(UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY) + CASE_X509_ERR(CERT_SIGNATURE_FAILURE) + CASE_X509_ERR(CRL_SIGNATURE_FAILURE) + CASE_X509_ERR(CERT_NOT_YET_VALID) + CASE_X509_ERR(CERT_HAS_EXPIRED) + CASE_X509_ERR(CRL_NOT_YET_VALID) + CASE_X509_ERR(CRL_HAS_EXPIRED) + CASE_X509_ERR(ERROR_IN_CERT_NOT_BEFORE_FIELD) + CASE_X509_ERR(ERROR_IN_CERT_NOT_AFTER_FIELD) + CASE_X509_ERR(ERROR_IN_CRL_LAST_UPDATE_FIELD) + CASE_X509_ERR(ERROR_IN_CRL_NEXT_UPDATE_FIELD) + CASE_X509_ERR(OUT_OF_MEM) + CASE_X509_ERR(DEPTH_ZERO_SELF_SIGNED_CERT) + CASE_X509_ERR(SELF_SIGNED_CERT_IN_CHAIN) + CASE_X509_ERR(UNABLE_TO_GET_ISSUER_CERT_LOCALLY) + CASE_X509_ERR(UNABLE_TO_VERIFY_LEAF_SIGNATURE) + CASE_X509_ERR(CERT_CHAIN_TOO_LONG) + CASE_X509_ERR(CERT_REVOKED) + CASE_X509_ERR(INVALID_CA) + CASE_X509_ERR(PATH_LENGTH_EXCEEDED) + CASE_X509_ERR(INVALID_PURPOSE) + CASE_X509_ERR(CERT_UNTRUSTED) + CASE_X509_ERR(CERT_REJECTED) + CASE_X509_ERR(HOSTNAME_MISMATCH) + } +#undef CASE_X509_ERR + return code; +} + +long us_internal_verify_peer_certificate( // NOLINT(runtime/int) + const SSL* ssl, + long def) { // NOLINT(runtime/int) + long err = def; // NOLINT(runtime/int) + X509* peer_cert = SSL_get_peer_certificate(ssl); + if (peer_cert) { + X509_free(peer_cert); + err = SSL_get_verify_result(ssl); + } else { + const SSL_CIPHER* curr_cipher = SSL_get_current_cipher(ssl); + + const SSL_SESSION* sess = SSL_get_session(ssl); + // Allow no-cert for PSK authentication in TLS1.2 and lower. + // In TLS1.3 check that session was reused because TLS1.3 PSK + // looks like session resumption. + if ((curr_cipher && SSL_CIPHER_get_auth_nid(curr_cipher) == NID_auth_psk) || + (sess && SSL_SESSION_get_protocol_version(sess) == TLS1_3_VERSION && + SSL_session_reused(ssl))) { + return X509_V_OK; + } + } + return err; +} + + +struct us_bun_verify_error_t us_internal_verify_error(struct us_internal_ssl_socket_t *s) { + + if (us_socket_is_closed(0, &s->s) || us_internal_ssl_socket_is_shut_down(s)) { + return (struct us_bun_verify_error_t) { .error = 0, .code = NULL, .reason = NULL }; + } + + SSL* ssl = s->ssl; + long x509_verify_error = // NOLINT(runtime/int) + us_internal_verify_peer_certificate( + ssl, + X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT); + + if (x509_verify_error == X509_V_OK) + return (struct us_bun_verify_error_t) { .error = x509_verify_error, .code = NULL, .reason = NULL }; + + const char* reason = X509_verify_cert_error_string(x509_verify_error); + const char* code = us_X509_error_code(x509_verify_error); + + return (struct us_bun_verify_error_t) { .error = x509_verify_error, .code = code, .reason = reason }; +} + +int us_verify_callback(int preverify_ok, X509_STORE_CTX* ctx) { + // From https://www.openssl.org/docs/man1.1.1/man3/SSL_verify_cb: + // + // If VerifyCallback returns 1, the verification process is continued. If + // VerifyCallback always returns 1, the TLS/SSL handshake will not be + // terminated with respect to verification failures and the connection will + // be established. The calling process can however retrieve the error code + // of the last verification error using SSL_get_verify_result(3) or by + // maintaining its own error storage managed by VerifyCallback. + // + // Since we cannot perform I/O quickly enough with X509_STORE_CTX_ APIs in + // this callback, we ignore all preverify_ok errors and let the handshake + // continue. It is imperative that the user use Connection::VerifyError after + // the 'secure' callback has been made. + return 1; +} + +SSL_CTX *create_ssl_context_from_bun_options(struct us_bun_socket_context_options_t options) { + /* Create the context */ + SSL_CTX *ssl_context = SSL_CTX_new(TLS_method()); + + /* Default options we rely on - changing these will break our logic */ + SSL_CTX_set_read_ahead(ssl_context, 1); + SSL_CTX_set_mode(ssl_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + + /* Anything below TLS 1.2 is disabled */ + SSL_CTX_set_min_proto_version(ssl_context, TLS1_2_VERSION); + + /* The following are helpers. You may easily implement whatever you want by using the native handle directly */ + + /* Important option for lowering memory usage, but lowers performance slightly */ + if (options.ssl_prefer_low_memory_usage) { + SSL_CTX_set_mode(ssl_context, SSL_MODE_RELEASE_BUFFERS); + } + + + if (options.passphrase) { + /* When freeing the CTX we need to check SSL_CTX_get_default_passwd_cb_userdata and + * free it if set */ + SSL_CTX_set_default_passwd_cb_userdata(ssl_context, (void *) strdup(options.passphrase)); + SSL_CTX_set_default_passwd_cb(ssl_context, passphrase_cb); + } + + /* This one most probably do not need the cert_file_name string to be kept alive */ + if (options.cert_file_name) { + if (SSL_CTX_use_certificate_chain_file(ssl_context, options.cert_file_name) != 1) { + free_ssl_context(ssl_context); + return NULL; + } + } else if (options.cert && options.cert_count > 0) { + for(unsigned int i = 0; i < options.cert_count; i++) { + if (us_ssl_ctx_use_certificate_chain(ssl_context, options.cert[i]) != 1) { + free_ssl_context(ssl_context); + return NULL; + } + } + } + + /* Same as above - we can discard this string afterwards I suppose */ + if (options.key_file_name) { + if (SSL_CTX_use_PrivateKey_file(ssl_context, options.key_file_name, SSL_FILETYPE_PEM) != 1) { + free_ssl_context(ssl_context); + return NULL; + } + } else if (options.key && options.key_count > 0) { + for(unsigned int i = 0; i < options.key_count; i++){ + if (us_ssl_ctx_use_privatekey_content(ssl_context, options.key[i], SSL_FILETYPE_PEM) != 1) { + free_ssl_context(ssl_context); + return NULL; + } + } + } + + if (options.ca_file_name) { + SSL_CTX_set_cert_store(ssl_context, us_get_default_ca_store()); + + STACK_OF(X509_NAME) *ca_list; + ca_list = SSL_load_client_CA_file(options.ca_file_name); + if(ca_list == NULL) { + free_ssl_context(ssl_context); + return NULL; + } + + SSL_CTX_set_client_CA_list(ssl_context, ca_list); + if (SSL_CTX_load_verify_locations(ssl_context, options.ca_file_name, NULL) != 1) { + free_ssl_context(ssl_context); + return NULL; + } + + if(options.reject_unauthorized) { + SSL_CTX_set_verify(ssl_context, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, us_verify_callback); + } else { + SSL_CTX_set_verify(ssl_context, SSL_VERIFY_PEER, us_verify_callback); + } + + }else if (options.ca && options.ca_count > 0) { + X509_STORE* cert_store = NULL; + + for(unsigned int i = 0; i < options.ca_count; i++){ + X509* ca_cert = us_ssl_ctx_get_X509_from(ssl_context, options.ca[i]); + if (ca_cert == NULL){ + free_ssl_context(ssl_context); + return NULL; + } + + if (cert_store == NULL) { + cert_store = us_get_default_ca_store(); + SSL_CTX_set_cert_store(ssl_context, cert_store); + } + + X509_STORE_add_cert(cert_store, ca_cert); + if(!SSL_CTX_add_client_CA(ssl_context, ca_cert)){ + free_ssl_context(ssl_context); + return NULL; + } + if(options.reject_unauthorized) { + SSL_CTX_set_verify(ssl_context, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, us_verify_callback); + } else { + SSL_CTX_set_verify(ssl_context, SSL_VERIFY_PEER, us_verify_callback); + } + } + } else { + if(options.request_cert) { + SSL_CTX_set_cert_store(ssl_context, us_get_default_ca_store()); + + if(options.reject_unauthorized) { + SSL_CTX_set_verify(ssl_context, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, us_verify_callback); + } else { + SSL_CTX_set_verify(ssl_context, SSL_VERIFY_PEER, us_verify_callback); + } + } + } + if (options.dh_params_file_name) { + /* Set up ephemeral DH parameters. */ + DH *dh_2048 = NULL; + FILE *paramfile; + paramfile = fopen(options.dh_params_file_name, "r"); + + if (paramfile) { + dh_2048 = PEM_read_DHparams(paramfile, NULL, NULL, NULL); + fclose(paramfile); + } else { + free_ssl_context(ssl_context); + return NULL; + } + + if (dh_2048 == NULL) { + free_ssl_context(ssl_context); + return NULL; + } + + const long set_tmp_dh = SSL_CTX_set_tmp_dh(ssl_context, dh_2048); + DH_free(dh_2048); + + if (set_tmp_dh != 1) { + free_ssl_context(ssl_context); + return NULL; + } + + /* OWASP Cipher String 'A+' (https://www.owasp.org/index.php/TLS_Cipher_String_Cheat_Sheet) */ + if (SSL_CTX_set_cipher_list(ssl_context, "DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256") != 1) { + free_ssl_context(ssl_context); + return NULL; + } + } + + if (options.ssl_ciphers) { + if (SSL_CTX_set_cipher_list(ssl_context, options.ssl_ciphers) != 1) { + free_ssl_context(ssl_context); + return NULL; + } + } + + if (options.secure_options) { + SSL_CTX_set_options(ssl_context, options.secure_options); + } + + /* This must be free'd with free_ssl_context, not SSL_CTX_free */ + return ssl_context; +} + +/* Returns a servername's userdata if any */ +void *us_internal_ssl_socket_context_find_server_name_userdata(struct us_internal_ssl_socket_context_t *context, const char *hostname_pattern) { + + /* We can use sni_find because looking up a "wildcard pattern" will match the exact literal "wildcard pattern" first, + * before it matches by the very wildcard itself, so it works fine (exact match is the only thing we care for here) */ + SSL_CTX *ssl_context = sni_find(context->sni, hostname_pattern); + + if (ssl_context) { + return SSL_CTX_get_ex_data(ssl_context, 0); + } + + return 0; +} + +/* Returns either nullptr or the previously set user data attached to this SSL's selected SNI context */ +void *us_internal_ssl_socket_get_sni_userdata(struct us_internal_ssl_socket_t *s) { + return SSL_CTX_get_ex_data(SSL_get_SSL_CTX(s->ssl), 0); +} + +/* Todo: return error on failure? */ +void us_internal_ssl_socket_context_add_server_name(struct us_internal_ssl_socket_context_t *context, const char *hostname_pattern, struct us_socket_context_options_t options, void *user) { + + /* Try and construct an SSL_CTX from options */ + SSL_CTX *ssl_context = create_ssl_context_from_options(options); + + /* Attach the user data to this context */ + if (1 != SSL_CTX_set_ex_data(ssl_context, 0, user)) { + printf("CANNOT SET EX DATA!\n"); + } + + /* We do not want to hold any nullptr's in our SNI tree */ + if (ssl_context) { + if (sni_add(context->sni, hostname_pattern, ssl_context)) { + /* If we already had that name, ignore */ + free_ssl_context(ssl_context); + } + } +} + +void us_bun_internal_ssl_socket_context_add_server_name(struct us_internal_ssl_socket_context_t *context, const char *hostname_pattern, struct us_bun_socket_context_options_t options, void *user) { + + /* Try and construct an SSL_CTX from options */ + SSL_CTX *ssl_context = create_ssl_context_from_bun_options(options); + + /* Attach the user data to this context */ + if (1 != SSL_CTX_set_ex_data(ssl_context, 0, user)) { + printf("CANNOT SET EX DATA!\n"); + } + + /* We do not want to hold any nullptr's in our SNI tree */ + if (ssl_context) { + if (sni_add(context->sni, hostname_pattern, ssl_context)) { + /* If we already had that name, ignore */ + free_ssl_context(ssl_context); + } + } +} + +void us_internal_ssl_socket_context_on_server_name(struct us_internal_ssl_socket_context_t *context, void (*cb)(struct us_internal_ssl_socket_context_t *, const char *hostname)) { + context->on_server_name = cb; +} + +void us_internal_ssl_socket_context_remove_server_name(struct us_internal_ssl_socket_context_t *context, const char *hostname_pattern) { + + /* The same thing must happen for sni_free, that's why we have a callback */ + SSL_CTX *sni_node_ssl_context = (SSL_CTX *) sni_remove(context->sni, hostname_pattern); + free_ssl_context(sni_node_ssl_context); +} + +/* Returns NULL or SSL_CTX. May call missing server name callback */ +SSL_CTX *resolve_context(struct us_internal_ssl_socket_context_t *context, const char *hostname) { + + /* Try once first */ + void *user = sni_find(context->sni, hostname); + if (!user) { + /* Emit missing hostname then try again */ + if (!context->on_server_name) { + /* We have no callback registered, so fail */ + return NULL; + } + + context->on_server_name(context, hostname); + + /* Last try */ + user = sni_find(context->sni, hostname); + } + + return user; +} + +// arg is context +int sni_cb(SSL *ssl, int *al, void *arg) { + + if (ssl) { + const char *hostname = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (hostname && hostname[0]) { + /* Try and resolve (match) required hostname with what we have registered */ + SSL_CTX *resolved_ssl_context = resolve_context((struct us_internal_ssl_socket_context_t *) arg, hostname); + if (resolved_ssl_context) { + //printf("Did find matching SNI context for hostname: <%s>!\n", hostname); + SSL_set_SSL_CTX(ssl, resolved_ssl_context); + } else { + /* Call a blocking callback notifying of missing context */ + } + + } + + return SSL_TLSEXT_ERR_OK; + } + + /* Can we even come here ever? */ + return SSL_TLSEXT_ERR_NOACK; +} + +struct us_internal_ssl_socket_context_t *us_internal_create_ssl_socket_context(struct us_loop_t *loop, int context_ext_size, struct us_socket_context_options_t options) { + /* If we haven't initialized the loop data yet, do so . + * This is needed because loop data holds shared OpenSSL data and + * the function is also responsible for initializing OpenSSL */ + us_internal_init_loop_ssl_data(loop); + + /* First of all we try and create the SSL context from options */ + SSL_CTX *ssl_context = create_ssl_context_from_options(options); + if (!ssl_context) { + /* We simply fail early if we cannot even create the OpenSSL context */ + return NULL; + } + + /* Otherwise ee continue by creating a non-SSL context, but with larger ext to hold our SSL stuff */ + struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_create_socket_context(0, loop, sizeof(struct us_internal_ssl_socket_context_t) + context_ext_size, options); + + /* I guess this is the only optional callback */ + context->on_server_name = NULL; + + /* Then we extend its SSL parts */ + context->ssl_context = ssl_context;//create_ssl_context_from_options(options); + context->is_parent = 1; + + context->pending_handshake = 0; + context->on_handshake = NULL; + context->handshake_data = NULL; + + /* We, as parent context, may ignore data */ + context->sc.is_low_prio = (int (*)(struct us_socket_t *)) ssl_is_low_prio; + + /* Parent contexts may use SNI */ + SSL_CTX_set_tlsext_servername_callback(context->ssl_context, sni_cb); + SSL_CTX_set_tlsext_servername_arg(context->ssl_context, context); + + /* Also create the SNI tree */ + context->sni = sni_new(); + + return context; +} +struct us_internal_ssl_socket_context_t *us_internal_bun_create_ssl_socket_context(struct us_loop_t *loop, int context_ext_size, struct us_bun_socket_context_options_t options) { + /* If we haven't initialized the loop data yet, do so . + * This is needed because loop data holds shared OpenSSL data and + * the function is also responsible for initializing OpenSSL */ + us_internal_init_loop_ssl_data(loop); + + /* First of all we try and create the SSL context from options */ + SSL_CTX *ssl_context = create_ssl_context_from_bun_options(options); + if (!ssl_context) { + /* We simply fail early if we cannot even create the OpenSSL context */ + return NULL; + } + + /* Otherwise ee continue by creating a non-SSL context, but with larger ext to hold our SSL stuff */ + struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_create_bun_socket_context(0, loop, sizeof(struct us_internal_ssl_socket_context_t) + context_ext_size, options); + + /* I guess this is the only optional callback */ + context->on_server_name = NULL; + + /* Then we extend its SSL parts */ + context->ssl_context = ssl_context;//create_ssl_context_from_options(options); + context->is_parent = 1; + context->pending_handshake = 0; + context->on_handshake = NULL; + context->handshake_data = NULL; + + /* We, as parent context, may ignore data */ + context->sc.is_low_prio = (int (*)(struct us_socket_t *)) ssl_is_low_prio; + + /* Parent contexts may use SNI */ + SSL_CTX_set_tlsext_servername_callback(context->ssl_context, sni_cb); + SSL_CTX_set_tlsext_servername_arg(context->ssl_context, context); + + /* Also create the SNI tree */ + context->sni = sni_new(); + + return context; +} + +/* Our destructor for hostnames, used below */ +void sni_hostname_destructor(void *user) { + /* Some nodes hold null, so this one must ignore this case */ + free_ssl_context((SSL_CTX *) user); +} + +void us_internal_ssl_socket_context_free(struct us_internal_ssl_socket_context_t *context) { + /* If we are parent then we need to free our OpenSSL context */ + if (context->is_parent) { + free_ssl_context(context->ssl_context); + + /* Here we need to register a temporary callback for all still-existing hostnames + * and their contexts. Only parents have an SNI tree */ + sni_free(context->sni, sni_hostname_destructor); + } + + us_socket_context_free(0, &context->sc); +} + +struct us_listen_socket_t *us_internal_ssl_socket_context_listen(struct us_internal_ssl_socket_context_t *context, const char *host, int port, int options, int socket_ext_size) { + return us_socket_context_listen(0, &context->sc, host, port, options, sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) + socket_ext_size); +} + +struct us_listen_socket_t *us_internal_ssl_socket_context_listen_unix(struct us_internal_ssl_socket_context_t *context, const char *path, int options, int socket_ext_size) { + return us_socket_context_listen_unix(0, &context->sc, path, options, sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) + socket_ext_size); +} + +struct us_internal_ssl_socket_t *us_internal_ssl_socket_context_connect(struct us_internal_ssl_socket_context_t *context, const char *host, int port, const char *source_host, int options, int socket_ext_size) { + return (struct us_internal_ssl_socket_t *) us_socket_context_connect(0, &context->sc, host, port, source_host, options, sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) + socket_ext_size); +} + +struct us_internal_ssl_socket_t *us_internal_ssl_socket_context_connect_unix(struct us_internal_ssl_socket_context_t *context, const char *server_path, int options, int socket_ext_size) { + return (struct us_internal_ssl_socket_t *) us_socket_context_connect_unix(0, &context->sc, server_path, options, sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) + socket_ext_size); +} + +void us_internal_ssl_socket_context_on_open(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *(*on_open)(struct us_internal_ssl_socket_t *s, int is_client, char *ip, int ip_length)) { + us_socket_context_on_open(0, &context->sc, (struct us_socket_t *(*)(struct us_socket_t *, int, char *, int)) ssl_on_open); + context->on_open = on_open; +} + +void us_internal_ssl_socket_context_on_close(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *(*on_close)(struct us_internal_ssl_socket_t *s, int code, void *reason)) { + us_socket_context_on_close(0, (struct us_socket_context_t *) context, (struct us_socket_t *(*)(struct us_socket_t *, int, void *)) ssl_on_close); + context->on_close = on_close; +} + +void us_internal_ssl_socket_context_on_data(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *(*on_data)(struct us_internal_ssl_socket_t *s, char *data, int length)) { + us_socket_context_on_data(0, (struct us_socket_context_t *) context, (struct us_socket_t *(*)(struct us_socket_t *, char *, int)) ssl_on_data); + context->on_data = on_data; +} + +void us_internal_ssl_socket_context_on_writable(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *(*on_writable)(struct us_internal_ssl_socket_t *s)) { + us_socket_context_on_writable(0, (struct us_socket_context_t *) context, (struct us_socket_t *(*)(struct us_socket_t *)) ssl_on_writable); + context->on_writable = on_writable; +} + +void us_internal_ssl_socket_context_on_timeout(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *(*on_timeout)(struct us_internal_ssl_socket_t *s)) { + us_socket_context_on_timeout(0, (struct us_socket_context_t *) context, (struct us_socket_t *(*)(struct us_socket_t *)) on_timeout); +} + +void us_internal_ssl_socket_context_on_long_timeout(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *(*on_long_timeout)(struct us_internal_ssl_socket_t *s)) { + us_socket_context_on_long_timeout(0, (struct us_socket_context_t *) context, (struct us_socket_t *(*)(struct us_socket_t *)) on_long_timeout); +} + +/* We do not really listen to passed FIN-handler, we entirely override it with our handler since SSL doesn't really have support for half-closed sockets */ +void us_internal_ssl_socket_context_on_end(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *(*on_end)(struct us_internal_ssl_socket_t *)) { + us_socket_context_on_end(0, (struct us_socket_context_t *) context, (struct us_socket_t *(*)(struct us_socket_t *)) ssl_on_end); +} + +void us_internal_ssl_socket_context_on_connect_error(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *(*on_connect_error)(struct us_internal_ssl_socket_t *, int code)) { + us_socket_context_on_connect_error(0, (struct us_socket_context_t *) context, (struct us_socket_t *(*)(struct us_socket_t *, int)) on_connect_error); +} + +void *us_internal_ssl_socket_context_ext(struct us_internal_ssl_socket_context_t *context) { + return context + 1; +} + +/* Per socket functions */ +void *us_internal_ssl_socket_get_native_handle(struct us_internal_ssl_socket_t *s) { + return s->ssl; +} + +int us_internal_ssl_socket_raw_write(struct us_internal_ssl_socket_t *s, const char *data, int length, int msg_more) { + + if (us_socket_is_closed(0, &s->s) || us_internal_ssl_socket_is_shut_down(s)) { + return 0; + } + return us_socket_write(0, &s->s, data, length, msg_more); +} + +int us_internal_ssl_socket_write(struct us_internal_ssl_socket_t *s, const char *data, int length, int msg_more) { + + + if (us_socket_is_closed(0, &s->s) || us_internal_ssl_socket_is_shut_down(s)) { + return 0; + } + + struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s); + + struct us_loop_t *loop = us_socket_context_loop(0, &context->sc); + struct loop_ssl_data *loop_ssl_data = (struct loop_ssl_data *) loop->data.ssl_data; + + // it makes literally no sense to touch this here! it should start at 0 and ONLY be set and reset by the on_data function! + // the way is is now, triggering a write from a read will essentially delete all input data! + // what we need to do is to check if this ever is non-zero and print a warning + + + + loop_ssl_data->ssl_read_input_length = 0; + + + loop_ssl_data->ssl_socket = &s->s; + loop_ssl_data->msg_more = msg_more; + loop_ssl_data->last_write_was_msg_more = 0; + //printf("Calling SSL_write\n"); + int written = SSL_write(s->ssl, data, length); + //printf("Returning from SSL_write\n"); + loop_ssl_data->msg_more = 0; + + if (loop_ssl_data->last_write_was_msg_more && !msg_more) { + us_socket_flush(0, &s->s); + } + + if (written > 0) { + return written; + } else { + int err = SSL_get_error(s->ssl, written); + if (err == SSL_ERROR_WANT_READ) { + // here we need to trigger writable event next ssl_read! + s->ssl_write_wants_read = 1; + } else if (err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL) { + // these two errors may add to the error queue, which is per thread and must be cleared + ERR_clear_error(); + + // all errors here except for want write are critical and should not happen + } + + return 0; + } +} + +void *us_internal_ssl_socket_ext(struct us_internal_ssl_socket_t *s) { + return s + 1; +} + +int us_internal_ssl_socket_is_shut_down(struct us_internal_ssl_socket_t *s) { + return us_socket_is_shut_down(0, &s->s) || SSL_get_shutdown(s->ssl) & SSL_SENT_SHUTDOWN; +} + +void us_internal_ssl_socket_shutdown(struct us_internal_ssl_socket_t *s) { + if (!us_socket_is_closed(0, &s->s) && !us_internal_ssl_socket_is_shut_down(s)) { + struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s); + struct us_loop_t *loop = us_socket_context_loop(0, &context->sc); + struct loop_ssl_data *loop_ssl_data = (struct loop_ssl_data *) loop->data.ssl_data; + + // also makes no sense to touch this here! + // however the idea is that if THIS socket is not the same as ssl_socket then this data is not for me + // but this is not correct as it is currently anyways, any data available should be properly reset + loop_ssl_data->ssl_read_input_length = 0; + + + // essentially we need two of these: one for CURRENT CALL and one for CURRENT SOCKET WITH DATA + // if those match in the BIO function then you may read, if not then you may not read + // we need ssl_read_socket to be set in on_data and checked in the BIO + loop_ssl_data->ssl_socket = &s->s; + + + loop_ssl_data->msg_more = 0; + + // sets SSL_SENT_SHUTDOWN no matter what (not actually true if error!) + int ret = SSL_shutdown(s->ssl); + if (ret == 0) { + ret = SSL_shutdown(s->ssl); + } + + if (ret < 0) { + + int err = SSL_get_error(s->ssl, ret); + if (err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL) { + // clear + ERR_clear_error(); + } + + // we get here if we are shutting down while still in init + us_socket_shutdown(0, &s->s); + } + } +} + +struct us_internal_ssl_socket_t *us_internal_ssl_socket_context_adopt_socket(struct us_internal_ssl_socket_context_t *context, struct us_internal_ssl_socket_t *s, int ext_size) { + // todo: this is completely untested + return (struct us_internal_ssl_socket_t *) us_socket_context_adopt_socket(0, &context->sc, &s->s, sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) + ext_size); +} + + + +struct us_internal_ssl_socket_t * ssl_wrapped_context_on_close(struct us_internal_ssl_socket_t *s, int code, void *reason) { + struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s); + struct us_wrapped_socket_context_t* wrapped_context = (struct us_wrapped_socket_context_t *)us_internal_ssl_socket_context_ext(context); + + if (wrapped_context->events.on_close) { + wrapped_context->events.on_close((struct us_socket_t*)s, code, reason); + } + + // writting here can cause the context to not be writable anymore but its the user responsability to check for that + if (wrapped_context->old_events.on_close) { + wrapped_context->old_events.on_close((struct us_socket_t*)s, code, reason); + } + + return s; +} + + +struct us_internal_ssl_socket_t * ssl_wrapped_context_on_writable(struct us_internal_ssl_socket_t *s) { + struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s); + struct us_wrapped_socket_context_t* wrapped_context = (struct us_wrapped_socket_context_t *)us_internal_ssl_socket_context_ext(context); + + if (wrapped_context->events.on_writable) { + wrapped_context->events.on_writable((struct us_socket_t*)s); + } + + // writting here can cause the context to not be writable anymore but its the user responsability to check for that + if (wrapped_context->old_events.on_writable) { + wrapped_context->old_events.on_writable((struct us_socket_t*)s); + } + + return s; +} + + +struct us_internal_ssl_socket_t * ssl_wrapped_context_on_data(struct us_internal_ssl_socket_t *s, char *data, int length) { + struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s); + struct us_wrapped_socket_context_t* wrapped_context = (struct us_wrapped_socket_context_t *)us_internal_ssl_socket_context_ext(context); + // raw data if needed + if (wrapped_context->old_events.on_data) { + wrapped_context->old_events.on_data((struct us_socket_t*)s, data, length); + } + // ssl wrapped data + return ssl_on_data(s, data, length); +} + +struct us_internal_ssl_socket_t * ssl_wrapped_context_on_timeout(struct us_internal_ssl_socket_t * s) { + struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s); + struct us_wrapped_socket_context_t* wrapped_context = (struct us_wrapped_socket_context_t *)us_internal_ssl_socket_context_ext(context); + + if (wrapped_context->events.on_timeout) { + wrapped_context->events.on_timeout((struct us_socket_t*)s); + } + + if (wrapped_context->old_events.on_timeout) { + wrapped_context->old_events.on_timeout((struct us_socket_t*)s); + } + + return s; +} + +struct us_internal_ssl_socket_t * ssl_wrapped_context_on_long_timeout(struct us_internal_ssl_socket_t * s) { + struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s); + struct us_wrapped_socket_context_t* wrapped_context = (struct us_wrapped_socket_context_t *)us_internal_ssl_socket_context_ext(context); + + if (wrapped_context->events.on_long_timeout) { + wrapped_context->events.on_long_timeout((struct us_socket_t*)s); + } + + if (wrapped_context->old_events.on_long_timeout) { + wrapped_context->old_events.on_long_timeout((struct us_socket_t*)s); + } + + return s; +} + +struct us_internal_ssl_socket_t * ssl_wrapped_context_on_end(struct us_internal_ssl_socket_t * s) { + struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s); + struct us_wrapped_socket_context_t* wrapped_context = (struct us_wrapped_socket_context_t *)us_internal_ssl_socket_context_ext(context); + + if (wrapped_context->events.on_end) { + wrapped_context->events.on_end((struct us_socket_t*)s); + } + + if (wrapped_context->old_events.on_end) { + wrapped_context->old_events.on_end((struct us_socket_t*)s); + } + return s; +} + +struct us_internal_ssl_socket_t * ssl_wrapped_on_connect_error(struct us_internal_ssl_socket_t * s, int code) { + struct us_internal_ssl_socket_context_t *context = (struct us_internal_ssl_socket_context_t *) us_socket_context(0, &s->s); + struct us_wrapped_socket_context_t* wrapped_context = (struct us_wrapped_socket_context_t *)us_internal_ssl_socket_context_ext(context); + + if (wrapped_context->events.on_connect_error) { + wrapped_context->events.on_connect_error((struct us_socket_t*)s, code); + } + + if (wrapped_context->old_events.on_connect_error) { + wrapped_context->old_events.on_connect_error((struct us_socket_t*)s, code); + } + return s; +} + +struct us_internal_ssl_socket_t* us_internal_ssl_socket_open(struct us_internal_ssl_socket_t * s, int is_client, char* ip, int ip_length) { + // closed + if (us_socket_is_closed(0, &s->s)) { + return s; + } + // already opened + if (s->ssl) return s; + + // start SSL open + return ssl_on_open(s, is_client, ip, ip_length); +} + +struct us_internal_ssl_socket_t *us_internal_ssl_socket_wrap_with_tls(struct us_socket_t *s, struct us_bun_socket_context_options_t options, struct us_socket_events_t events, int socket_ext_size) { + /* Cannot wrap a closed socket */ + if (us_socket_is_closed(0, s)) { + return NULL; + } + + struct us_socket_context_t * old_context = us_socket_context(0, s); + + struct us_socket_context_t * context = us_create_bun_socket_context(1, old_context->loop, sizeof(struct us_wrapped_socket_context_t), options); + struct us_internal_ssl_socket_context_t *tls_context = (struct us_internal_ssl_socket_context_t *) context; + + struct us_wrapped_socket_context_t* wrapped_context = (struct us_wrapped_socket_context_t *)us_internal_ssl_socket_context_ext(tls_context); + // we need to fire this events on the old context + struct us_socket_events_t old_events = (struct us_socket_events_t) { + .on_close = old_context->on_close, + .on_data = old_context->on_data, + .on_writable = old_context->on_writable, + .on_timeout = old_context->on_socket_timeout, + .on_long_timeout = old_context->on_socket_long_timeout, + .on_end = old_context->on_end, + .on_connect_error = old_context->on_connect_error, + }; + wrapped_context->old_events = old_events; + wrapped_context->events = events; + + + // no need to wrap open because socket is already open (only new context will be called so we can configure hostname and ssl stuff normally here before handshake) + tls_context->on_open = (struct us_internal_ssl_socket_t *(*)(struct us_internal_ssl_socket_t *, int, char *, int))events.on_open; + + // on handshake is not available on the old context so we just add this + if(events.on_handshake){ + us_internal_on_ssl_handshake(tls_context, (void (*)(struct us_internal_ssl_socket_t *, int, struct us_bun_verify_error_t, void*))events.on_handshake, NULL); + } + + // we need to wrap these events because we need to call the old context events as well + us_socket_context_on_connect_error(0, context, (struct us_socket_t *(*)(struct us_socket_t *, int)) ssl_wrapped_on_connect_error); + us_socket_context_on_end(0, context, (struct us_socket_t *(*)(struct us_socket_t *)) ssl_wrapped_context_on_end); + us_socket_context_on_long_timeout(0, context, (struct us_socket_t *(*)(struct us_socket_t *)) ssl_wrapped_context_on_long_timeout); + us_socket_context_on_timeout(0, context, (struct us_socket_t *(*)(struct us_socket_t *)) ssl_wrapped_context_on_timeout); + + // special case this will be called after ssl things are done + + // called from ssl_on_data handler is called inside ssl_wrapped_context_on_data + tls_context->on_data = (struct us_internal_ssl_socket_t *(*)(struct us_internal_ssl_socket_t *, char *, int))events.on_data; + us_socket_context_on_data(0, context, (struct us_socket_t *(*)(struct us_socket_t *, char *, int)) ssl_wrapped_context_on_data); + + // here is the inverse of the above ssl_on_writable will call ssl_wrapped_context_on_writable + tls_context->on_writable = ssl_wrapped_context_on_writable; + us_socket_context_on_writable(0, context, (struct us_socket_t *(*)(struct us_socket_t *)) ssl_on_writable); + + tls_context->on_close = ssl_wrapped_context_on_close; + us_socket_context_on_close(0, context, (struct us_socket_t *(*)(struct us_socket_t *, int, void *)) ssl_on_close); + + // will resize to tls + ext size + struct us_internal_ssl_socket_t * socket = (struct us_internal_ssl_socket_t *) us_socket_context_adopt_socket(0, context, s, sizeof(struct us_internal_ssl_socket_t) - sizeof(struct us_socket_t) + socket_ext_size); + socket->ssl = NULL; + socket->ssl_write_wants_read = 0; + socket->ssl_read_wants_write = 0; + + return socket; +} + +#endif diff --git a/packages/bun-usockets/src/crypto/root_certs.h b/packages/bun-usockets/src/crypto/root_certs.h new file mode 100644 index 000000000..0eacc2b45 --- /dev/null +++ b/packages/bun-usockets/src/crypto/root_certs.h @@ -0,0 +1,3438 @@ +// Maintaining the root certificates +// +// `src/crypto/root_certs.h` contains a compiled-in set of root certificates used as trust anchors +// for TLS certificate validation. +// +// The certificates come from Mozilla, specifically NSS's `certdata.txt` file. +// +// The PEM encodings of the certificates are converted to C strings, and committed +// in `src/crypto/root_certs.h`. +// +// When to update +// +// Root certificates should be updated sometime after Mozilla makes an NSS release, +// check the NSS release schedule in https://wiki.mozilla.org/NSS:Release_Versions. +// +// Process +// +// The `generate-root-certs.js` script automates the update of +// the root certificates, including: +// +// * Downloading `certdata.txt` from Mozilla's source control repository. +// * Running `generate-ca-bundle.pl` to convert the certificates and generate +// `src/crypto/root_certs.h`. +// * Using `git diff-files` to determine which certificate have been added and/or +// removed. +// +#include "libusockets.h" +static struct us_cert_string_t root_certs[] = { + +/* GlobalSign Root CA */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMC\n" +"QkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNV\n" +"BAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBa\n" +"MFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdS\n" +"b290IENBMRswGQYDVQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUA\n" +"A4IBDwAwggEKAoIBAQDaDuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtI\n" +"K+6NiY6arymAZavpxy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCO\n" +"XkNz8kHp1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG\n" +"snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3\n" +"dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DP\n" +"AgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRg\n" +"e2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUFAAOCAQEA1nPnfE920I2/7LqivjTF\n" +"KDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY7\n" +"76BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9\n" +"LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr\n" +"+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME\n" +"HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==\n" +"-----END CERTIFICATE-----",.len=1258}, + +/* Entrust.net Premium 2048 Secure Server CA */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVz\n" +"dC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJl\n" +"Zi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0\n" +"ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4\n" +"KTAeFw05OTEyMjQxNzUwNTFaFw0yOTA3MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0\n" +"Lm5ldDFAMD4GA1UECxQ3d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVm\n" +"LiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRl\n" +"ZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp\n" +"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtK\n" +"TY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/EC\n" +"DNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ\n" +"/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzWnLLPKQP5L6RQstRIzgUyVYr9smRM\n" +"DuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVC\n" +"wQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/\n" +"BAUwAwEB/zAdBgNVHQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJKoZIhvcNAQEFBQAD\n" +"ggEBADubj1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPyT/4xmf3IDExo\n" +"U8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6YfzX1XEC+b\n" +"BAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5bu/8j72gZyxKTJ1wD\n" +"LW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+bYQLCIt+jerXmCHG8+c8eS9e\n" +"nNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/ErfF6adulZkMV8gzURZVE=\n" +"-----END CERTIFICATE-----",.len=1501}, + +/* Baltimore CyberTrust Root */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJRTESMBAG\n" +"A1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYDVQQDExlCYWx0aW1v\n" +"cmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjEL\n" +"MAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEi\n" +"MCAGA1UEAxMZQmFsdGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQAD\n" +"ggEPADCCAQoCggEBAKMEuyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2ygu\n" +"zmKiYv60iNoS6zjrIZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo\n" +"6vWrJYeKmpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu\n" +"XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3z\n" +"yZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkC\n" +"AwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1BE3wMBIGA1UdEwEB/wQIMAYB\n" +"Af8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27\n" +"TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukM\n" +"JY2GQE/szKN+OMY3EU/t3WgxjkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhS\n" +"NzkE1akxehi/oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67\n" +"G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS\n" +"R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp\n" +"-----END CERTIFICATE-----",.len=1258}, + +/* Entrust Root Certification Authority */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAU\n" +"BgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMg\n" +"aW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwg\n" +"SW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4X\n" +"DTA2MTEyNzIwMjM0MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQK\n" +"Ew1FbnRydXN0LCBJbmMuMTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29y\n" +"cG9yYXRlZCBieSByZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4x\n" +"LTArBgNVBAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ\n" +"KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poB\n" +"j6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypo\n" +"wCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+\n" +"SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rV\n" +"vDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2\n" +"HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB\n" +"/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSME\n" +"GDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE\n" +"vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4IBAQCT\n" +"1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISMY/YP\n" +"yyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa\n" +"v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE\n" +"2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPc\n" +"j2A781q0tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8\n" +"-----END CERTIFICATE-----",.len=1639}, + +/* Comodo AAA Services root */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UE\n" +"CAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21v\n" +"ZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0\n" +"MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdy\n" +"ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENB\n" +"IExpbWl0ZWQxITAfBgNVBAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZI\n" +"hvcNAQEBBQADggEPADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686td\n" +"UIoWMQuaBtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe\n" +"3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8Ioa\n" +"E+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULi\n" +"mAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cmez6KJcfA3Z3mNWgQIJ2P2N7S\n" +"w4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYD\n" +"VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDov\n" +"L2NybC5jb21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0\n" +"dHA6Ly9jcmwuY29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG\n" +"9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q\n" +"GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLzRt0vxuBq\n" +"w8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z8VlI\n" +"MCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C\n" +"12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==\n" +"-----END CERTIFICATE-----",.len=1513}, + +/* QuoVadis Root CA 2 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNV\n" +"BAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMjAeFw0w\n" +"NjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBR\n" +"dW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDIwggIiMA0GCSqG\n" +"SIb3DQEBAQUAA4ICDwAwggIKAoICAQCaGMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4Gt\n" +"Mh6QRr+jhiYaHv5+HBg6XJxgFyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp\n" +"3MJGF/hd/aTa/55JWpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsR\n" +"E8Scd3bBrrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp\n" +"+ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1ksOR1YqI\n" +"0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/iUcw6UwxI5g69ybR2\n" +"BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIizPtGo/KPaHbDRsSNU30R2be1B\n" +"2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og/zOhD7osFRXql7PSorW+8oyWHhqPHWyk\n" +"YTe5hnMz15eWniN9gqRMgeKh0bpnX5UHoycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyP\n" +"ZFwDwzqLID9ujWc9Otb+fVuIyV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQAB\n" +"o4GwMIGtMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwz\n" +"JQTU7tD2A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL\n" +"MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1Zh\n" +"ZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2fBluornFdLwUvZ+YT\n" +"RYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzng/iN/Ae42l9NLmeyhP3ZRPx3\n" +"UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2BlfF/nJrP3MpCYUNQ3cVX2kiF495V5+vgt\n" +"JodmVjB3pjd4M1IQWK4/YY7yarHvGH5KWWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q8\n" +"0m/DShcK+JDSV6IZUaUtl0HaB0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W\n" +"6ZM/57Es3zrWIozchLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQj\n" +"rLhVoQPRTUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD\n" +"mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0ZohEUGW6y\n" +"hhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y4aOTHcyKJloJONDO\n" +"1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza8eOx79+Rj1QqCyXBJhnEUhAF\n" +"ZdWCEOrCMc0u\n" +"-----END CERTIFICATE-----",.len=2037}, + +/* QuoVadis Root CA 3 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNV\n" +"BAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMzAeFw0w\n" +"NjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBR\n" +"dW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDMwggIiMA0GCSqG\n" +"SIb3DQEBAQUAA4ICDwAwggIKAoICAQDMV0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTP\n" +"krgEQK0CSzGrvI2RaNggDhoB4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZ\n" +"z3HmDyl2/7FWeUUrH556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2Objyj\n" +"Ptr7guXd8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv\n" +"vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLTmZw67mta\n" +"a7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhebtfZFG4ZM2mnO4SJ\n" +"k8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjcT5EOE7zelaTfi5m+rJsziO+1\n" +"ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDtWAEXMJPpGovgc2PZapKUSU60rUqFxKMi\n" +"MPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZc6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArl\n" +"zW1oUevaPwV/izLmE1xr/l9A4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQAB\n" +"o4IBlTCCAZEwDwYDVR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMw\n" +"gcUwgZMGCCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0\n" +"aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0aWZpY2F0\n" +"ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVudC4wLQYIKwYBBQUH\n" +"AgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2NwczALBgNVHQ8EBAMCAQYwHQYD\n" +"VR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4GA1UdIwRnMGWAFPLAE+CCQz777i9nMpY1\n" +"XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJCTTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEb\n" +"MBkGA1UEAxMSUXVvVmFkaXMgUm9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62g\n" +"LEz6wPJv92ZVqyM07ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon\n" +"24QRiSemd1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd\n" +"+LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B4f/xI4hR\n" +"OJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadNt54CrnMAyNojA+j5\n" +"6hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6xDYBEwa7TIzdfu4V8K5Iu6H6l\n" +"i92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8S\n" +"h17rRdhs9ZgC06DYVYoGmRmioHfRMJ6szHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7\n" +"j2G4aSgWQgRecCocIdiP4b0jWy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEo\n" +"kt4YutUqKLsRixeTmJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7\n" +"zTSMmfXK4SVhM7JZG+Ju1zdXtg2pEto=\n" +"-----END CERTIFICATE-----",.len=2349}, + +/* Security Communication Root CA */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UE\n" +"ChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJv\n" +"b3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEY\n" +"MBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0\n" +"aW9uIFJvb3RDQTEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8\n" +"V6UMbXaKL0u/ZPtM7orw8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzG\n" +"jGdnSj74cbAZJ6kJDKaVv0uMDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1ae\n" +"V+7AwFb9Ms+k2Y7CI9eNqPPYJayX5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/N\n" +"QV3Is00qVUarH9oe4kA92819uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OV\n" +"YNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZ\n" +"aNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG\n" +"9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g0dNq/vu+m22/xwVtWSDEHPC32oRY\n" +"AmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7K\n" +"aEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKq\n" +"L8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfci\n" +"oU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw==\n" +"-----END CERTIFICATE-----",.len=1221}, + +/* XRamp Global CA Root */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCBgjELMAkG\n" +"A1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJh\n" +"bXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlm\n" +"aWNhdGlvbiBBdXRob3JpdHkwHhcNMDQxMTAxMTcxNDA0WhcNMzUwMTAxMDUzNzE5WjCBgjEL\n" +"MAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMb\n" +"WFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2Vy\n" +"dGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCY\n" +"JB69FbS638eMpSe2OAtp87ZOqCwuIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCP\n" +"KZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMxfoArtYzAQDsRhtDLooY2YKTVMIJt2W7QDxIEM5df\n" +"T2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FEzG+gSqmUsE3a56k0enI4qEHMPJQRfevIpoy3\n" +"hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqsAxcZZPRaJSKNNCyy9mgdEm3Tih4U2sSP\n" +"puIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNViPvryxS3T/dRlAgMBAAGjgZ8wgZwwEwYJ\n" +"KwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O\n" +"BBYEFMZPoj0GY4QJnM5i5ASsjVy16bYbMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwu\n" +"eHJhbXBzZWN1cml0eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQEwDQYJKoZIhvcN\n" +"AQEFBQADggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc/Kh4ZzXxHfAR\n" +"vbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxtqZ4Bfj8p\n" +"zgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLaIR9NmXmd4c8nnxCb\n" +"HIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSyi6mx5O+aGtA9aZnuqCij4Tyz\n" +"8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQO+7ETPTsJ3xCwnR8gooJybQDJbw=\n" +"-----END CERTIFICATE-----",.len=1509}, + +/* Go Daddy Class 2 CA */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEhMB8GA1UE\n" +"ChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAy\n" +"IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3MDYyMFoXDTM0MDYyOTE3MDYy\n" +"MFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjEx\n" +"MC8GA1UECxMoR28gRGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAw\n" +"DQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWiz\n" +"V3GgXne77ZtJ6XCAPVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HF\n" +"iH7Eux6wwdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi\n" +"EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lN\n" +"f4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44\n" +"dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLEsNKR1EwRcbNhyz2h/t2oatTj\n" +"MIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h/t2oatTjoWekZTBjMQswCQYDVQQGEwJV\n" +"UzEhMB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRk\n" +"eSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJ\n" +"KoZIhvcNAQEFBQADggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYX\n" +"MP80kWNyOO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P\n" +"TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQHmyW74cN\n" +"xA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mERdEr/VxqHD3VILs9R\n" +"aRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5CufReYNnyicsbkqWletNw+vHX/b\n" +"vZ8=\n" +"-----END CERTIFICATE-----",.len=1445}, + +/* Starfield Class 2 CA */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzElMCMGA1UE\n" +"ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENs\n" +"YXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNjI5MTczOTE2WhcNMzQwNjI5\n" +"MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2ll\n" +"cywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRo\n" +"b3JpdHkwggEgMA0GCSqGSIb3DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N\n" +"78gDGIc/oav7PKaf8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMe\n" +"j2YcOadN+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0\n" +"X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aaK4Umkhyn\n" +"ArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA1W4TNSNe35tfPe/W\n" +"93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0GA1UdDgQWBBS/X7fRzt0fhvRb\n" +"Vazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fRzt0fhvRbVazc1xDCDqmI56FspGowaDEL\n" +"MAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAw\n" +"BgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwG\n" +"A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1ep\n" +"oXkJKtv3L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D\n" +"eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJlxy16paq8\n" +"U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynpVSJYACPq4xJDKVtH\n" +"CN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEYWQPJIrSPnNVeKtelttQKbfi3\n" +"QBFGmh95DmK/D5fs4C8fF5Q=\n" +"-----END CERTIFICATE-----",.len=1465}, + +/* DigiCert Assured ID Root CA */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYD\n" +"VQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu\n" +"Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAw\n" +"MDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQg\n" +"SW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1\n" +"cmVkIElEIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOA\n" +"XLGH87dg+XESpa7cJpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lT\n" +"XDGEKvYPmDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+\n" +"wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/l\n" +"bQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcX\n" +"xH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQE\n" +"AwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF66Kv9JLLgjEtUYunpyGd823IDzAf\n" +"BgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog68\n" +"3+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqo\n" +"R+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+\n" +"fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx\n" +"H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe+o0bJW1s\n" +"j6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g==\n" +"-----END CERTIFICATE-----",.len=1347}, + +/* DigiCert Global Root CA */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYD\n" +"VQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu\n" +"Y29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBa\n" +"Fw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx\n" +"GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBS\n" +"b290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKP\n" +"C3eQyaKl7hLOllsBCSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscF\n" +"s3YnFo97nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\n" +"43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6g\n" +"SzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSii\n" +"cNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYD\n" +"VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm8KPiGxvDl7I90VUwHwYDVR0jBBgw\n" +"FoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1E\n" +"nE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDi\n" +"qw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBA\n" +"I+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\n" +"YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQkCAUw7C29\n" +"C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\n" +"-----END CERTIFICATE-----",.len=1335}, + +/* DigiCert High Assurance EV Root CA */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYD\n" +"VQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu\n" +"Y29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2\n" +"MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERp\n" +"Z2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNl\n" +"cnQgSGlnaCBBc3N1cmFuY2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\n" +"AQoCggEBAMbM5XPm+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlB\n" +"WTrT3JTWPNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM\n" +"xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeB\n" +"QVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5\n" +"OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsgEsxBu24LUTi4S8sCAwEAAaNj\n" +"MGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9H\n" +"AdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3\n" +"DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1\n" +"ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VH\n" +"MWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2\n" +"Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCevEsXCS+0\n" +"yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K\n" +"-----END CERTIFICATE-----",.len=1363}, + +/* SwissSign Gold CA - G2 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"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\n" +"W8mw0FfB+j564ZfJ\n" +"-----END CERTIFICATE-----",.len=2041}, + +/* SwissSign Silver CA - G2 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"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\n" +"5Aq7KkzrCWA5zspi2C5u\n" +"-----END CERTIFICATE-----",.len=2045}, + +/* SecureTrust CA */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYD\n" +"VQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNl\n" +"Y3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UE\n" +"BhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1\n" +"cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7C\n" +"T8rU4niVWJxB4Q2ZQCQXOZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29\n" +"vo6pQT64lO0pGtSO0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZ\n" +"bf2IzIaowW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj\n" +"7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xH\n" +"CzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIE\n" +"Bh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE\n" +"/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYjaHR0cDovL2NybC5zZWN1cmV0cnVz\n" +"dC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDt\n" +"T0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQ\n" +"f2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cp\n" +"rp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS\n" +"CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR3ItHuuG5\n" +"1WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE=\n" +"-----END CERTIFICATE-----",.len=1347}, + +/* Secure Global CA */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYD\n" +"VQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNl\n" +"Y3VyZSBHbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYD\n" +"VQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNl\n" +"Y3VyZSBHbG9iYWwgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxV\n" +"aQZx5RNoJLNP2MwhR/jxYDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6Mpjh\n" +"HZevj8fcyTiW89sa/FHtaMbQbqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ\n" +"/kG5VacJjnIFHovdRIWCQtBJwB1g8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnI\n" +"HmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYVHDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPi\n" +"XB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGC\n" +"NxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9E\n" +"BMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAnoCWGI2h0dHA6Ly9jcmwuc2VjdXJl\n" +"dHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBBQUAA4IB\n" +"AQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+OYo68+aCiV0BN7OrJKQV\n" +"DpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cnCDpOGR86p1hcF895\n" +"P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/53CYNv6ZHdAbY\n" +"iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xcf8LDmBxr\n" +"ThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW\n" +"-----END CERTIFICATE-----",.len=1351}, + +/* COMODO Certification Authority */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkG\n" +"A1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9y\n" +"ZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZp\n" +"Y2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQsw\n" +"CQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxm\n" +"b3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRp\n" +"ZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECL\n" +"i3LjkRv3UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI\n" +"2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7eu\n" +"NJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC\n" +"8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQF\n" +"ZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVIrLsm9wIDAQABo4GOMIGLMB0GA1Ud\n" +"DgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw\n" +"AwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9D\n" +"ZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5\n" +"t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv\n" +"IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/RxdMosIG\n" +"lgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmcIGfE\n" +"7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN\n" +"+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ==\n" +"-----END CERTIFICATE-----",.len=1485}, + +/* COMODO ECC Certification Authority */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UE\n" +"BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEa\n" +"MBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlm\n" +"aWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTEL\n" +"MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2Fs\n" +"Zm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0Mg\n" +"Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmC\n" +"FYX7deSRFtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J\n" +"cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZ\n" +"SBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggq\n" +"hkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDmfQjGGoe9GKhzvSbKYAydzpmf\n" +"z1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeAU/7dIOA1mjbRxwG55tzd8/8dLDoW\n" +"V9mSOdY=\n" +"-----END CERTIFICATE-----",.len=938}, + +/* Certigna */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZS\n" +"MRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMw\n" +"NVoXDTI3MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczER\n" +"MA8GA1UEAwwIQ2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ\n" +"1tazNHUmgh7stL7qXOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lI\n" +"zw7sebYs5zRLcAglozyHGxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxr\n" +"yIRWijOp5yIVUxbwzBfsV1/pogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJb\n" +"zg4ij02Q130yGLMLLGq/jj8UEYkgDncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2\n" +"JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKfIrjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0T\n" +"AQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQtCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AU\n" +"Gu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlt\n" +"eW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/JSP8wDgYDVR0PAQH/BAQDAgEGMBEG\n" +"CWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEAhQMeknH2Qq/ho2Ge6/PAD/Kl\n" +"1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+ImvaYS5/1HI93TDhHkxA\n" +"GYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1kluPBS1xp81HlDQwY9q\n" +"cEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY1gkIl2PlwS6w\n" +"t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/QwWyH8EZE0\n" +"vkHve52Xdf+XlcCWWC/qu0bXu+TZLg==\n" +"-----END CERTIFICATE-----",.len=1327}, + +/* ePKI Root Certification Authority */ +{.str="-----BEGIN CERTIFICATE-----\n" +"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\n" +"EZw=\n" +"-----END CERTIFICATE-----",.len=2029}, + +/* certSIGN ROOT CA */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREw\n" +"DwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQx\n" +"NzIwMDRaFw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lH\n" +"TjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\n" +"AQoCggEBALczuX7IJUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oq\n" +"rl0Hj0rDKH/v+yv6efHHrfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsA\n" +"fsT8AzNXDe3i+s5dRdY4zTW2ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUo\n" +"Se1b16kQOA7+j0xbm0bqQfWwCHTD0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwv\n" +"JoIQ4uNllAoEwF73XVv4EOLQunpL+943AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNC\n" +"MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPx\n" +"fIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IBAQA+0hyJLjX8+HXd5n9liPRyTMks1zJO\n" +"890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6\n" +"IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0x2DEW8xXjrJ1/RsCCdtZb3KT\n" +"afcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIltvBTjD4au8as+x6AJzKNI\n" +"0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7NzTogVZ96edhBiIL5V\n" +"aZVDADlN9u6wWk5JRFRYX0KD\n" +"-----END CERTIFICATE-----",.len=1173}, + +/* NetLock Arany (Class Gold) FÅ‘tanúsÃtvány */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTER\n" +"MA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFu\n" +"w7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwws\n" +"TmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjEx\n" +"MTUwODIxWhcNMjgxMjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFw\n" +"ZXN0MRUwEwYDVQQKDAxOZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lh\n" +"ZMOzayAoQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkg\n" +"KENsYXNzIEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n" +"MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFtt\n" +"vzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn\n" +"7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5VA1lddkVQZQBr17s9o3x/61k\n" +"/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7\n" +"GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiL\n" +"o0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpn\n" +"k/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ\n" +"5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C\n" +"+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzCbLBQWV2Q\n" +"WzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5KfnaNwUA\n" +"SZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu\n" +"dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E=\n" +"-----END CERTIFICATE-----",.len=1473}, + +/* Hongkong Post Root CA 1 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNV\n" +"BAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4X\n" +"DTAzMDUxNTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT\n" +"DUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjAN\n" +"BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSS\n" +"HSL22oVyaf7XPwnU3ZG1ApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8g\n" +"PW2iNr4joLFutbEnPzlTCeqrauh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7j\n" +"EAaPIpjhZY4bXSNmO7ilMlHIhqqhqZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9\n" +"nnV0ttgCXjqQesBCNnLsak3c78QA3xMYV18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208\n" +"o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNVHRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQE\n" +"AwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7ih9legYsCmEEIjEy82tvuJxuC52pF7BaL\n" +"T4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37piol7Yutmcn1KZJ/RyTZXaeQi/cImya\n" +"T/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5LmeiIAQ32pwL0xch4I+XeTRvhEgC\n" +"IDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88psT/oROhUVRsPNH4NbLUES\n" +"7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilTc4afU9hDDl3WY4Jx\n" +"HYB0yvbiAmvZWg==\n" +"-----END CERTIFICATE-----",.len=1165}, + +/* SecureSign RootCA11 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UE\n" +"ChMiSmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJl\n" +"U2lnbiBSb290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNV\n" +"BAYTAkpQMSswKQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRww\n" +"GgYDVQQDExNTZWN1cmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n" +"CgKCAQEA/XeqpRyQBTvLTJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1y\n" +"fIw/XwFndBWW4wI8h9uuywGOwvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyK\n" +"yiyhFTOVMdrAG/LuYpmGYz+/3ZMqg6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9\n" +"UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rPO7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V\n" +"1uinMrPmmECGxc0nEovMe863ETxiYAcjPitAbpSACW22s293bzUIUPsCh8U+iQIDAQABo0Iw\n" +"QDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZXt94wDgYDVR0PAQH/BAQDAgEGMA8GA1Ud\n" +"EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKChOBZmLqdWHyGcBvod7bkixTgm2E5P\n" +"7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4rbnpwrxYO4wJs+0LmGJ1F2FXI\n" +"6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQOh29Dbx7VFALuUKvVaAY\n" +"ga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01y8hSyn+B/tlr0/cR\n" +"7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061lgeLKBObjBmN\n" +"QSdJQO7e5iNEOdyhIta6A/I=\n" +"-----END CERTIFICATE-----",.len=1246}, + +/* Microsec e-Szigno Root CA 2009 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJI\n" +"VTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMM\n" +"Hk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0Bl\n" +"LXN6aWduby5odTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQG\n" +"EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNV\n" +"BAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5m\n" +"b0BlLXN6aWduby5odTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG\n" +"2KfgQvvPkd6mJviZpWNwrZuuyjNAfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tc\n" +"cbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDH\n" +"QWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKApxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqp\n" +"GrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV\n" +"87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUCAwEAAaOBgDB+MA8GA1UdEwEB/wQF\n" +"MAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAf\n" +"BgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREEFDASgRBpbmZvQGUtc3pp\n" +"Z25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0olZMEyL/azXm4Q5Dw\n" +"pL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfXI/OMn74dseGk\n" +"ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775tyERzAMB\n" +"VnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02yULy\n" +"Mtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi\n" +"LXpUq3DDfSJlgnCW\n" +"-----END CERTIFICATE-----",.len=1457}, + +/* GlobalSign Root CA - R3 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMX\n" +"R2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMT\n" +"Ckdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQL\n" +"ExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UE\n" +"AxMKR2xvYmFsU2lnbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5Bngi\n" +"FvXAg7aEyiie/QV2EcWtiHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0M\n" +"K66X17YUhhB5uzsTgHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL\n" +"0gRgykmmKPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd\n" +"QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613\n" +"t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQD\n" +"AgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0G\n" +"CSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7lgAJQayzE4aGKAczymvmdLm6AC2u\n" +"pArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdW\n" +"PoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0\n" +"095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJr\n" +"lAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH\n" +"WD9f\n" +"-----END CERTIFICATE-----",.len=1226}, + +/* Autoridad de Certificacion Firmaprofesional CIF A62634068 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMx\n" +"QjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwg\n" +"Q0lGIEE2MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNV\n" +"BAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zl\n" +"c2lvbmFsIENJRiBBNjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDK\n" +"lmuO6vj78aI14H9M2uDDUtd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOS\n" +"L/UR5GLXMnE42QQMcas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9\n" +"qFD0sefGL9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i\n" +"NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2\n" +"f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44\n" +"I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCyZ/QYFpM6/EfY0XiWMR+6Kwxf\n" +"XZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy\n" +"9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF\n" +"8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mV\n" +"BngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1UdEwEB/wQIMAYBAf8C\n" +"AQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNHDhpkLzCBpgYD\n" +"VR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZpcm1hcHJv\n" +"ZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAAbABh\n" +"ACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx\n" +"ADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+\n" +"xDLx51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5x\n" +"hOW1//qkR71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5\n" +"eTSSPi5E6PaPT481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5Fl\n" +"ClrD2VQS3a/DTg4fJl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5k\n" +"SeTy36LssUzAKh3ntLFlosS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2\n" +"gHN99ZwExEWN57kci57q13XRcrHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYD\n" +"NEeyrPsiBsse3RdHHF9mudMaotoRsaS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhr\n" +"JKyXWERHrVkY8SFlcN7ONGCoQPHzPKTDKCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIyS\n" +"xZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi6Et8Vcad+qMUu2WFbm5PEn4KPJ2V\n" +"-----END CERTIFICATE-----",.len=2162}, + +/* Izenpe.com */ +{.str="-----BEGIN CERTIFICATE-----\n" +"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\n" +"1V4tC5h9Pmzb/CaIxw==\n" +"-----END CERTIFICATE-----",.len=2118}, + +/* Go Daddy Root Certificate Authority - G2 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNV\n" +"BAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29t\n" +"LCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAt\n" +"IEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAw\n" +"DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5\n" +"LmNvbSwgSW5jLjExMC8GA1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3Jp\n" +"dHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3\n" +"gElY6SKDE6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH\n" +"/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLI\n" +"jWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6\n" +"gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGRtDtwKj9useiciAF9n9T521Nt\n" +"YJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO\n" +"BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3\n" +"DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC\n" +"2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95\n" +"kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo\n" +"2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPOLPAvTK33\n" +"sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1\n" +"-----END CERTIFICATE-----",.len=1363}, + +/* Starfield Root Certificate Authority - G2 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNV\n" +"BAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBU\n" +"ZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRl\n" +"IEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJ\n" +"BgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYD\n" +"VQQKExxTdGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQg\n" +"Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEP\n" +"ADCCAQoCggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg\n" +"nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSu\n" +"S/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhh\n" +"dM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dNdloedl40wOiWVpmKs/B/pM29\n" +"3DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbUJtQIBFnQmA4O5t78w+wfkPECAwEA\n" +"AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n\n" +"2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWU\n" +"XuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox\n" +"9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg\n" +"8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/KpL/QlwVK\n" +"vOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZc2T5\n" +"NnReJaH1ZgUufzkVqSr7UIuOhWn0\n" +"-----END CERTIFICATE-----",.len=1396}, + +/* Starfield Services Root Certificate Authority - G2 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNV\n" +"BAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBU\n" +"ZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENl\n" +"cnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1\n" +"OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNk\n" +"YWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJT\n" +"dGFyZmllbGQgU2VydmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw\n" +"DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p\n" +"OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2\n" +"dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS\n" +"7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufehRhJfGZOozptqbXuNC66DQO4\n" +"M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFBrMnUVN+HL8cisibMn1lUaJ/8viov\n" +"xFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC\n" +"AQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBL\n" +"NqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynV\n" +"v/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z\n" +"qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkdiEDPfUYd\n" +"/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jzaYyWf\n" +"/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6\n" +"-----END CERTIFICATE-----",.len=1420}, + +/* AffirmTrust Commercial */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMx\n" +"FDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFs\n" +"MB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNV\n" +"BAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjAN\n" +"BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTW\n" +"zsO3qyxPxkEylFf6EqdbDuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U\n" +"6Mje+SJIZMblq8Yrba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNA\n" +"FxHUdPALMeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1\n" +"yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1J\n" +"dX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8w\n" +"DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAFis\n" +"9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M\n" +"06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1Ua\n" +"ADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjip\n" +"M1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclN\n" +"msxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8=\n" +"-----END CERTIFICATE-----",.len=1201}, + +/* AffirmTrust Networking */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMx\n" +"FDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5n\n" +"MB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNV\n" +"BAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjAN\n" +"BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWRE\n" +"ZY9nZOIG41w3SfYvm4SEHi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ\n" +"/Ls6rnla1fTWcbuakCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXL\n" +"viRmVSRLQESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp\n" +"6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKB\n" +"Nv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0w\n" +"DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAIlX\n" +"shZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t\n" +"3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA\n" +"3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzek\n" +"ujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfx\n" +"ojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s=\n" +"-----END CERTIFICATE-----",.len=1201}, + +/* AffirmTrust Premium */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMx\n" +"FDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4X\n" +"DTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoM\n" +"C0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG\n" +"9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64t\n" +"b+eT2TZwamjPjlGjhVtnBKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/\n" +"0qRY7iZNyaqoe5rZ+jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/\n" +"K+k8rNrSs8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5\n" +"HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua\n" +"2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/\n" +"9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+SqHZGnEJlPqQewQcDWkYtuJfz\n" +"t9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m\n" +"6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKP\n" +"KrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNC\n" +"MEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYD\n" +"VR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2\n" +"KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMgNt58D2kT\n" +"iKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC6C1Y\n" +"91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S\n" +"L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQ\n" +"wUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFo\n" +"oC8k4gmVBtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5Yw\n" +"H2AG7hsj/oFgIxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/\n" +"qzWaVYa8GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO\n" +"RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAlo\n" +"GRwYQw==\n" +"-----END CERTIFICATE-----",.len=1887}, + +/* AffirmTrust Premium ECC */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDAS\n" +"BgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAe\n" +"Fw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQK\n" +"DAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcq\n" +"hkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQU\n" +"X+iOGasvLkjmrBhDeKzQN8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR\n" +"4ptlKymjQjBAMB0GA1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTAD\n" +"AQH/MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs\n" +"aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9C\n" +"a/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ==\n" +"-----END CERTIFICATE-----",.len=751}, + +/* Certum Trusted Network CA */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYD\n" +"VQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlm\n" +"aWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0Ew\n" +"HhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UE\n" +"ChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmlj\n" +"YXRpb24gQXV0aG9yaXR5MSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIB\n" +"IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/\n" +"91sts1rHUV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM\n" +"TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmt\n" +"VSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM\n" +"+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8xAcPs3hEtF10fuFDRXhmnad4H\n" +"MyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQI\n" +"ds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEB\n" +"AKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsi\n" +"srCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv\n" +"94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY\n" +"VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI03YnnZot\n" +"BqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw=\n" +"-----END CERTIFICATE-----",.len=1351}, + +/* TWCA Root Certification Authority */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UE\n" +"CgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2Vy\n" +"dGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBf\n" +"MQswCQYDVQQGEwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSow\n" +"KAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3\n" +"DQEBAQUAA4IBDwAwggEKAoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bi\n" +"hSX0NXIP+FPQQeFEAcK0HMMxQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQ\n" +"sIBct+HHK3XLfJ+utdGdIzdjp9xCoi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeX\n" +"RfwZVzsrb+RH9JlF/h3x+JejiB03HFyP4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJb\n" +"KdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1ry+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxY\n" +"A7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud\n" +"DgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG9w0BAQUFAAOCAQEAPNV3PdrfibqH\n" +"DAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lCmtYR5VPOL8zy2gXE/uJQxDqG\n" +"fczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4g\n" +"umlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKu\n" +"D8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ\n" +"YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw==\n" +"-----END CERTIFICATE-----",.len=1266}, + +/* Security Communication RootCA2 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UE\n" +"ChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29t\n" +"bXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTEL\n" +"MAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAl\n" +"BgNVBAsTHlNlY3VyaXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEB\n" +"BQADggEPADCCAQoCggEBANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz3\n" +"35c9S672XewhtUGrzbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonC\n" +"v/Q4EpVMVAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ\n" +"hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhw\n" +"Hyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCca\n" +"dfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5csOPEK7DzPMA4GA1UdDwEB/wQE\n" +"AwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBMOqNErLlFsceTfsgL\n" +"CkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8\n" +"AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g6\n" +"9ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR\n" +"50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/\n" +"SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03\n" +"-----END CERTIFICATE-----",.len=1258}, + +/* Actalis Authentication Root CA */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCSVQx\n" +"DjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1ODUyMDk2NzEn\n" +"MCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENBMB4XDTExMDkyMjExMjIw\n" +"MloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYD\n" +"VQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRo\n" +"ZW50aWNhdGlvbiBSb290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bE\n" +"pSmkLO/lGMWwUKNvUTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW\n" +"1V8IbInX4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9\n" +"KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/gCczWw63\n" +"igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1YprbrxTIW6HMiRvhMCb8\n" +"oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ51Q+qDp2JE+BIcXjDwL4k5RH\n" +"ILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2Fbe8lEfKXGkJh90qX6IuxEAf6ZYGyojnP\n" +"9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxeKF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4\n" +"RCHiMUVhUE4Q2OM1fEwZtN4Fv6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U\n" +"5r0F+7if5t+L4sbnfpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/j\n" +"Vo23Eek7jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz\n" +"ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbtifN7OHCU\n" +"yQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQALe3KHwGCmSUyIWOYd\n" +"iPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70jsNjLiNmsGe+b7bAEzlgqqI0\n" +"JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDzWochcYBNy+A4mz+7+uAwTc+G02UQGRjR\n" +"lwKxK3JCaKygvU5a2hi/a5iB0P2avl4VSM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2ryk\n" +"OLpn7VU+Xlff1ANATIGk0k9jpwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2T\n" +"lf05fbsq4/aC4yyXX04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst\n" +"842/6+OkfcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R\n" +"K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btUZCzJJ7VL\n" +"kn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJULysRJyU3eExRarDz\n" +"zFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaTLnPqZih4zR0Uv6CPLy64Lo7y\n" +"FIrM6bV8+2ydDKXhlg==\n" +"-----END CERTIFICATE-----",.len=2045}, + +/* Buypass Class 2 Root CA */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UE\n" +"CgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMiBSb290\n" +"IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1owTjELMAkGA1UEBhMCTk8xHTAb\n" +"BgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDIg\n" +"Um9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1\n" +"aeTuMgHbo4Yf5FkNuud1g1Lr6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXl\n" +"zwx87vFKu3MwZfPVL4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FV\n" +"M5I+GC911K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx\n" +"MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZQmdiXnfg\n" +"olXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkBarcNuAeBfos4Gzjm\n" +"CleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clrUs3ERo/ctfPYV3Me6ZQ5BL/T\n" +"3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLiFRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1L\n" +"PC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRSP/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIH\n" +"ZMbXb5tMOA1jrGKvNouicwoN9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVe\n" +"e7QHIJihdjK4TWxPAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+Bi\n" +"koL1RpzzuvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h\n" +"9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462sA20ucS6v\n" +"xOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3tOluwlN5E40EIosHs\n" +"Hdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo+fsicdl9sz1Gv7SEr5AcD48S\n" +"aq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlq\n" +"YLYdDnkM/crqJIByw5c/8nerQyIKx+u2DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6\n" +"OBE1/yWDLfJ6v9r9jv6ly0UsH8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6w\n" +"pJ9qzo6ysmD0oyLQI+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYK\n" +"beaP4NK75t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h\n" +"3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPzY11aWOIv\n" +"4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA=\n" +"-----END CERTIFICATE-----",.len=1911}, + +/* Buypass Class 3 Root CA */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UE\n" +"CgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMyBSb290\n" +"IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFowTjELMAkGA1UEBhMCTk8xHTAb\n" +"BgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDMg\n" +"Um9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEG\n" +"Mnqb8RB2uACatVI2zSRHsJ8YZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fk\n" +"oF0LXOBXByow9c3EN3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOo\n" +"TyrvYLs9tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX\n" +"0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c/3ATAOux\n" +"9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8XKhDzzMro6/1rqy6a\n" +"ny2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTYzIw0bOIjL9kSGiG5VZFvC5F5\n" +"GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvSO1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon\n" +"74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3i\n" +"iZ+gMEuFuZyUJHmPfWupRWgPK9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFM\n" +"OVLAMLrwjEsCsLa3AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/l\n" +"b+74suwvTg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj\n" +"QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdVcSQy9sgL\n" +"8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXSIGrs/CIBKM+GuIAe\n" +"qcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2HJLw5QY33KbmkJs4j1xrG0aG\n" +"Q0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsaO5S3HWCntZznKWlXWpuTekMwGwPXYshA\n" +"pqr8ZORK15FTAaggiG6cX0S5y2CBNOxv033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjEN\n" +"SoYc6+I2KSb12tjE8nVhz36udmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr1\n" +"8okmAWiDSKIz6MkEkbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2X\n" +"cEQNtg413OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD\n" +"u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq4/g7u9xN\n" +"12TyUb7mqqta6THuBrxzvxNiCp/HuZc=\n" +"-----END CERTIFICATE-----",.len=1911}, + +/* T-TeleSec GlobalRoot Class 3 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNV\n" +"BAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lz\n" +"dGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNz\n" +"IDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzAp\n" +"BgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQt\n" +"U3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENs\n" +"YXNzIDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3Z\n" +"JNW4t/zN8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/\n" +"RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4hqX2iys5\n" +"2qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5ZeAsVMhg02YXP+HM\n" +"VDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltMEnAMbEQgqxHY9Bn20pxSN+f6\n" +"tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD\n" +"VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0B\n" +"AQsFAAOCAQEAVj3vlNW92nOyWL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ\n" +"85cp4IaH3gRZD/FZ1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/\n" +"vBa3wW306gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT\n" +"91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuImle9eiPZaG\n" +"zPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4pTpPDpFQUWw==\n" +"-----END CERTIFICATE-----",.len=1363}, + +/* D-TRUST Root Class 3 CA 2 2009 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRFMRUwEwYD\n" +"VQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIg\n" +"MjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NThaME0xCzAJBgNVBAYTAkRFMRUw\n" +"EwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENB\n" +"IDIgMjAwOTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/W\n" +"UEWJNTrGa9v+2wBoqOADER03UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23D\n" +"EE0NkVJD2IfgXU42tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/\n" +"RcPHAY9RySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM\n" +"lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp/hnd2HN8\n" +"gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8GA1UdEwEB/wQFMAMB\n" +"Af8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4GA1UdDwEB/wQEAwIBBjCB0wYD\n" +"VR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRS\n" +"VVNUJTIwUm9vdCUyMENsYXNzJTIwMyUyMENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21i\n" +"SCxDPURFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10\n" +"cnVzdC5uZXQvY3JsL2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZI\n" +"hvcNAQELBQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni\n" +"acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0o3/U37CY\n" +"Aqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4KzCUqNQT4YJEVdT1B\n" +"/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8PIWmawomDeCTmGCufsYkl4ph\n" +"X5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3YJohw1+qRzT65ysCQblrGXnRl11z+o+I=\n" +"-----END CERTIFICATE-----",.len=1513}, + +/* D-TRUST Root Class 3 CA 2 EV 2009 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRFMRUwEwYD\n" +"VQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIg\n" +"RVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUwNDZaMFAxCzAJBgNVBAYTAkRF\n" +"MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAz\n" +"IENBIDIgRVYgMjAwOTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3\n" +"MKCOvXwEz75ivJn9gpfSegpnljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut\n" +"8Mxk2og+KbgPCdM03TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsT\n" +"l28So/6ZqQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR\n" +"p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8HgQ+HKDY\n" +"D8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQwggEgMA8GA1UdEwEB\n" +"/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9NteaHNxnMA4GA1UdDwEB/wQEAwIB\n" +"BjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFwOi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0\n" +"L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xhc3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89\n" +"RC1UcnVzdCUyMEdtYkgsQz1ERT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBo\n" +"dHRwOi8vd3d3LmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2\n" +"XzIwMDkuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp\n" +"3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05nsKtjHEh\n" +"8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNFCSuGdXzfX2lXANtu\n" +"2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7naxpeG0ILD5EJt/rDiZE4OJudA\n" +"NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjm\n" +"JuVvw9y4AyHqnxbxLFS1\n" +"-----END CERTIFICATE-----",.len=1534}, + +/* CA Disig Root R2 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"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\n" +"-----END CERTIFICATE-----",.len=1931}, + +/* ACCVRAIZ1 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UEAwwJQUND\n" +"VlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQswCQYDVQQGEwJFUzAe\n" +"Fw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQBgNVBAMMCUFDQ1ZSQUlaMTEQ\n" +"MA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUNDVjELMAkGA1UEBhMCRVMwggIiMA0GCSqG\n" +"SIb3DQEBAQUAA4ICDwAwggIKAoICAQCbqau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPf\n" +"mt4ftVTdFXxpNRFvu8gMjmoYHtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM38\n" +"02/J+Nq2DoLSRYWoG2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkX\n" +"hBilyNpAlHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr\n" +"IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/0KIV9VMJ\n" +"cRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eHk6fGioozl2A3ED6X\n" +"Pm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/474KElB0iryYl0/wiPgL/AlmXz\n" +"7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMOm3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE\n" +"3fj5tItQo05iifCHJPqDQsGH+tUtKSpacXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbh\n" +"dQrqeWZ2iE9x6wQl1gpaepPluUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQAB\n" +"o4ICyzCCAscwfQYIKwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3Yu\n" +"ZXMvZmlsZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG\n" +"AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2VuqB5TbM\n" +"jB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeTVfZW6oHlNsyMHj+9\n" +"MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIGCCsGAQUFBwICMIIBFB6CARAA\n" +"QQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUAcgB0AGkAZgBpAGMAYQBjAGkA8wBuACAA\n" +"UgBhAO0AegAgAGQAZQAgAGwAYQAgAEEAQwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUA\n" +"IABUAGUAYwBuAG8AbABvAGcA7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4A\n" +"IABFAGwAZQBjAHQAcgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUA\n" +"KQAuACAAQwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA\n" +"czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2MuaHRtMFUG\n" +"A1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRtaW4vQXJjaGl2b3Mv\n" +"Y2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1UdDwEB/wQEAwIBBjAXBgNVHREE\n" +"EDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEFBQADggIBAJcxAp/n/UNnSEQU5CmH7Uwo\n" +"ZtCPNdpNYbdKl02125DgBS4OxnnQ8pdpD70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTK\n" +"FpV8M9xii6g3+CfYCS0b78gUJyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/\n" +"+QEnWCzI7UiRfD+mAM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA\n" +"9j9ajepDvV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms\n" +"tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH7ehVRE2I\n" +"9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5hI6zppSSMEYCUWqKi\n" +"uUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szAh1xA2syVP1XgNce4hL60Xc16\n" +"gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xFd3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew\n" +"+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2HpPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbR\n" +"D0tVNEYqi4Y7\n" +"-----END CERTIFICATE-----",.len=2767}, + +/* TWCA Global Root CA */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcxEjAQBgNV\n" +"BAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMTVFdDQSBHbG9iYWwg\n" +"Um9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5NTlaMFExCzAJBgNVBAYTAlRX\n" +"MRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0Eg\n" +"R2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zE\n" +"booh745NnHEKH1Jw7W2CnJfF10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvV\n" +"avKOZsTuKwEHktSz0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XT\n" +"P3VfKfChMBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH\n" +"zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc46KokWof\n" +"wpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2yKsi2XMPpfRaAok/\n" +"T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCilaLOz9qC5wc0GZbpuCGqKX6m\n" +"OL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYPoA/pyJV/v1WRBXrPPRXAb94JlAGD1zQb\n" +"zECl8LibZ9WYkTunhHiVJqRaCPgrdLQABDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJr\n" +"nu+8vyq8W8BQj0FwcYeyTbcEqYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53\n" +"L46g5pIOBvwFItIm4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/\n" +"BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL\n" +"1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsnLhpNgb+E\n" +"1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WFH6vPNOw/KP4M8VeG\n" +"TslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNoRI2T9GRwoD2dKAXDOXC4Ynsg\n" +"/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+nile98FRYB/e2guyLXW3Q0iT5/Z5xoRd\n" +"gFlglPx4mI88k1HtQJAH32RjJMtOcQWh15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92\n" +"a6O2JryPA9gK8kxkRr05YuWW6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/Zuepi\n" +"iI7E8UuDEq3mi4TWnsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZz\n" +"JBPqpK5jwa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz\n" +"aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmyKwbQBM0=\n" +"-----END CERTIFICATE-----",.len=1878}, + +/* TeliaSonera Root CA v1 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAwNzEUMBIG\n" +"A1UECgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJvb3QgQ0EgdjEwHhcN\n" +"MDcxMDE4MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYDVQQKDAtUZWxpYVNvbmVyYTEf\n" +"MB0GA1UEAwwWVGVsaWFTb25lcmEgUm9vdCBDQSB2MTCCAiIwDQYJKoZIhvcNAQEBBQADggIP\n" +"ADCCAgoCggIBAMK+6yfwIaPzaSZVfp3FVRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3\n" +"t+XmfHnqjLWCi65ItqwA3GV17CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq\n" +"/t75rH2D+1665I+XZ75Ljo1kB1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1\n" +"jF3oI7x+/jXh7VB7qTCNGdMJjmhnXb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs\n" +"81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxHoLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkmdtwoSxAg\n" +"HNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3F0fUTPHSiXk+TT2YqGHeOh7S+F4D4MHJHIzT\n" +"jU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJoWjiUIMusDor8zagrC/kb2HCUQk5PotT\n" +"ubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4pgd7gUY2BIbdeTXHlSw7sKMXNeVzH7Rc\n" +"We/a6hBle3rQf5+ztCo3O3CLm1u5K7fsslESl1MpWtTwEhDcTwK7EpIvYtQ/aUN8Ddb8WHUB\n" +"iJ1YFkveupD/RwGJBmr2X7KQarMCpgKIv7NHfirZ1fpoeDVNAgMBAAGjPzA9MA8GA1UdEwEB\n" +"/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qWDNXr+nuqF+gTEjAN\n" +"BgkqhkiG9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNmzqjMDfz1mgbl\n" +"dxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx0GtnLLCo\n" +"4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1TjTQpgcmLNkQfWpb/I\n" +"mWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBedY2gea+zDTYa4EzAvXUYNR0PV\n" +"G6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KF\n" +"dSpcc41teyWRyu5FrgZLAMzTsVlQ2jqIOylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrE\n" +"gUy7onOTJsjrDNYmiLbAJM+7vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQ\n" +"mz1wHiRszYd2qReWt88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfuj\n" +"uLpwQMcnHL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx\n" +"SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY=\n" +"-----END CERTIFICATE-----",.len=1866}, + +/* E-Tugra Certification Authority */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNVBAYTAlRS\n" +"MQ8wDQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtu\n" +"b2xvamlsZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlm\n" +"aWthc3lvbiBNZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9y\n" +"aXR5MB4XDTEzMDMwNTEyMDk0OFoXDTIzMDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8w\n" +"DQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xv\n" +"amlsZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWth\n" +"c3lvbiBNZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5\n" +"MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4vU/kwVRHoViVF56C/UYB4Oufq98\n" +"99SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vdhQd2h8y/L5VMzH2nPbxHD5hw+IyFHnSOkm0b\n" +"QNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5KCKpbknSFQ9OArqGIW66z6l7LFpp3RMih\n" +"9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEoq1+gElIwcxmOj+GMB6LDu0rw6h8VqO4l\n" +"zKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3Dk14opz8n8Y4e0ypQBaNV2cvnOVPAmJ6MVGKLJrD3\n" +"fY185MaeZkJVgkfnsliNZvcHfC425lAcP9tDJMW/hkd5s3kc91r0E+xs+D/iWR+V7kI+ua2o\n" +"MoVJl0b+SzGPWsutdEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gzrt48Ue7LE3wBf4QO\n" +"XVGUnhMMti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAqjqFGOjGY5RH8\n" +"zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+LznrFpct1pH\n" +"XFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUXU8u3Zg5mTPj5dUyQ\n" +"5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6Jyr+zE7S6E5UMA8GA1UdEwEB\n" +"/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQD\n" +"AgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAFNzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd\n" +"0dCrfOAKkEh47U6YA5n+KGCRHTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/\n" +"u6Au/U5Mh/jOXKqYGwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1\n" +"Q9Jauz1c77NCR807VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3\n" +"+GbHeJAAFS6LrVE1Uweoa2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WKvJUawSg5\n" +"TB9D0pH0clmKuVb8P7Sd2nCcdlqMQ1DujjByTd//SffGqWfZbawCEeI6FiWnWAjLb1NBnEg4\n" +"R2gz0dfHj9R0IdTDBZB6/86WiLEVKV0jq9BgoRJP3vQXzTLlyb/IQ639Lo7xr+L0mPoSHyDY\n" +"wKcMhcWQ9DstliaxLL5Mq+ux0orJ23gTDx4JnW2PAJ8C2sH6H3p6CcRK5ogql5+Ji/03X186\n" +"zjhZhkuvcQu02PJwT58yE+Owp1fl2tpDy4Q08ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9\n" +"I2cNE3X7z2VnIp2usAnRCf8dNL/+I5c30jn6PQ0GC7TbO6Orb1wdtn7os4I07QZcJA==\n" +"-----END CERTIFICATE-----",.len=2239}, + +/* T-TeleSec GlobalRoot Class 2 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNV\n" +"BAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lz\n" +"dGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNz\n" +"IDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzAp\n" +"BgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQt\n" +"U3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENs\n" +"YXNzIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl\n" +"82hVYAUdAqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC\n" +"FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi1hKTXrcx\n" +"lkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6IavqjnKgP6TeMFvvhk1q\n" +"lVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZwI18gfNycJ5v/hqO2V81xrJv\n" +"NHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD\n" +"VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0B\n" +"AQsFAAOCAQEAMQOiYQsfdOhyNsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSp\n" +"p+ghla0+AGIWiPACuvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joi\n" +"fsFvHZVwIEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6\n" +"g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN9noHV8ci\n" +"gwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlPBSeOE6Fuwg==\n" +"-----END CERTIFICATE-----",.len=1363}, + +/* Atos TrustedRoot 2011 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UEAwwVQXRv\n" +"cyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0xMTA3\n" +"MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3Qg\n" +"MjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IB\n" +"DwAwggEKAoIBAQCVhTuXbyo7LjvPpvMpNb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI41\n" +"9KkM/IL9bcFyYie96mvr54rMVD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+\n" +"yj5vdHLqqjAqc2K+SZFhyBH+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFs\n" +"Q/H3NYkQ4J7sVaE3IqKHBAUsR320HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0L\n" +"cp2AMBYHlT8oDv3FdU9T1nSatCQujgKRz3bFmx5VdJx4IbHwLfELn8LVlhgf8FQieowHAgMB\n" +"AAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7Rl+lwrrw7GWzbITAPBgNVHRMBAf8EBTADAQH/\n" +"MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZbNshMBgGA1UdIAQRMA8wDQYLKwYBBAGw\n" +"LQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4IBAQAmdzTblEiGKkGdLD4G\n" +"kGDEjKwLVLgfuXvTBznk+j57sj1O7Z8jvZfza1zv7v1Apt+hk6EKhqzvINB5Ab149xnYJDE0\n" +"BAGmuhWawyfc2E8PzBhj/5kPDpFrdRbhIfzYJsdHt6bPWHJxfrrhTZVHO8mvbaG0weyJ9rQP\n" +"OLXiZNwlz6bb65pcmaHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYV\n" +"qL2Gns2Dlmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv\n" +"KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed\n" +"-----END CERTIFICATE-----",.len=1258}, + +/* QuoVadis Root CA 1 G3 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQELBQAwSDEL\n" +"MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1Zh\n" +"ZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00MjAxMTIxNzI3NDRaMEgxCzAJ\n" +"BgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRp\n" +"cyBSb290IENBIDEgRzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjuny\n" +"bEC0BJyFuTHK3C3kEakEPBtVwedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/\n" +"gpqG7D0DmVIB0jWerNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOG\n" +"MAqNF34168Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh\n" +"4Pw5qlPafX7PGglTvF0FBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXpUhtStZI5\n" +"cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+oabw4m6SkltFi2mnA\n" +"AZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc3sX5kCLliEVx3ZGZbHqfPT2Y\n" +"fF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/GKubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVd\n" +"YdHLiZxfokqRmu8hqkkWCKi9YSgxyXSthfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7\n" +"a3F98N/ETH3Goy7IlXnLc6KOTk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfn\n" +"kduebPRq34wGmAOtzCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB\n" +"/wQEAwIBBjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD\n" +"ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOCMTaIzen7\n" +"xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2cDMT/uFPpiN3GPoa\n" +"jOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUNqXsCHKnQO18LwIE6PWThv6ct\n" +"Tr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvr\n" +"iBbP+V04ikkwj+3x6xn0dxoxGE1nVGwvb2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCk\n" +"eF9OrYMh3jRJjehZrJ3ydlo28hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ\n" +"+p6Q9pxyz0fawx/kNSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTD\n" +"vdbJWqNjZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp\n" +"q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFtnh8GKjwS\n" +"tIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD\n" +"-----END CERTIFICATE-----",.len=1919}, + +/* QuoVadis Root CA 2 G3 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQELBQAwSDEL\n" +"MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1Zh\n" +"ZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00MjAxMTIxODU5MzJaMEgxCzAJ\n" +"BgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRp\n" +"cyBSb290IENBIDIgRzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjc\n" +"V4g/Ruv5r+LrI3HimtFhZiFfqq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WV\n" +"HhLL5hSEBMHfNrMWn4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs\n" +"+L5u+9ymc5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+\n" +"O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1o9/NgJ8M\n" +"SPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0jIaFYAI7D0GoT7RPj\n" +"EiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKqIcGY5jDjZ1XHm26sGahVpkUG\n" +"0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIh\n" +"O4M15zHfeiFuuDIIfR0ykRVKYnLP43ehvNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoos\n" +"FCT5v0ICvybIxo/gbjh9Uy3l7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4\n" +"r0jyWbYW8jwNkALGcC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB\n" +"/wQEAwIBBjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD\n" +"ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66AarHakE7\n" +"kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RCroijQ1h5fq7KpVMN\n" +"qT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0GaW/ZZGYjeVYg3UQt4XAoeo0L9\n" +"x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4nlv1mNDthcnPxFlxHBlRJAHpYErAK74X9\n" +"sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE+V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDb\n" +"Il9qxV6XU/IyAgkwo1jwDQHVcsaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+\n" +"D77vfoRrQ+NwmNtddbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpn\n" +"IdsPNWNgKCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM\n" +"HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4WSr2Rz0Z\n" +"iC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M\n" +"-----END CERTIFICATE-----",.len=1919}, + +/* QuoVadis Root CA 3 G3 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQELBQAwSDEL\n" +"MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1Zh\n" +"ZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00MjAxMTIyMDI2MzJaMEgxCzAJ\n" +"BgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRp\n" +"cyBSb290IENBIDMgRzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47q\n" +"FJenMioKVjZ/aEzHs286IxSR/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O\n" +"2YIyC0TeytuMrKNuFoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMB\n" +"OSBDGzXRU7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c\n" +"ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERRORFHAGjx+f\n" +"+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/kA9HvFZcba5DFApCT\n" +"ZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzweyuxwHApw0BiLTtIadwjPEjr\n" +"ewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634RylsSqiMd5mBPfAdOhx3v89WcyWJhKLhZVX\n" +"GqtrdQtEPREoPHtht+KPZ0/l7DxMYIBpVzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUC\n" +"f+D+IOs15xGsIs5XPd7JMG0QA4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/J\n" +"xHwc64B+27bQ3RP+ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB\n" +"/wQEAwIBBjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD\n" +"ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3pxKGmPc+FS\n" +"kNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnIFUBhynLWcKzSt/Ac\n" +"5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5WvvoxXqA/4Ti2Tk08HS6IT7SdEQ\n" +"TXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFgu/BYpbWcC/ePIlUnwEsBbTuZDdQdm2Nn\n" +"L9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE\n" +"8/nxoGibIh6BJpsQBJFxwAYf3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8\n" +"XgBCH/MyJnmDhPbl8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H\n" +"6QrG2vd+DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN\n" +"PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ywaZWWDY\n" +"WGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0\n" +"-----END CERTIFICATE-----",.len=1919}, + +/* DigiCert Assured ID Root G2 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBlMQswCQYD\n" +"VQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu\n" +"Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwHhcNMTMwODAxMTIw\n" +"MDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQg\n" +"SW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1\n" +"cmVkIElEIFJvb3QgRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82\n" +"ckmIkzTz+GoeMVSAn61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxN\n" +"EP4HteccbiJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp\n" +"EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lAbx3RvpO7\n" +"04gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6YuYjOuFgJ3RFrngQo8\n" +"p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMBAAGjQjBAMA8GA1UdEwEB/wQF\n" +"MAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTOw0q5mVXyuNtgv6l+vVa1lzan1jAN\n" +"BgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPIQW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTU\n" +"iaWxMTeKySHMq2zNixya1r9I0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LR\n" +"dWLjSisCx1BL4GnilmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70\n" +"CjTVW0z9B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv\n" +"ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwoIhNzbM8m\n" +"9Yop5w==\n" +"-----END CERTIFICATE-----",.len=1303}, + +/* DigiCert Assured ID Root G3 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQswCQYDVQQG\n" +"EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29t\n" +"MSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwHhcNMTMwODAxMTIwMDAw\n" +"WhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5j\n" +"MRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVk\n" +"IElEIFJvb3QgRzMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0F\n" +"FfLvC/8QdJ+1YlJfZn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+\n" +"CW7if17QRSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/\n" +"BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQDAwNnADBk\n" +"AjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlYJjZ91eQ0hjkCMHw2\n" +"U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv6pZjamVFkpUBtA==\n" +"-----END CERTIFICATE-----",.len=848}, + +/* DigiCert Global Root G2 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBhMQswCQYD\n" +"VQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu\n" +"Y29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMjAeFw0xMzA4MDExMjAwMDBa\n" +"Fw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx\n" +"GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBS\n" +"b290IEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/\n" +"RrohCgiN9RlUyfuI2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxps\n" +"MNzaHxmx1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ\n" +"q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5WztCO7TG1F\n" +"8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQvIOlCsRnKPZzFBQ9\n" +"RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G\n" +"A1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcN\n" +"AQELBQADggEBAGBnKJRvDkhj6zHd6mcY1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVn\n" +"NeyIv/iPsGEMNKSuIEyExtv4NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2Z\n" +"L7tcu7XUIOGZX1NGFdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUT\n" +"Fy8bJZ918rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe\n" +"pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTflMrY=\n" +"-----END CERTIFICATE-----",.len=1290}, + +/* DigiCert Global Root G3 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQswCQYDVQQG\n" +"EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29t\n" +"MSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAeFw0xMzA4MDExMjAwMDBaFw0z\n" +"ODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAX\n" +"BgNVBAsTEHd3dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290\n" +"IEczMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu8\n" +"0JX28MzQC7phW1FGfp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6I\n" +"p6FrtUPOZ9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd\n" +"BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIxAK288mw/\n" +"EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/oAIwOWZbwmSNuJ5Q\n" +"3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8sycX\n" +"-----END CERTIFICATE-----",.len=836}, + +/* DigiCert Trusted Root G4 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"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\n" +"6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+\n" +"-----END CERTIFICATE-----",.len=1984}, + +/* COMODO RSA Certification Authority */ +{.str="-----BEGIN CERTIFICATE-----\n" +"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\n" +"-----END CERTIFICATE-----",.len=2081}, + +/* USERTrust RSA Certification Authority */ +{.str="-----BEGIN CERTIFICATE-----\n" +"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\n" +"-----END CERTIFICATE-----",.len=2089}, + +/* USERTrust ECC Certification Authority */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDELMAkGA1UE\n" +"BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYD\n" +"VQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2Vy\n" +"dGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCB\n" +"iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBD\n" +"aXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVz\n" +"dCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQa\n" +"rFRaqfloI+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng\n" +"o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0GA1UdDgQW\n" +"BBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB\n" +"/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMBzzuqQhFkoJ2UOQIReVx7Hfpk\n" +"ue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbWRNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbR\n" +"qZtNyWHa0V1Xahg=\n" +"-----END CERTIFICATE-----",.len=946}, + +/* GlobalSign ECC Root CA - R5 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEkMCIGA1UE\n" +"CxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMw\n" +"EQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEk\n" +"MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxT\n" +"aWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9\n" +"Xb/pOdEh+J8LttV7HpI6SFkc8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwz\n" +"ocWdTaRvQZU4f8kehOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMC\n" +"AQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI\n" +"KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg515dTguD\n" +"nFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnOxwy8p2Fp8fc74SrL\n" +"+SvzZpA3\n" +"-----END CERTIFICATE-----",.len=792}, + +/* IdenTrust Commercial Root CA 1 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBKMQswCQYD\n" +"VQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVy\n" +"Y2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQwMTE2MTgxMjIzWjBKMQswCQYD\n" +"VQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVy\n" +"Y2lhbCBSb290IENBIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k9\n" +"1DNG8W9RYYKyqU+PZ4ldhNlT3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1e\n" +"hm7zCYofWjK9ouuU+ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQf\n" +"Yo3fw7gpS0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1\n" +"bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORiT0/Br4sO\n" +"dBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCLvYf5jysjCiN2O/cz\n" +"4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjKVsk9+w8YfYs7wRPCTY/JTw43\n" +"6R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZKdHzVWYfCP04MXFL0PfdSgvHqo6z9STQa\n" +"KPNBiDoT7uje/5kdX7rL6B7yuVBgwDHTc+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h\n" +"9tgJ4TNkK2PXMl6f+cB7D3hvl7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHw\n" +"cz09lCqxC0EOoP5NiGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/\n" +"BAUwAwEB/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD\n" +"ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH6oi6mYtQ\n" +"lNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwtLRvM7Kqas6pgghst\n" +"O8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93nAbowacYXVKV7cndJZ5t+qnt\n" +"ozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3+wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gj\n" +"mmmVYjzlVYA211QC//G5Xc7UI2/YRYRKW2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l\n" +"2xPE4iUXfeu+h1sXIFRRk0pTAwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lz\n" +"zY9GvlU47/rokTLql1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2\n" +"gXjtw+hG4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ\n" +"mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A7/qxXDgG\n" +"pRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H\n" +"-----END CERTIFICATE-----",.len=1919}, + +/* IdenTrust Public Sector Root CA 1 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBNMQswCQYD\n" +"VQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGlj\n" +"IFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcNMzQwMTE2MTc1MzMyWjBNMQsw\n" +"CQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVi\n" +"bGljIFNlY3RvciBSb290IENBIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2\n" +"IpT8pEiv6EdrCvsnduTyP4o7ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2L\n" +"qEfpYnYeEe4IFNGyRBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1\n" +"B5+ctMlSbdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF\n" +"/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R3j6HEDbh\n" +"uaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vwEUs3oERte8uojHH0\n" +"1bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy9yLxkA2T26pEUWbMfXYD62qo\n" +"KjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9VGxyhLrXHFub4qjySjmm2AcG1hp2JDws4\n" +"lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYP\n" +"Qxxp+pu10GFqzcpL2UyQRqsVWaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsby\n" +"VtTdX5Vy7W1f90gDW/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD\n" +"VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN\n" +"AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qjt2odIFfl\n" +"AWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHVDRDtfULAj+7AmgjV\n" +"QdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9TaDKQGXSc3z1i9kKlT/YPyNt\n" +"GtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8GlwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S\n" +"3OFtm6/n6J91eEyrRjuazr8FGF1NFTwWmhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHW\n" +"chezxQMxNRF4eKLg6TCMf4DfWN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF\n" +"5PgLZxYWxoK4Mhn5+bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57I\n" +"cXR5f1GJtshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA\n" +"GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv8Ue1fXws\n" +"BOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c\n" +"-----END CERTIFICATE-----",.len=1927}, + +/* Entrust Root Certification Authority - G2 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMCVVMxFjAU\n" +"BgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVn\n" +"YWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9y\n" +"aXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0\n" +"aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcyNTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UE\n" +"BhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVz\n" +"dC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBm\n" +"b3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj\n" +"YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6\n" +"hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/TRU4cctZOMvJyig/3\n" +"gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWNcCG0szLni6LVhjkCsbjSR87k\n" +"yUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hWwcKUs/Ja5CeanyTXxuzQmyWC48zCxEXF\n" +"jJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+\n" +"tVOxMyLlbc9wPBr64ptntoP0jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1Ud\n" +"DwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2f\n" +"kBJmqzANBgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/\n" +"jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZRkfz6/dj\n" +"wUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v1fN2D807iDginWyT\n" +"msQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4RnAuknZoh8/CbCzB428Hch0P+\n" +"vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmHVHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ\n" +"19xOe4pIb4tF9g==\n" +"-----END CERTIFICATE-----",.len=1530}, + +/* Entrust Root Certification Authority - EC1 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkGA1UEBhMC\n" +"VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5u\n" +"ZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVudHJ1c3QsIEluYy4gLSBmb3Ig\n" +"YXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMqRW50cnVzdCBSb290IENlcnRpZmljYXRp\n" +"b24gQXV0aG9yaXR5IC0gRUMxMB4XDTEyMTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8x\n" +"CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3\n" +"LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJ\n" +"bmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD\n" +"ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABIQT\n" +"ydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBtByQnoaD41UcZYUx9\n" +"ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlHBz7MIKNCMEAwDgYDVR0PAQH/\n" +"BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFLdj5xrdjekIplWDpOBqUEFlEUJJ\n" +"MAoGCCqGSM49BAMDA2cAMGQCMGF52OVCR98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHv\n" +"AvX7td/M/k7//qnmpwIwW5nXhTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZB\n" +"WyVgrtBIGu4G\n" +"-----END CERTIFICATE-----",.len=1088}, + +/* CFCA EV ROOT */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJDTjEwMC4G\n" +"A1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQD\n" +"DAxDRkNBIEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkxMjMxMDMwNzAxWjBWMQswCQYD\n" +"VQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y\n" +"aXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJPT1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK\n" +"AoICAQDXXWvNED8fBVnVBU03sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCj\n" +"Z9YMrM8irq93VCpLTIpTUnrD7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3Iv\n" +"HWOX6Jn5/ZOkVIBMUtRSqy5J35DNuF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp\n" +"7hZZLDRJGqgG16iI0gNyejLi6mhNbiyWZXvKWfry4t3uMCz7zEasxGPrb382KzRzEpR/38wm\n" +"nvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7xzbh72fROdOXW3NiGUgthxwG+3SYIElz8AXS\n" +"G7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9fpy25IGvPa931DfSCt/SyZi4QKPaXWnuW\n" +"Fo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqotaK8KgWU6cMGbrU1tVMoqLUuFG7OA5nBF\n" +"DWteNfB/O7ic5ARwiRIlk9oKmSJgamNgTnYGmE69g60dWIolhdLHZR4tjsbftsbhf4oEIRUp\n" +"dPA+nJCdDC7xij5aqgwJHsfVPKPtl8MeNPo4+QgO48BdK4PRVmrJtqhUUy54Mmc9gn900Pvh\n" +"tgVguXDbjgv5E1hvcWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAfBgNVHSMEGDAWgBTj\n" +"/i39KNALtbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd\n" +"BgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIBACXGumvr\n" +"h8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObTej/tUxPQ4i9qecsA\n" +"IyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdLjOztUmCypAbqTuv0axn96/Ua\n" +"4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBSESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9B\n" +"C2sGE5uPhnEFtC+NiWYzKXZUmhH4J/qyP5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rU\n" +"QElsgIfXBDrDMlI1Dlb4pd19xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZeP\n" +"glr4UeWJoBjnaH9dCi77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4R\n" +"UHlzEhLN5mydLIhyPDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe\n" +"/v5WOaHIz16eGWRGENoXkbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+ZAAoACxGV\n" +"2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ5nbv0CO7O6l5s9UC\n" +"Kc2Jo5YPSjXnTkLAdc0Hz+Ys63su\n" +"-----END CERTIFICATE-----",.len=1980}, + +/* OISTE WISeKey Global Root GB CA */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBtMQswCQYD\n" +"VQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBF\n" +"bmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQiBDQTAeFw0x\n" +"NDEyMDExNTAwMzJaFw0zOTEyMDExNTEwMzFaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdX\n" +"SVNlS2V5MSIwIAYDVQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9P\n" +"SVNURSBXSVNlS2V5IEdsb2JhbCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n" +"MIIBCgKCAQEA2Be3HEokKtaXscriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvY\n" +"D06fWvGxWuR51jIjK+FTzJlFXHtPrby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX\n" +"1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNku7dCjmn2\n" +"1HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4oQnc/nSMbsrY9gBQHTC5P99UKFg29ZkM3fiND\n" +"ecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvgGUpuuy9rM2RYk61pv48b74JIxwIDAQAB\n" +"o1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUNQ/INmNe4qPs\n" +"+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZIhvcNAQELBQADggEBAEBM+4ey\n" +"mYGQfp3FsLAmzYh7KzKNbrghcViXfa43FK8+5/ea4n32cZiZBKpDdHij40lhPnOMTZTg+XHE\n" +"thYOU3gf1qKHLwI5gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0VQreUGdNZtGn//3Z\n" +"wLWoo4rOZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEuiHZeeevJuQHHf\n" +"aPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02IcNc1MaRVU\n" +"GpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM=\n" +"-----END CERTIFICATE-----",.len=1343}, + +/* SZAFIR ROOT CA2 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQELBQAwUTEL\n" +"MAkGA1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6ZW5pb3dhIFMuQS4x\n" +"GDAWBgNVBAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkwNzQzMzBaFw0zNTEwMTkwNzQz\n" +"MzBaMFExCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9LcmFqb3dhIEl6YmEgUm96bGljemVuaW93\n" +"YSBTLkEuMRgwFgYDVQQDDA9TWkFGSVIgUk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IB\n" +"DwAwggEKAoIBAQC3vD5QqEvNQLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj\n" +"5/QqGJ3a0a4m7utT3PSQ1hNKDJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd\n" +"3BucPbOw3gAeqDRHu5rr/gsUvTaE2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr6\n" +"3fE9biCloBK0TXC5ztdyO4mTp4CEHCdJckm1/zuVnsHMyAHs6A6KCpbns6aH5db5BSsNl0Bw\n" +"PLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwiieDhZNRnvDF5YTy7ykHNXGoAyDw4jlivAgMB\n" +"AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBQuFqlK\n" +"GLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsFAAOCAQEAtXP4A9xZWx126aMqe5Aosk3A\n" +"M0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw8PRBEew/R40/cof5O/2kbytTAOD/OblqBw7rHRz2\n" +"onKQy4I9EYKL0rufKq8h5mOGnXkZ7/e7DDWQw4rtTw/1zBLZpD67oPwglV9PJi8RI4NOdQcP\n" +"v5vRtB3pEAT+ymCPoky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul4+vJhaAlIDf7js4M\n" +"NIThPIGyd05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6+/NNIxuZMzSg\n" +"LvWpCz/UXeHPhJ/iGcJfitYgHuNztw==\n" +"-----END CERTIFICATE-----",.len=1254}, + +/* Certum Trusted Network CA 2 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"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\n" +"-----END CERTIFICATE-----",.len=2073}, + +/* Hellenic Academic and Research Institutions RootCA 2015 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1IxDzANBgNV\n" +"BAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIElu\n" +"c3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWlj\n" +"IGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTUwHhcNMTUwNzA3MTAxMTIx\n" +"WhcNNDAwNjMwMTAxMTIxWjCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIG\n" +"A1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0\n" +"LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJ\n" +"bnN0aXR1dGlvbnMgUm9vdENBIDIwMTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC\n" +"AQDC+Kk/G4n8PDwEXT2QNrCROnk8ZlrvbTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA4yjsriFB\n" +"zh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+ehiGsxr/CL0BgzuNtFajT0AoAkKAoCFZVedioN\n" +"mToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+6PAQZe104S+nfK8nNLspfZu2zwnI5dMK\n" +"/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06CojXdFPQf/7J31Ycvqm59JCfnxssm5uX+\n" +"Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV9Cz82XBST3i4vTwri5WY9bPRaM8gFH5MXF/ni+X1\n" +"NYEZN9cRCLdmvtNKzoNXADrDgfgXy5I2XdGj2HUb4Ysn6npIQf1FGQatJ5lOwXBH3bWfgVMS\n" +"5bGMSF0xQxfjjMZ6Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2fu/Z8VFRfS0myGlZ\n" +"YeCsargqNhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9muiNX6hME6wGko\n" +"LfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZaycBw/qa9wf\n" +"LgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/\n" +"MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVdctA4GGqd83EkVAswDQYJKoZI\n" +"hvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0IXtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg\n" +"2mF+D1hYc2Ryx+hFjtyp8iY/xnmMsVMIM4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6Hwb\n" +"ISHTGzrMd/K4kPFox/la/vot9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkN\n" +"aeJK9E10A/+yd+2VZ5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRG\n" +"ar9KC/eaj8GsGsVn82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnh\n" +"X9izjFk0WaSrT2y7HxjbdavYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQl033DlZd\n" +"wJVqwjbDG2jJ9SrcR5q+ss7FJej6A7na+RZukYT1HCjI/CbM1xyQVqdfbzoEvM14iQuODy+j\n" +"qk+iGxI9FghAD/FGTNeqewjBCvVtJ94Cj8rDtSvK6evIIVM4pcw72Hc3MKJP2W/R8kCtQXoX\n" +"xdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGaJI7ZjnHKe7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODf\n" +"qiAeW2GFZECpkJcNrVPSWh2HagCXZWK0vm9qp/UsQu0yrbYhnr68\n" +"-----END CERTIFICATE-----",.len=2150}, + +/* Hellenic Academic and Research Institutions ECC RootCA 2015 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzANBgNVBAcT\n" +"BkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3Rp\n" +"dHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hlbGxlbmljIEFjYWRlbWljIGFu\n" +"ZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgRUNDIFJvb3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcx\n" +"MloXDTQwMDYzMDEwMzcxMlowgaoxCzAJBgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBC\n" +"BgNVBAoTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2Vy\n" +"dC4gQXV0aG9yaXR5MUQwQgYDVQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2gg\n" +"SW5zdGl0dXRpb25zIEVDQyBSb290Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKg\n" +"QehLgoRc4vgxEZmGZE4JJS+dQS8KrjVPdJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJajq4onvkt\n" +"TpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoKVlp8aQuqgAkkbH7BRqNCMEAwDwYDVR0TAQH/\n" +"BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFLQiC4KZJAEOnLvkDv2/+5cgk5kq\n" +"MAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaeplSTAGiecMjvAwNW6qef4BENThe5SId6d\n" +"9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7SofTUwJCA3sS61kFyjndc5FZXIhF8siQQ6ME5g4mlRt\n" +"m8rifOoCWCKR\n" +"-----END CERTIFICATE-----",.len=1015}, + +/* ISRG Root X1 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"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\n" +"-----END CERTIFICATE-----",.len=1935}, + +/* AC RAIZ FNMT-RCM */ +{.str="-----BEGIN CERTIFICATE-----\n" +"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\n" +"qhOLUTpPSPaLtrM=\n" +"-----END CERTIFICATE-----",.len=1968}, + +/* Amazon Root CA 1 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsFADA5MQsw\n" +"CQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAx\n" +"MB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNV\n" +"BAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEB\n" +"BQADggEPADCCAQoCggEBALJ4gHHKeNXjca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOg\n" +"Q3pOsqTQNroBvo3bSMgHFzZM9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9\n" +"tBb6dNqcmzU5L/qwIFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAw\n" +"hmahRWa6VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L\n" +"93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQmjgSubJrI\n" +"qg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYE\n" +"FIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUAA4IBAQCY8jdaQZChGsV2USgg\n" +"NiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDIU5PMCCjjmCXPI6T53iHTfIUJrU6adTrC\n" +"C2qJeHZERxhlbI1Bjjt/msv0tadQ1wUsN+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V\n" +"8viTO96LXFvKWlJbYK8U90vvo/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJc\n" +"JmApzyMZFo6IQ6XU5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeX\n" +"eGADbkpyrqXRfboQnoZsG4q5WTP468SQvvG5\n" +"-----END CERTIFICATE-----",.len=1185}, + +/* Amazon Root CA 2 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwFADA5MQsw\n" +"CQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAy\n" +"MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNV\n" +"BAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEB\n" +"BQADggIPADCCAgoCggIBAK2Wny2cSkxKgXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBv\n" +"IITplLGbhQPDW9tK6Mj4kHbZW0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZM\n" +"UnbqQ523BNFQ9lXg1dKmSYXpN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6\n" +"PBJTYv9K8nu+NQWpEjTj82R0Yiw9AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r\n" +"2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvdfLC6HM783k81ds8P+HgfajZRRidhW+mez/CiVX18\n" +"JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAExkv8LV/SasrlX6avvDXbR8O70zoan4G7ptGmh\n" +"32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSSbtqDT6ZjmUyl+17vIWR6IF9sZIUVyzfp\n" +"YgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz7Mt0Q5X5bGlSNscpb/xVA1wf+5+9R+vn\n" +"SUeVC06JIglJ4PVhHvG/LopyboBZ/1c6+XUyo05f7O0oYtlNc/LMgRdg7c3r3NunysV+Ar3y\n" +"VAhU/bQtCSwXVEqY0VThUWcI0u1ufm8/0i2BWSlmy5A5lREedCf+3euvAgMBAAGjQjBAMA8G\n" +"A1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSwDPBMMPQFWAJI/TPl\n" +"Uq9LhONmUjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oAA7CXDpO8Wqj2\n" +"LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY+gn0oJMs\n" +"XdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kSk5Nrp+gvU5LEYFiw\n" +"zAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl7uxMMne0nxrpS10gxdr9HIcW\n" +"xkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygmbtmlyTrIQRNg91CMFa6ybRoVGld45pIq\n" +"2WWQgj9sAq+uEjonljYE1x2igGOpm/HlurR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JO\n" +"cQ3AWEbWaQbLU8uz/mtBzUF+fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn\n" +"9Kr5v2c69BoVYh63n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG\n" +"6lzWE7OE76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H\n" +"9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT4PsJYGw=\n" +"-----END CERTIFICATE-----",.len=1878}, + +/* Amazon Root CA 3 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5MQswCQYD\n" +"VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAzMB4X\n" +"DTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoT\n" +"BkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMzBZMBMGByqGSM49AgEGCCqGSM49\n" +"AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKlui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6A\n" +"F2hiRVd9RFgdszflZwjrZt6jQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGG\n" +"MB0GA1UdDgQWBBSrttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWS\n" +"oxe3jfkrBqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM\n" +"YyRIHN8wfdVoOw==\n" +"-----END CERTIFICATE-----",.len=654}, + +/* Amazon Root CA 4 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5MQswCQYD\n" +"VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSA0MB4X\n" +"DTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoT\n" +"BkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgNDB2MBAGByqGSM49AgEGBSuBBAAi\n" +"A2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhF\n" +"XRbb/egQbeOc4OO9X4Ri83BkM6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYD\n" +"VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc\n" +"84ZtV+WBMAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw\n" +"CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW1KyLa2tJ\n" +"ElMzrdfkviT8tQp21KW8EA==\n" +"-----END CERTIFICATE-----",.len=735}, + +/* TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIxGDAWBgNV\n" +"BAcTD0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxpbXNlbCB2ZSBUZWtu\n" +"b2xvamlrIEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0wKwYDVQQLEyRLYW11IFNlcnRp\n" +"ZmlrYXN5b24gTWVya2V6aSAtIEthbXUgU00xNjA0BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBT\n" +"U0wgS29rIFNlcnRpZmlrYXNpIC0gU3VydW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUw\n" +"ODI1NTVaMIHSMQswCQYDVQQGEwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYD\n" +"VQQKEzlUdXJraXllIEJpbGltc2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAt\n" +"IFRVQklUQUsxLTArBgNVBAsTJEthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBT\n" +"TTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11IFNNIFNTTCBLb2sgU2VydGlmaWthc2kgLSBTdXJ1\n" +"bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr3UwM6q7a9OZLBI3hNmNe5eA\n" +"027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y86Ij5iySrLqP1N+RAjhgleYN1Hzv/bKjF\n" +"xlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INrN3wcwv61A+xXzry0tcXtAA9TNypN9E8M\n" +"g/uGz8v+jE69h/mniyFXnHrfA2eJLJ2XYacQuFWQfw4tJzh03+f92k4S400VIgLI4OD8D62K\n" +"18lUUMw7D8oWgITQUVbDjlZ/iSIzL+aFCr2lqBs23tPcLG07xxO9WSMs5uWk99gL7eqQQESo\n" +"lbuT1dCANLZGeA4fAJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQUZT/HiobGPN08VFw1\n" +"+DrtUgxHV8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL\n" +"BQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifhAHe+SMg1\n" +"qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPfIPP54+M638yclNhO\n" +"T8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4lzwDGrpDxpa5RXI4s6ehlj2R\n" +"e37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X\n" +"8W0jq5Rm+K37DwhuJi1/FwcJsoz7UMCflo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM=\n" +"-----END CERTIFICATE-----",.len=1578}, + +/* GDCA TrustAUTH R5 ROOT */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCQ04x\n" +"MjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8w\n" +"HQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0MTEyNjA1MTMxNVoXDTQwMTIz\n" +"MTE1NTk1OVowYjELMAkGA1UEBhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNB\n" +"VEUgQVVUSE9SSVRZIENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09U\n" +"MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92\n" +"hhJCfVZmPoiC7XJjDp6L3TQsAlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlK\n" +"CvLriXBjTnnEt1u9ol2x8kECK62pOqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+u\n" +"KU49tm7srsHwJ5uu4/Ts765/94Y9cnrrpftZTqfrlYwiOXnhLQiPzLyRuEH3FMEjqcOtmkVE\n" +"s7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ9Cy5WmYqsBebnh52nUpmMUHfP/vFBu8btn4a\n" +"Rjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQxXABZG12ZuGR224HwGGALrIuL4xwp9E7\n" +"PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloPzgsMR6flPri9fcebNaBhlzpBdRfMK5Z3\n" +"KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3GkL30SgLdTMEZeS1SZD2fJpcjyIMGC7J0R38IC+xo7\n" +"0e0gmu9lZJIQDSri3nDxGGeCjGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4oR24qoAATILnsn8JuLww\n" +"oC8N9VKejveSswoAHQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx9hoh49pwBiFYFIeF\n" +"d3mqgnkCAwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlRMA8GA1UdEwEB\n" +"/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfgp8xoWLoB\n" +"DysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZmDRd9FBUb1Ov9H5r2\n" +"XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5COmSdI31R9KrO9b7eGZONn35\n" +"6ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ryL3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1j\n" +"PLHd+PwyvzeG5LuOmCd+uh8W4XAR8gPfJWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw\n" +"9C+df/KQHtZa37dG/OaG+svgIHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3\n" +"cbK1daFQqUBDF8Io2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrX\n" +"mKGcjBBV09tL7ECQ8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQ\n" +"XR4EzzffHqhmsYzmIGrv/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrqT8p+ck0L\n" +"cIymSLumoRT2+1hEmRSuqguTaaApJUqlyyvdimYHFngVV3Eb7PVHhPOeMTd61X8kreS8/f3M\n" +"boPoDKi3QWwH3b08hpcv0g==\n" +"-----END CERTIFICATE-----",.len=1976}, + +/* SSL.com Root Certification Authority RSA */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMx\n" +"DjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9y\n" +"YXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBS\n" +"U0EwHhcNMTYwMjEyMTczOTM5WhcNNDEwMjEyMTczOTM5WjB8MQswCQYDVQQGEwJVUzEOMAwG\n" +"A1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlv\n" +"bjExMC8GA1UEAwwoU1NMLmNvbSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCC\n" +"AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/Wv\n" +"pOz6Sl2RxFdHaxh3a3by/ZPkPQ/CFp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aX\n" +"qhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcCC52GVWH9\n" +"ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/geoeOy3ZExqysdBP+lSgQ36YWkMyv94tZVNHwZ\n" +"pEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkpk8zruFvh/l8lqjRYyMEjVJ0bmBHDOJx+\n" +"PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrFYD3ZfBjVsqnTdXgDciLKOsMf7yzlLqn6\n" +"niy2UUb9rwPW6mBo6oUWNmuF6R7As93EJNyAKoFBbZQ+yODJgUEAnl6/f8UImKIYLEJAs/lv\n" +"OCdLToD0PYFH4Ih86hzOtXVcUS4cK38acijnALXRdMbX5J+tB5O2UzU1/Dfkw/ZdFr4hc96S\n" +"CvigY2q8lpJqPvi8ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi81xtZPCvM8hnIk2sn\n" +"YxnP/Okm+Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4sbE6x/c+cCbqi\n" +"M+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNVHRMBAf8E\n" +"BTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4GA1UdDwEB/wQEAwIB\n" +"hjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGVcpNxJK1ok1iOMq8bs3AD/CUr\n" +"dIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBcHadm47GUBwwyOabqG7B52B2ccETjit3E\n" +"+ZUfijhDPwGFpUenPUayvOUiaPd7nNgsPgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52j\n" +"vATGGAslu1OJD7OAUN5F7kR/q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3\n" +"oFRuIIhxdRjqerQ0cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfd\n" +"SSLBv9jra6x+3uxjMxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90I\n" +"H37hVZkLId6Tngr75qNJvTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/YK9f1JmzJ\n" +"BjSWFupwWRoyeXkLtoh/D1JIPb9s2KJELtFOt3JY04kTlf5Eq/jXixtunLwsoFvVagCvXzfh\n" +"1foQC5ichucmj87w7G6KVwuA406ywKBjYZC6VWg3dGq2ktufoYYitmUnDuy2n0Jg5GfCtdpB\n" +"C8TTi2EbvPofkSvXRAdeuims2cXp71NIWuuA8ShYIc2wBlX7Jz9TkHCpBB5XJ7k=\n" +"-----END CERTIFICATE-----",.len=2089}, + +/* SSL.com Root Certification Authority ECC */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMCVVMxDjAM\n" +"BgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRp\n" +"b24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBFQ0Mw\n" +"HhcNMTYwMjEyMTgxNDAzWhcNNDEwMjEyMTgxNDAzWjB8MQswCQYDVQQGEwJVUzEOMAwGA1UE\n" +"CAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjEx\n" +"MC8GA1UEAwwoU1NMLmNvbSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAG\n" +"ByqGSM49AgEGBSuBBAAiA2IABEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtH\n" +"llirLZXI7Z4INcgn64mMU1jrYor+8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPg\n" +"CemB+vNH06NjMGEwHQYDVR0OBBYEFILRhXMw5zUE044CkvvlpNHEIejNMA8GA1UdEwEB/wQF\n" +"MAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTTjgKS++Wk0cQh6M0wDgYDVR0PAQH/BAQDAgGG\n" +"MAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCWe+0F+S8Tkdzt5fxQaxFGRrMcIQBiu77D\n" +"5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+gA0z5Wajs6O7pdWLjwkspl1+4vAHCGht\n" +"0nxpbl/f5Wpl\n" +"-----END CERTIFICATE-----",.len=942}, + +/* SSL.com EV Root Certification Authority RSA R2 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"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\n" +"Y6GsdqnC+w==\n" +"-----END CERTIFICATE-----",.len=2110}, + +/* SSL.com EV Root Certification Authority ECC */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMCVVMxDjAM\n" +"BgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRp\n" +"b24xNDAyBgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBF\n" +"Q0MwHhcNMTYwMjEyMTgxNTIzWhcNNDEwMjEyMTgxNTIzWjB/MQswCQYDVQQGEwJVUzEOMAwG\n" +"A1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlv\n" +"bjE0MDIGA1UEAwwrU1NMLmNvbSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVD\n" +"QzB2MBAGByqGSM49AgEGBSuBBAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjK\n" +"AMTH6kMAVIbc/R/fALhBYlzccBYy3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1Kthku\n" +"WnBaBu2+8KGwytAJKaNjMGEwHQYDVR0OBBYEFFvKXuXe0oGqzagtZFG22XKbl+ZPMA8GA1Ud\n" +"EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe5d7SgarNqC1kUbbZcpuX5k8wDgYDVR0PAQH/\n" +"BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJN+vp1RPZytRrJPOwPYdGWBrssd9v+1a6\n" +"cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZgh5Mmm7I1HrrW9zzRHM76JTymGoEVW/MS\n" +"D2zuZYrJh6j5B+BimoxcSg==\n" +"-----END CERTIFICATE-----",.len=954}, + +/* GlobalSign Root CA - R6 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"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\n" +"1gLXCmeDBVrJpBA=\n" +"-----END CERTIFICATE-----",.len=1968}, + +/* OISTE WISeKey Global Root GC CA */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQswCQYDVQQG\n" +"EwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRv\n" +"cnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQyBDQTAeFw0xNzA1\n" +"MDkwOTQ4MzRaFw00MjA1MDkwOTU4MzNaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNl\n" +"S2V5MSIwIAYDVQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNU\n" +"RSBXSVNlS2V5IEdsb2JhbCBSb290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQ\n" +"wMYPchi82PG6s4nieUqjFqdrVCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQ\n" +"mqJLIX4Wp2OQ0jnUsYd4XxiWD1AbNTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8E\n" +"BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7TrYy0UGYw\n" +"EAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0EAwMDaAAwZQIwJsdpW9zV57LnyAyMjMPdeYwb\n" +"Y9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtkAjEA2zQgMgj/mkkCtojeFK9dbJlxjRo/\n" +"i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9\n" +"-----END CERTIFICATE-----",.len=893}, + +/* UCA Global G2 Root */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFRjCCAy6gAwIBAgIQXd+x2lqj7V2+WmUgZQOQ7zANBgkqhkiG9w0BAQsFADA9MQswCQYD\n" +"VQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxGzAZBgNVBAMMElVDQSBHbG9iYWwgRzIgUm9v\n" +"dDAeFw0xNjAzMTEwMDAwMDBaFw00MDEyMzEwMDAwMDBaMD0xCzAJBgNVBAYTAkNOMREwDwYD\n" +"VQQKDAhVbmlUcnVzdDEbMBkGA1UEAwwSVUNBIEdsb2JhbCBHMiBSb290MIICIjANBgkqhkiG\n" +"9w0BAQEFAAOCAg8AMIICCgKCAgEAxeYrb3zvJgUno4Ek2m/LAfmZmqkywiKHYUGRO8vDaBsG\n" +"xUypK8FnFyIdK+35KYmToni9kmugow2ifsqTs6bRjDXVdfkX9s9FxeV67HeToI8jrg4aA3++\n" +"1NDtLnurRiNb/yzmVHqUwCoV8MmNsHo7JOHXaOIxPAYzRrZUEaalLyJUKlgNAQLx+hVRZ2zA\n" +"+te2G3/RVogvGjqNO7uCEeBHANBSh6v7hn4PJGtAnTRnvI3HLYZveT6OqTwXS3+wmeOwcWDc\n" +"C/Vkw85DvG1xudLeJ1uK6NjGruFZfc8oLTW4lVYa8bJYS7cSN8h8s+1LgOGN+jIjtm+3SJUI\n" +"sUROhYw6AlQgL9+/V087OpAh18EmNVQg7Mc/R+zvWr9LesGtOxdQXGLYD0tK3Cv6brxzks3s\n" +"x1DoQZbXqX5t2Okdj4q1uViSukqSKwxW/YDrCPBeKW4bHAyvj5OJrdu9o54hyokZ7N+1wxrr\n" +"Fv54NkzWbtA+FxyQF2smuvt6L78RHBgOLXMDj6DlNaBa4kx1HXHhOThTeEDMg5PXCp6dW4+K\n" +"5OXgSORIskfNTip1KnvyIvbJvgmRlld6iIis7nCs+dwp4wwcOxJORNanTrAmyPPZGpeRaOrv\n" +"jUYG0lZFWJo8DA+DuAUlwznPO6Q0ibd5Ei9Hxeepl2n8pndntd978XplFeRhVmUCAwEAAaNC\n" +"MEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFIHEjMz15DD/\n" +"pQwIX4wVZyF0Ad/fMA0GCSqGSIb3DQEBCwUAA4ICAQATZSL1jiutROTL/7lo5sOASD0Ee/oj\n" +"L3rtNtqyzm325p7lX1iPyzcyochltq44PTUbPrw7tgTQvPlJ9Zv3hcU2tsu8+Mg51eRfB70V\n" +"VJd0ysrtT7q6ZHafgbiERUlMjW+i67HM0cOU2kTC5uLqGOiiHycFutfl1qnN3e92mI0ADs0b\n" +"+gO3joBYDic/UvuUospeZcnWhNq5NXHzJsBPd+aBJ9J3O5oUb3n09tDh05S60FdRvScFDcH9\n" +"yBIw7m+NESsIndTUv4BFFJqIRNow6rSn4+7vW4LVPtateJLbXDzz2K36uGt/xDYotgIVilQs\n" +"nLAXc47QN6MUPJiVAAwpBVueSUmxX8fjy88nZY41F7dXyDDZQVu5FLbowg+UMaeUmMxq67Xh\n" +"J/UQqAHojhJi6IjMtX9Gl8CbEGY4GjZGXyJoPd/JxhMnq1MGrKI8hgZlb7F+sSlEmqO6SWko\n" +"aY/X5V+tBIZkbxqgDMUIYs6Ao9Dz7GjevjPHF1t/gMRMTLGmhIrDO7gJzRSBuhjjVFc2/tsv\n" +"fEehOjPI+Vg7RE+xygKJBJYoaMVLuCaJu9YzL1DV/pqJuhgyklTGW+Cd+V7lDSKb9triyCGy\n" +"YiGqhkCyLmTTX8jjfhFnRR8F/uOi77Oos/N9j/gMHyIfLXC0uAE0djAA5SN4p1bXUB+K+wb1\n" +"whnw0A==\n" +"-----END CERTIFICATE-----",.len=1887}, + +/* UCA Extended Validation Root */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFWjCCA0KgAwIBAgIQT9Irj/VkyDOeTzRYZiNwYDANBgkqhkiG9w0BAQsFADBHMQswCQYD\n" +"VQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBFeHRlbmRlZCBWYWxp\n" +"ZGF0aW9uIFJvb3QwHhcNMTUwMzEzMDAwMDAwWhcNMzgxMjMxMDAwMDAwWjBHMQswCQYDVQQG\n" +"EwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0\n" +"aW9uIFJvb3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCpCQcoEwKwmeBkqh5D\n" +"FnpzsZGgdT6o+uM4AHrsiWogD4vFsJszA1qGxliG1cGFu0/GnEBNyr7uaZa4rYEwmnySBesF\n" +"K5pI0Lh2PpbIILvSsPGP2KxFRv+qZ2C0d35qHzwaUnoEPQc8hQ2E0B92CvdqFN9y4zR8V05W\n" +"AT558aopO2z6+I9tTcg1367r3CTueUWnhbYFiN6IXSV8l2RnCdm/WhUFhvMJHuxYMjMR83dk\n" +"sHYf5BA1FxvyDrFspCqjc/wJHx4yGVMR59mzLC52LqGj3n5qiAno8geK+LLNEOfic0CTuwjR\n" +"P+H8C5SzJe98ptfRr5//lpr1kXuYC3fUfugH0mK1lTnj8/FtDw5lhIpjVMWAtuCeS31HJqcB\n" +"CF3RiJ7XwzJE+oJKCmhUfzhTA8ykADNkUVkLo4KRel7sFsLzKuZi2irbWWIQJUoqgQtHB0MG\n" +"cIfS+pMRKXpITeuUx3BNr2fVUbGAIAEBtHoIppB/TuDvB0GHr2qlXov7z1CymlSvw4m6WC31\n" +"MJixNnI5fkkE/SmnTHnkBVfblLkWU41Gsx2VYVdWf6/wFlthWG82UBEL2KwrlRYaDh8IzTY0\n" +"ZRBiZtWAXxQgXy0MoHgKaNYs1+lvK9JKBZP8nm9rZ/+I8U6laUpSNwXqxhaN0sSZ0YIrO7o1\n" +"dfdRUVjzyAfd5LQDfwIDAQABo0IwQDAdBgNVHQ4EFgQU2XQ65DA9DfcS3H5aBZ8eNJr34RQw\n" +"DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBADaN\n" +"l8xCFWQpN5smLNb7rhVpLGsaGvdftvkHTFnq88nIua7Mui563MD1sC3AO6+fcAURap8lTwEp\n" +"cOPlDOHqWnzcSbvBHiqB9RZLcpHIojG5qtr8nR/zXUACE/xOHAbKsxSQVBcZEhrxH9cMaVr2\n" +"cXj0lH2RC47skFSOvG+hTKv8dGT9cZr4QQehzZHkPJrgmzI5c6sq1WnIeJEmMX3ixzDx/BR4\n" +"dxIOE/TdFpS/S2d7cFOFyrC78zhNLJA5wA3CXWvp4uXViI3WLL+rG761KIcSF3Ru/H38j9CH\n" +"JrAb+7lsq+KePRXBOy5nAliRn+/4Qh8st2j1da3Ptfb/EX3C8CSlrdP6oDyp+l3cpaDvRKS+\n" +"1ujl5BOWF3sGPjLtx7dCvHaj2GU4Kzg1USEODm8uNBNA4StnDG1KQTAYI1oyVZnJF+A83vbs\n" +"ea0rWBmirSwiGpWOvpaQXUJXxPkUAzUrHC1RVwinOt4/5Mi0A3PCwSaAuwtCH60NryZy2sy+\n" +"s6ODWA2CxR9GUeOcGMyNm43sSet1UNWMKFnKdDTajAshqx7qG+XH/RU+wBeq+yNuJkbL+vmx\n" +"cmtpzyKEC2IPrNkZAJSidjzULZrtBJ4tBmIQN1IchXIbJ+XMxjHsN+xjWZsLHXbMfjKaiJUI\n" +"NlK73nZfdklJrX+9ZSCyycErdhh2n1ax\n" +"-----END CERTIFICATE-----",.len=1911}, + +/* Certigna Root CA */ +{.str="-----BEGIN CERTIFICATE-----\n" +"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\n" +"2k+KYTlerj1KjL0=\n" +"-----END CERTIFICATE-----",.len=2260}, + +/* emSign Root CA - G1 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYDVQQGEwJJ\n" +"TjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9sb2dpZXMg\n" +"TGltaXRlZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBHMTAeFw0xODAyMTgxODMwMDBa\n" +"Fw00MzAyMTgxODMwMDBaMGcxCzAJBgNVBAYTAklOMRMwEQYDVQQLEwplbVNpZ24gUEtJMSUw\n" +"IwYDVQQKExxlTXVkaHJhIFRlY2hub2xvZ2llcyBMaW1pdGVkMRwwGgYDVQQDExNlbVNpZ24g\n" +"Um9vdCBDQSAtIEcxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0u76WaK7p1b\n" +"1TST0Bsew+eeuGQzf2N4aLTNLnF115sgxk0pvLZoYIr3IZpWNVrzdr3YzZr/k1ZLpVkGoZM0\n" +"Kd0WNHVO8oG0x5ZOrRkVUkr+PHB1cM2vK6sVmjM8qrOLqs1D/fXqcP/tzxE7lM5OMhbTI0Aq\n" +"d7OvPAEsbO2ZLIvZTmmYsvePQbAyeGHWDV/D+qJAkh1cF+ZwPjXnorfCYuKrpDhMtTk1b+oD\n" +"afo6VGiFbdbyL0NVHpENDtjVaqSW0RM8LHhQ6DqS0hdW5TUaQBw+jSztOd9C4INBdN+jzcKG\n" +"YEho42kLVACL5HZpIQ15TjQIXhTCzLG3rdd8cIrHhQIDAQABo0IwQDAdBgNVHQ4EFgQU++8N\n" +"hp6w492pufEhF38+/PB3KxowDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJ\n" +"KoZIhvcNAQELBQADggEBAFn/8oz1h31xPaOfG1vR2vjTnGs2vZupYeveFix0PZ7mddrXuqe8\n" +"QhfnPZHr5X3dPpzxz5KsbEjMwiI/aTvFthUvozXGaCocV685743QNcMYDHsAVhzNixl03r4P\n" +"EuDQqqE/AjSxcM6dGNYIAwlG7mDgfrbESQRRfXBgvKqy/3lyeqYdPV8q+Mri/Tm3R7nrft8E\n" +"I6/6nAYH6ftjk4BAtcZsCjEozgyfz7MjNYBBjWzEN3uBL4ChQEKF6dk4jeihU80Bv2noWgby\n" +"RQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxxiN66zB+A\n" +"fko=\n" +"-----END CERTIFICATE-----",.len=1299}, + +/* emSign ECC Root CA - G3 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQGEwJJTjET\n" +"MBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGlt\n" +"aXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMwHhcNMTgwMjE4MTgzMDAw\n" +"WhcNNDMwMjE4MTgzMDAwWjBrMQswCQYDVQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTEl\n" +"MCMGA1UEChMcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWdu\n" +"IEVDQyBSb290IENBIC0gRzMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3i\n" +"AhKAnjlfSU2fySU0WXTsuwYc58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4P\n" +"PwT01xySfvalY8L1X44uT6EYGQIrMgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuB\n" +"zhccLikenEhjQjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQD\n" +"AwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+DCBeQyh+KTOgNG3qxrdWBCUfvO6wIBHxcmbHt\n" +"RwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7jHvrZQnD+JbNR6iC8hZVdyR+EhCVBCyj\n" +"-----END CERTIFICATE-----",.len=856}, + +/* emSign Root CA - C1 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIDczCCAlugAwIBAgILAK7PALrEzzL4Q7IwDQYJKoZIhvcNAQELBQAwVjELMAkGA1UEBhMC\n" +"VVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQD\n" +"ExNlbVNpZ24gUm9vdCBDQSAtIEMxMB4XDTE4MDIxODE4MzAwMFoXDTQzMDIxODE4MzAwMFow\n" +"VjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEg\n" +"SW5jMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEMxMIIBIjANBgkqhkiG9w0BAQEFAAOC\n" +"AQ8AMIIBCgKCAQEAz+upufGZBczYKCFK83M0UYRWEPWgTywS4/oTmifQz/l5GnRfHXk5/Fv4\n" +"cI7gklL35CX5VIPZHdPIWoU/Xse2B+4+wM6ar6xWQio5JXDWv7V7Nq2s9nPczdcdioOl+yuQ\n" +"FTdrHCZH3DspVpNqs8FqOp099cGXOFgFixwR4+S0uF2FHYP+eF8LRWgYSKVGczQ7/g/IdrvH\n" +"GPMF0Ybzhe3nudkyrVWIzqa2kbBPrH4VI5b2P/AgNBbeCsbEBEV5f6f9vtKppa+cxSMq9zwh\n" +"bL2vj07FOrLzNBL834AaSaTUqZX3noleoomslMuoaJuvimUnzYnu3Yy1aylwQ6BpC+S5DwID\n" +"AQABo0IwQDAdBgNVHQ4EFgQU/qHgcB4qAzlSWkK+XJGFehiqTbUwDgYDVR0PAQH/BAQDAgEG\n" +"MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAMJKVvoVIXsoounlHfv4LcQ5\n" +"lkFMOycsxGwYFYDGrK9HWS8mC+M2sO87/kOXSTKZEhVb3xEp/6tT+LvBeA+snFOvV71ojD1p\n" +"M/CjoCNjO2RnIkSt1XHLVip4kqNPEjE2NuLe/gDEo2APJ62gsIq1NnpSob0n9CAnYuhNlCQT\n" +"5AoE6TyrLshDCUrGYQTlSTR+08TI9Q/Aqum6VF7zYytPT1DU/rl7mYw9wC68AivTxEDkigcx\n" +"HpvOJpkT+xHqmiIMERnHXhuBUDDIlhJu58tBf5E7oke3VIAb3ADMmpDqw8NQBmIMMMAVSKeo\n" +"WXzhriKi4gp6D/piq1JM4fHfyr6DDUI=\n" +"-----END CERTIFICATE-----",.len=1254}, + +/* emSign ECC Root CA - C3 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIICKzCCAbGgAwIBAgIKe3G2gla4EnycqDAKBggqhkjOPQQDAzBaMQswCQYDVQQGEwJVUzET\n" +"MBEGA1UECxMKZW1TaWduIFBLSTEUMBIGA1UEChMLZU11ZGhyYSBJbmMxIDAeBgNVBAMTF2Vt\n" +"U2lnbiBFQ0MgUm9vdCBDQSAtIEMzMB4XDTE4MDIxODE4MzAwMFoXDTQzMDIxODE4MzAwMFow\n" +"WjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEg\n" +"SW5jMSAwHgYDVQQDExdlbVNpZ24gRUNDIFJvb3QgQ0EgLSBDMzB2MBAGByqGSM49AgEGBSuB\n" +"BAAiA2IABP2lYa57JhAd6bciMK4G9IGzsUJxlTm801Ljr6/58pc1kjZGDoeVjbk5Wum739D+\n" +"yAdBPLtVb4OjavtisIGJAnB9SMVK4+kiVCJNk7tCDK93nCOmfddhEc5lx/h//vXyqaNCMEAw\n" +"HQYDVR0OBBYEFPtaSNCAIEDyqOkAB2kZd6fmw/TPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB\n" +"Af8EBTADAQH/MAoGCCqGSM49BAMDA2gAMGUCMQC02C8Cif22TGK6Q04ThHK1rt0c3ta13FaP\n" +"WEBaLd4gTCKDypOofu4SQMfWh0/434UCMBwUZOR8loMRnLDRWmFLpg9J0wD8ofzkpf9/rdcw\n" +"0Md3f76BB1UwUCAU9Vc4CqgxUQ==\n" +"-----END CERTIFICATE-----",.len=812}, + +/* Hongkong Post Root CA 3 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFzzCCA7egAwIBAgIUCBZfikyl7ADJk0DfxMauI7gcWqQwDQYJKoZIhvcNAQELBQAwbzEL\n" +"MAkGA1UEBhMCSEsxEjAQBgNVBAgTCUhvbmcgS29uZzESMBAGA1UEBxMJSG9uZyBLb25nMRYw\n" +"FAYDVQQKEw1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25na29uZyBQb3N0IFJvb3QgQ0Eg\n" +"MzAeFw0xNzA2MDMwMjI5NDZaFw00MjA2MDMwMjI5NDZaMG8xCzAJBgNVBAYTAkhLMRIwEAYD\n" +"VQQIEwlIb25nIEtvbmcxEjAQBgNVBAcTCUhvbmcgS29uZzEWMBQGA1UEChMNSG9uZ2tvbmcg\n" +"UG9zdDEgMB4GA1UEAxMXSG9uZ2tvbmcgUG9zdCBSb290IENBIDMwggIiMA0GCSqGSIb3DQEB\n" +"AQUAA4ICDwAwggIKAoICAQCziNfqzg8gTr7m1gNt7ln8wlffKWihgw4+aMdoWJwcYEuJQwy5\n" +"1BWy7sFOdem1p+/l6TWZ5Mwc50tfjTMwIDNT2aa71T4Tjukfh0mtUC1Qyhi+AViiE3CWu4mI\n" +"VoBc+L0sPOFMV4i707mV78vH9toxdCim5lSJ9UExyuUmGs2C4HDaOym71QP1mbpV9WTRYA6z\n" +"iUm4ii8F0oRFKHyPaFASePwLtVPLwpgchKOesL4jpNrcyCse2m5FHomY2vkALgbpDDtw1VAl\n" +"iJnLzXNg99X/NWfFobxeq81KuEXryGgeDQ0URhLj0mRiikKYvLTGCAj4/ahMZJx2Ab0vqWwz\n" +"D9g/KLg8aQFChn5pwckGyuV6RmXpwtZQQS4/t+TtbNe/JgERohYpSms0BpDsE9K2+2p20jzt\n" +"8NYt3eEV7KObLyzJPivkaTv/ciWxNoZbx39ri1UbSsUgYT2uy1DhCDq+sI9jQVMwCFk8mB13\n" +"umOResoQUGC/8Ne8lYePl8X+l2oBlKN8W4UdKjk60FSh0Tlxnf0h+bV78OLgAo9uliQlLKAe\n" +"LKjEiafv7ZkGL7YKTE/bosw3Gq9HhS2KX8Q0NEwA/RiTZxPRN+ZItIsGxVd7GYYKecsAyVKv\n" +"Qv83j+GjHno9UKtjBucVtT+2RTeUN7F+8kjDf8V1/peNRY8apxpyKBpADwIDAQABo2MwYTAP\n" +"BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQXnc0ei9Y5K3DT\n" +"XNSguB+wAPzFYTAdBgNVHQ4EFgQUF53NHovWOStw01zUoLgfsAD8xWEwDQYJKoZIhvcNAQEL\n" +"BQADggIBAFbVe27mIgHSQpsY1Q7XZiNc4/6gx5LS6ZStS6LG7BJ8dNVI0lkUmcDrudHr9Egw\n" +"W62nV3OZqdPlt9EuWSRY3GguLmLYauRwCy0gUCCkMpXRAJi70/33MvJJrsZ64Ee+bs7Lo3I6\n" +"LWldy8joRTnU+kLBEUx3XZL7av9YROXrgZ6voJmtvqkBZss4HTzfQx/0TW60uhdG/H39h4F5\n" +"ag0zD/ov+BS5gLNdTaqX4fnkGMX41TiMJjz98iji7lpJiCzfeT2OnpA8vUFKOt1b9pq0zj8l\n" +"MH8yfaIDlNDceqFS3m6TjRgm/VWsvY+b0s+v54Ysyx8Jb6NvqYTUc79NoXQbTiNg8swOqn+k\n" +"nEwlqLJmOzj/2ZQw9nKEvmhVEA/GcywWaZMH/rFF7buiVWqw2rVKAiUnhde3t4ZEFolsgCs+\n" +"l6mc1X5VTMbeRRAc6uk7nwNT7u56AQIWeNTowr5GdogTPyK7SBIdUgC0An4hGh6cJfTzPV4e\n" +"0hz5sy229zdcxsshTrD3mUcYhcErulWuBurQB7Lcq9CClnXO0lD+mefPL5/ndtFhKvshuzHQ\n" +"qp9HpLIiyhY6UFfEW0NnxWViA0kB60PZ2Pierc+xYw5F9KBaLJstxabArahH9CdMOA0uG0k7\n" +"UvToiIMrVCjU8jVStDKDYmlkDJGcn5fqdBb9HxEGmpv0\n" +"-----END CERTIFICATE-----",.len=2069}, + +/* Entrust Root Certification Authority - G4 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIGSzCCBDOgAwIBAgIRANm1Q3+vqTkPAAAAAFVlrVgwDQYJKoZIhvcNAQELBQAwgb4xCzAJ\n" +"BgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVu\n" +"dHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJbmMu\n" +"IC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0\n" +"aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0MB4XDTE1MDUyNzExMTExNloXDTM3MTIyNzExNDEx\n" +"Nlowgb4xCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9T\n" +"ZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRy\n" +"dXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3Qg\n" +"Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0MIICIjANBgkqhkiG9w0BAQEFAAOC\n" +"Ag8AMIICCgKCAgEAsewsQu7i0TD/pZJH4i3DumSXbcr3DbVZwbPLqGgZ2K+EbTBwXX7zLtJT\n" +"meH+H17ZSK9dE43b/2MzTdMAArzE+NEGCJR5WIoV3imz/f3ET+iq4qA7ec2/a0My3dl0ELn3\n" +"9GjUu9CH1apLiipvKgS1sqbHoHrmSKvS0VnM1n4j5pds8ELl3FFLFUHtSUrJ3hCX1nbB76W1\n" +"NhSXNdh4IjVS70O92yfbYVaCNNzLiGAMC1rlLAHGVK/XqsEQe9IFWrhAnoanw5CGAlZSCXqc\n" +"0ieCU0plUmr1POeo8pyvi73TDtTUXm6Hnmo9RR3RXRv06QqsYJn7ibT/mCzPfB3pAqoEmh64\n" +"3IhuJbNsZvc8kPNXwbMv9W3y+8qh+CmdRouzavbmZwe+LGcKKh9asj5XxNMhIWNlUpEbsZmO\n" +"eX7m640A2Vqq6nPopIICR5b+W45UYaPrL0swsIsjdXJ8ITzI9vF01Bx7owVV7rtNOzK+mndm\n" +"nqxpkCIHH2E6lr7lmk/MBTwoWdPBDFSoWWG9yHJM6Nyfh3+9nEg2XpWjDrk4JFX8dWbrAuMI\n" +"NClKxuMrLzOg2qOGpRKX/YAr2hRC45K9PvJdXmd0LhyIRyk0X+IyqJwlN4y6mACXi0mWHv0l\n" +"iqzc2thddG5msP9E36EYxr5ILzeUePiVSj9/E15dWf10hkNjc0kCAwEAAaNCMEAwDwYDVR0T\n" +"AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJ84xFYjwznooHFs6FRM5Og6\n" +"sb9nMA0GCSqGSIb3DQEBCwUAA4ICAQAS5UKme4sPDORGpbZgQIeMJX6tuGguW8ZAdjwD+MlZ\n" +"9POrYs4QjbRaZIxowLByQzTSGwv2LFPSypBLhmb8qoMi9IsabyZIrHZ3CL/FmFz0Jomee8O5\n" +"ZDIBf9PD3Vht7LGrhFV0d4QEJ1JrhkzO3bll/9bGXp+aEJlLdWr+aumXIOTkdnrG0CSqkM0g\n" +"kLpHZPt/B7NTeLUKYvJzQ85BK4FqLoUWlFPUa19yIqtRLULVAJyZv967lDtX/Zr1hstWO1uI\n" +"AeV8KEsD+UmDfLJ/fOPtjqF/YFOOVZ1QNBIPt5d7bIdKROf1beyAN/BYGW5KaHbwH5Lk6rWS\n" +"02FREAutp9lfx1/cH6NcjKF+m7ee01ZvZl4HliDtC3T7Zk6LERXpgUl+b7DUUH8i119lAg2m\n" +"9IUe2K4GS0qn0jFmwvjO5QimpAKWRGhXxNUzzxkvFMSUHHuk2fCfDrGA4tGeEWSpiBE6doLl\n" +"YsKA2KSD7ZPvfC+QsDJMlhVoSFLUmQjAJOgc47OlIQ6SwJAfzyBfyjs4x7dtOvPmRLgOMWuI\n" +"jnDrnBdSqEGULoe256YSxXXfW8AKbnuk5F6G+TaU33fD6Q3AOfF5u0aOq0NZJ7cguyPpVkAh\n" +"7DE9ZapD8j3fcEThuk0mEDuYn/PIjhs4ViFqUZPTkcpG2om3PVODLAgfi49T3f+sHw==\n" +"-----END CERTIFICATE-----",.len=2239}, + +/* Microsoft ECC Root Certificate Authority 2017 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQswCQYDVQQG\n" +"EwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNyb3Nv\n" +"ZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwHhcNMTkxMjE4MjMwNjQ1\n" +"WhcNNDIwNzE4MjMxNjA0WjBlMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENv\n" +"cnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0\n" +"aG9yaXR5IDIwMTcwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATUvD0CQnVBEyPNgASGAlEvaqiB\n" +"YgtlzPbKnR5vSmZRogPZnZH6thaxjG7efM3beaYvzrvOcS/lpaso7GMEZpn4+vKTEAXhgShC\n" +"48Zo9OYbhGBKia/teQ87zvH2RPUBeMCjVDBSMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8E\n" +"BTADAQH/MB0GA1UdDgQWBBTIy5lycFIM+Oa+sgRXKSrPQhDtNTAQBgkrBgEEAYI3FQEEAwIB\n" +"ADAKBggqhkjOPQQDAwNoADBlAjBY8k3qDPlfXu5gKcs68tvWMoQZP3zVL8KxzJOuULsJMsbG\n" +"7X7JNpQS5GiFBqIb0C8CMQCZ6Ra0DvpWSNSkMBaReNtUjGUBiudQZsIxtzm6uBoiB078a1QW\n" +"IP8rtedMDE2mT3M=\n" +"-----END CERTIFICATE-----",.len=873}, + +/* Microsoft RSA Root Certificate Authority 2017 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBlMQswCQYD\n" +"VQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNy\n" +"b3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwHhcNMTkxMjE4MjI1\n" +"MTIyWhcNNDIwNzE4MjMwMDIzWjBlMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0\n" +"IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUg\n" +"QXV0aG9yaXR5IDIwMTcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKW76UM4wp\n" +"lZEWCpW9R2LBifOZNt9GkMml7Xhqb0eRaPgnZ1AzHaGm++DlQ6OEAlcBXZxIQIJTELy/xzto\n" +"kLaCLeX0ZdDMbRnMlfl7rEqUrQ7eS0MdhweSE5CAg2Q1OQT85elss7YfUJQ4ZVBcF0a5toW1\n" +"HLUX6NZFndiyJrDKxHBKrmCk3bPZ7Pw71VdyvD/IybLeS2v4I2wDwAW9lcfNcztmgGTjGqwu\n" +"+UcF8ga2m3P1eDNbx6H7JyqhtJqRjJHTOoI+dkC0zVJhUXAoP8XFWvLJjEm7FFtNyP9nTUwS\n" +"lq31/niol4fX/V4ggNyhSyL71Imtus5Hl0dVe49FyGcohJUcaDDv70ngNXtk55iwlNpNhTs+\n" +"VcQor1fznhPbRiefHqJeRIOkpcrVE7NLP8TjwuaGYaRSMLl6IE9vDzhTyzMMEyuP1pq9Ksgt\n" +"sRx9S1HKR9FIJ3Jdh+vVReZIZZ2vUpC6W6IYZVcSn2i51BVrlMRpIpj0M+Dt+VGOQVDJNE92\n" +"kKz8OMHY4Xu54+OU4UZpyw4KUGsTuqwPN1q3ErWQgR5WrlcihtnJ0tHXUeOrO8ZV/R4O03QK\n" +"0dqq6mm4lyiPSMQH+FJDOvTKVTUssKZqwJz58oHhEmrARdlns87/I6KJClTUFLkqqNfs+avN\n" +"JVgyeY+QW5g5xAgGwax/Dj0ApQIDAQABo1QwUjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/\n" +"BAUwAwEB/zAdBgNVHQ4EFgQUCctZf4aycI8awznjwNnpv7tNsiMwEAYJKwYBBAGCNxUBBAMC\n" +"AQAwDQYJKoZIhvcNAQEMBQADggIBAKyvPl3CEZaJjqPnktaXFbgToqZCLgLNFgVZJ8og6Lq4\n" +"6BrsTaiXVq5lQ7GPAJtSzVXNUzltYkyLDVt8LkS/gxCP81OCgMNPOsduET/m4xaRhPtthH80\n" +"dK2Jp86519efhGSSvpWhrQlTM93uCupKUY5vVau6tZRGrox/2KJQJWVggEbbMwSubLWYdFQl\n" +"3JPk+ONVFT24bcMKpBLBaYVu32TxU5nhSnUgnZUP5NbcA/FZGOhHibJXWpS2qdgXKxdJ5XbL\n" +"wVaZOjex/2kskZGT4d9Mozd2TaGf+G0eHdP67Pv0RR0Tbc/3WeUiJ3IrhvNXuzDtJE3cfVa7\n" +"o7P4NHmJweDyAmH3pvwPuxwXC65B2Xy9J6P9LjrRk5Sxcx0ki69bIImtt2dmefU6xqaWM/5T\n" +"kshGsRGRxpl/j8nWZjEgQRCHLQzWwa80mMpkg/sTV9HB8Dx6jKXB/ZUhoHHBk2dxEuqPiApp\n" +"GWSZI1b7rCoucL5mxAyE7+WL85MB+GqQk2dLsmijtWKP6T+MejteD+eMuMZ87zf9dOLITzNy\n" +"4ZQ5bb0Sr74MTnB8G2+NszKTc0QWbej09+CVgI+WXTik9KveCjCHk9hNAHFiRSdLOkKEW39l\n" +"t2c0Ui2cFmuqqNh7o0JMcccMyj6D5KbvtwEwXlGjefVwaaZBRA+GsCyRxj3qrg+E\n" +"-----END CERTIFICATE-----",.len=2016}, + +/* e-Szigno Root CA 2017 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNVBAYTAkhV\n" +"MREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRkLjEXMBUGA1UEYQwO\n" +"VkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJvb3QgQ0EgMjAxNzAeFw0xNzA4\n" +"MjIxMjA3MDZaFw00MjA4MjIxMjA3MDZaMHExCzAJBgNVBAYTAkhVMREwDwYDVQQHDAhCdWRh\n" +"cGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcx\n" +"HjAcBgNVBAMMFWUtU3ppZ25vIFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEH\n" +"A0IABJbcPYrYsHtvxie+RJCxs1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9o\n" +"MrWGQd+HWyx7xf58etqjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G\n" +"A1UdDgQWBBSHERUI0arBeAyxr87GyZDvvzAEwDAfBgNVHSMEGDAWgBSHERUI0arBeAyxr87G\n" +"yZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEAtVfd14pVCzbhhkT61NlojbjcI4qKDdQvfepz\n" +"7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxOsvxyqltZ+efcMQ==\n" +"-----END CERTIFICATE-----",.len=840}, + +/* certSIGN Root CA G2 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFRzCCAy+gAwIBAgIJEQA0tk7GNi02MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNVBAYTAlJP\n" +"MRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04gUk9PVCBDQSBHMjAe\n" +"Fw0xNzAyMDYwOTI3MzVaFw00MjAyMDYwOTI3MzVaMEExCzAJBgNVBAYTAlJPMRQwEgYDVQQK\n" +"EwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04gUk9PVCBDQSBHMjCCAiIwDQYJKoZI\n" +"hvcNAQEBBQADggIPADCCAgoCggIBAMDFdRmRfUR0dIf+DjuW3NgBFszuY5HnC2/OOwppGnzC\n" +"46+CjobXXo9X69MhWf05N0IwvlDqtg+piNguLWkh59E3GE59kdUWX2tbAMI5Qw02hVK5U2UP\n" +"HULlj88F0+7cDBrZuIt4ImfkabBoxTzkbFpG583H+u/E7Eu9aqSs/cwoUe+StCmrqzWaTOTE\n" +"CMYmzPhpn+Sc8CnTXPnGFiWeI8MgwT0PPzhAsP6CRDiqWhqKa2NYOLQV07YRaXseVO6MGiKs\n" +"cpc/I1mbySKEwQdPzH/iV8oScLumZfNpdWO9lfsbl83kqK/20U6o2YpxJM02PbyWxPFsqa7l\n" +"zw1uKA2wDrXKUXt4FMMgL3/7FFXhEZn91QqhngLjYl/rNUssuHLoPj1PrCy7Lobio3aP5ZMq\n" +"z6WryFyNSwb/EkaseMsUBzXgqd+L6a8VTxaJW732jcZZroiFDsGJ6x9nxUWO/203Nit4ZoOR\n" +"USs9/1F3dmKh7Gc+PoGD4FapUB8fepmrY7+EF3fxDTvf95xhszWYijqy7DwaNz9+j5LP2RIU\n" +"ZNoQAhVB/0/E6xyjyfqZ90bp4RjZsbgyLcsUDFDYg2WD7rlcz8sFWkz6GZdr1l0T08JcVLwy\n" +"c6B49fFtHsufpaafItzRUZ6CeWRgKRM+o/1Pcmqr4tTluCRVLERLiohEnMqE0yo7AgMBAAGj\n" +"QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSCIS1mxteg\n" +"4BXrzkwJd8RgnlRuAzANBgkqhkiG9w0BAQsFAAOCAgEAYN4auOfyYILVAzOBywaK8SJJ6ejq\n" +"kX/GM15oGQOGO0MBzwdw5AgeZYWR5hEit/UCI46uuR59H35s5r0l1ZUa8gWmr4UCb6741jH/\n" +"JclKyMeKqdmfS0mbEVeZkkMR3rYzpMzXjWR91M08KCy0mpbqTfXERMQlqiCA2ClV9+BB/AYm\n" +"/7k29UMUA2Z44RGx2iBfRgB4ACGlHgAoYXhvqAEBj500mv/0OJD7uNGzcgbJceaBxXntC6Z5\n" +"8hMLnPddDnskk7RI24Zf3lCGeOdA5jGokHZwYa+cNywRtYK3qq4kNFtyDGkNzVmf9nGvnAvR\n" +"Cjj5BiKDUyUM/FHE5r7iOZULJK2v0ZXkltd0ZGtxTgI8qoXzIKNDOXZbbFD+mpwUHmUUihW9\n" +"o4JFWklWatKcsWMy5WHgUyIOpwpJ6st+H6jiYoD2EEVSmAYY3qXNL3+q1Ok+CHLsIwMCPKaq\n" +"2LxndD0UF/tUSxfj03k9bWtJySgOLnRQvwzZRjoQhsmnP+mg7H/rpXdYaXHmgwo38oZJar55\n" +"CJD2AhZkPuXaTH4MNMn5X7azKFGnpyuqSfqNZSlO42sTp5SjLVFteAxEy9/eCG/Oo2Sr05WE\n" +"1LlSVHJ7liXMvGnjSG4N0MedJ5qq+BOS3R7fY581qRY27Iy4g/Q9iY/NtBde17MXQRBdJ3Ng\n" +"hVdJIgc=\n" +"-----END CERTIFICATE-----",.len=1887}, + +/* Trustwave Global Certification Authority */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIF2jCCA8KgAwIBAgIMBfcOhtpJ80Y1LrqyMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQG\n" +"EwJVUzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRy\n" +"dXN0d2F2ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0\n" +"aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMxOTM0MTJaFw00MjA4MjMxOTM0MTJaMIGI\n" +"MQswCQYDVQQGEwJVUzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAf\n" +"BgNVBAoMGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEds\n" +"b2JhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC\n" +"AgoCggIBALldUShLPDeS0YLOvR29zd24q88KPuFd5dyqCblXAj7mY2Hf8g+CY66j96xz0Xzn\n" +"swuvCAAJWX/NKSqIk4cXGIDtiLK0thAfLdZfVaITXdHG6wZWiYj+rDKd/VzDBcdu7oaJuogD\n" +"nXIhhpCujwOl3J+IKMujkkkP7NAP4m1ET4BqstTnoApTAbqOl5F2brz81Ws25kCI1nsvXwXo\n" +"LG0R8+eyvpJETNKXpP7ScoFDB5zpET71ixpZfR9oWN0EACyW80OzfpgZdNmcc9kYvkHHNHnZ\n" +"9GLCQ7mzJ7Aiy/k9UscwR7PJPrhq4ufogXBeQotPJqX+OsIgbrv4Fo7NDKm0G2x2EOFYeUY+\n" +"VM6AqFcJNykbmROPDMjWLBz7BegIlT1lRtzuzWniTY+HKE40Cz7PFNm73bZQmq131BnW2hqI\n" +"yE4bJ3XYsgjxroMwuREOzYfwhI0Vcnyh78zyiGG69Gm7DIwLdVcEuE4qFC49DxweMqZiNu5m\n" +"4iK4BUBjECLzMx10coos9TkpoNPnG4CELcU9402x/RpvumUHO1jsQkUm+9jaJXLE9gCxInm9\n" +"43xZYkqcBW89zubWR2OZxiRvchLIrH+QtAuRcOi35hYQcRfO3gZPSEF9NUqjifLJS3tBEW1n\n" +"twiYTOURGa5CgNz7kAXU+FDKvuStx8KU1xad5hePrzb7AgMBAAGjQjBAMA8GA1UdEwEB/wQF\n" +"MAMBAf8wHQYDVR0OBBYEFJngGWcNYtt2s9o9uFvo/ULSMQ6HMA4GA1UdDwEB/wQEAwIBBjAN\n" +"BgkqhkiG9w0BAQsFAAOCAgEAmHNw4rDT7TnsTGDZqRKGFx6W0OhUKDtkLSGm+J1WE2pIPU/H\n" +"PinbbViDVD2HfSMF1OQc3Og4ZYbFdada2zUFvXfeuyk3QAUHw5RSn8pk3fEbK9xGChACMf1K\n" +"aA0HZJDmHvUqoai7PF35owgLEQzxPy0QlG/+4jSHg9bP5Rs1bdID4bANqKCqRieCNqcVtgim\n" +"QlRXtpla4gt5kNdXElE1GYhBaCXUNxeEFfsBctyV3lImIJgm4nb1J2/6ADtKYdkNy1GTKv0W\n" +"BpanI5ojSP5RvbbEsLFUzt5sQa0WZ37b/TjNuThOssFgy50X31ieemKyJo90lZvkWx3SD92Y\n" +"HJtZuSPTMaCm/zjdzyBP6VhWOmfD0faZmZ26NraAL4hHT4a/RDqA5Dccprrql5gR0IRiR2Qe\n" +"qu5AvzSxnI9O4fKSTx+O856X3vOmeWqJcU9LJxdI/uz0UA9PSX3MReO9ekDFQdxhVicGaeVy\n" +"QYHTtgGJoC86cnn+OjC/QezHYj6RS8fZMXZC+fc8Y+wmjHMMfRod6qh8h6jCJ3zhM0EPz8/8\n" +"AKAigJ5Kp28AsEFFtyLKaEjFQqKu3R3y4G5OBVixwJAWKqQ9EEC+j2Jjg6mcgn0tAumDMHzL\n" +"J8n9HmYAsC7TIS+OMxZsmO0QqAfWzJPP29FpHOTKyeC2nOnOcXHebD8WpHk=\n" +"-----END CERTIFICATE-----",.len=2085}, + +/* Trustwave Global ECC P256 Certification Authority */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIICYDCCAgegAwIBAgIMDWpfCD8oXD5Rld9dMAoGCCqGSM49BAMCMIGRMQswCQYDVQQGEwJV\n" +"UzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0\n" +"d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDI1\n" +"NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMxOTM1MTBaFw00MjA4MjMxOTM1\n" +"MTBaMIGRMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNh\n" +"Z28xITAfBgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3\n" +"YXZlIEdsb2JhbCBFQ0MgUDI1NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTBZMBMGByqGSM49\n" +"AgEGCCqGSM49AwEHA0IABH77bOYj43MyCMpg5lOcunSNGLB4kFKA3TjASh3RqMyTpJcGOMoN\n" +"FWLGjgEqZZ2q3zSRLoHB5DOSMcT9CTqmP62jQzBBMA8GA1UdEwEB/wQFMAMBAf8wDwYDVR0P\n" +"AQH/BAUDAwcGADAdBgNVHQ4EFgQUo0EGrJBt0UrrdaVKEJmzsaGLSvcwCgYIKoZIzj0EAwID\n" +"RwAwRAIgB+ZU2g6gWrKuEZ+Hxbb/ad4lvvigtwjzRM4q3wghDDcCIC0mA6AFvWvR9lz4ZcyG\n" +"bbOcNEhjhAnFjXca4syc4XR7\n" +"-----END CERTIFICATE-----",.len=881}, + +/* Trustwave Global ECC P384 Certification Authority */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIICnTCCAiSgAwIBAgIMCL2Fl2yZJ6SAaEc7MAoGCCqGSM49BAMDMIGRMQswCQYDVQQGEwJV\n" +"UzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0\n" +"d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDM4\n" +"NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMxOTM2NDNaFw00MjA4MjMxOTM2\n" +"NDNaMIGRMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNh\n" +"Z28xITAfBgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3\n" +"YXZlIEdsb2JhbCBFQ0MgUDM4NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTB2MBAGByqGSM49\n" +"AgEGBSuBBAAiA2IABGvaDXU1CDFHBa5FmVXxERMuSvgQMSOjfoPTfygIOiYaOs+Xgh+AtycJ\n" +"j9GOMMQKmw6sWASr9zZ9lCOkmwqKi6vr/TklZvFe/oyujUF5nQlgziip04pt89ZF1PKYhDhl\n" +"oKNDMEEwDwYDVR0TAQH/BAUwAwEB/zAPBgNVHQ8BAf8EBQMDBwYAMB0GA1UdDgQWBBRVqYSJ\n" +"0sEyvRjLbKYHTsjnnb6CkDAKBggqhkjOPQQDAwNnADBkAjA3AZKXRRJ+oPM+rRk6ct30UJMD\n" +"Er5E0k9BpIycnR+j9sKS50gU/k6bpZFXrsY3crsCMGclCrEMXu6pY5Jv5ZAL/mYiykf9ijH3\n" +"g/56vxC+GCsej/YpHpRZ744hN8tRmKVuSw==\n" +"-----END CERTIFICATE-----",.len=966}, + +/* NAVER Global Root Certification Authority */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFojCCA4qgAwIBAgIUAZQwHqIL3fXFMyqxQ0Rx+NZQTQ0wDQYJKoZIhvcNAQEMBQAwaTEL\n" +"MAkGA1UEBhMCS1IxJjAkBgNVBAoMHU5BVkVSIEJVU0lORVNTIFBMQVRGT1JNIENvcnAuMTIw\n" +"MAYDVQQDDClOQVZFUiBHbG9iYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0x\n" +"NzA4MTgwODU4NDJaFw0zNzA4MTgyMzU5NTlaMGkxCzAJBgNVBAYTAktSMSYwJAYDVQQKDB1O\n" +"QVZFUiBCVVNJTkVTUyBQTEFURk9STSBDb3JwLjEyMDAGA1UEAwwpTkFWRVIgR2xvYmFsIFJv\n" +"b3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK\n" +"AoICAQC21PGTXLVAiQqrDZBbUGOukJR0F0Vy1ntlWilLp1agS7gvQnXp2XskWjFlqxcX0TM6\n" +"2RHcQDaH38dq6SZeWYp34+hInDEW+j6RscrJo+KfziFTowI2MMtSAuXaMl3Dxeb57hHHi8lE\n" +"HoSTGEq0n+USZGnQJoViAbbJAh2+g1G7XNr4rRVqmfeSVPc0W+m/6imBEtRTkZazkVrd/pBz\n" +"KPswRrXKCAfHcXLJZtM0l/aM9BhK4dA9WkW2aacp+yPOiNgSnABIqKYPszuSjXEOdMWLyEz5\n" +"9JuOuDxp7W87UC9Y7cSw0BwbagzivESq2M0UXZR4Yb8ObtoqvC8MC3GmsxY/nOb5zJ9TNeID\n" +"oKAYv7vxvvTWjIcNQvcGufFt7QSUqP620wbGQGHfnZ3zVHbOUzoBppJB7ASjjw2i1QnK1sua\n" +"8e9DXcCrpUHPXFNwcMmIpi3Ua2FzUCaGYQ5fG8Ir4ozVu53BA0K6lNpfqbDKzE0K70dpAy8i\n" +"+/Eozr9dUGWokG2zdLAIx6yo0es+nPxdGoMuK8u180SdOqcXYZaicdNwlhVNt0xz7hlcxVs+\n" +"Qf6sdWA7G2POAN3aCJBitOUt7kinaxeZVL6HSuOpXgRM6xBtVNbv8ejyYhbLgGvtPe31HzCl\n" +"rkvJE+2KAQHJuFFYwGY6sWZLxNUxAmLpdIQM201GLQIDAQABo0IwQDAdBgNVHQ4EFgQU0p+I\n" +"36HNLL3s9TsBAZMzJ7LrYEswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJ\n" +"KoZIhvcNAQEMBQADggIBADLKgLOdPVQG3dLSLvCkASELZ0jKbY7gyKoNqo0hV4/GPnrK21HU\n" +"UrPUloSlWGB/5QuOH/XcChWB5Tu2tyIvCZwTFrFsDDUIbatjcu3cvuzHV+YwIHHW1xDBE1UB\n" +"jCpD5EHxzzp6U5LOogMFDTjfArsQLtk70pt6wKGm+LUx5vR1yblTmXVHIloUFcd4G7ad6Qz4\n" +"G3bxhYTeodoS76TiEJd6eN4MUZeoIUCLhr0N8F5OSza7OyAfikJW4Qsav3vQIkMsRIz75Sq0\n" +"bBwcupTgE34h5prCy8VCZLQelHsIJchxzIdFV4XTnyliIoNRlwAYl3dqmJLJfGBs32x9SuRw\n" +"TMKeuB330DTHD8z7p/8Dvq1wkNoL3chtl1+afwkyQf3NosxabUzyqkn+Zvjp2DXrDige7kgv\n" +"OtB5CTh8piKCk5XQA76+AqAF3SAi428diDRgxuYKuQl1C/AH6GmWNcf7I4GOODm4RStDeKLR\n" +"LBT/DShycpWbXgnbiUSYqqFJu3FS8r/2/yehNq+4tneI3TqkbZs0kNwUXTC/t+sX5Ie3cdCh\n" +"13cV1ELX8vMxmV2b3RZtP+oGI/hGoiLtk/bdmuYqh7GYVPEi92tF4+KOdh2ajcQGjTa3FPOd\n" +"VGm3jjzVpG2Tgbet9r1ke8LJaDmgkpzNNIaRkPpkUZ3+/uul9XXeifdy\n" +"-----END CERTIFICATE-----",.len=2008}, + +/* AC RAIZ FNMT-RCM SERVIDORES SEGUROS */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIICbjCCAfOgAwIBAgIQYvYybOXE42hcG2LdnC6dlTAKBggqhkjOPQQDAzB4MQswCQYDVQQG\n" +"EwJFUzERMA8GA1UECgwIRk5NVC1SQ00xDjAMBgNVBAsMBUNlcmVzMRgwFgYDVQRhDA9WQVRF\n" +"Uy1RMjgyNjAwNEoxLDAqBgNVBAMMI0FDIFJBSVogRk5NVC1SQ00gU0VSVklET1JFUyBTRUdV\n" +"Uk9TMB4XDTE4MTIyMDA5MzczM1oXDTQzMTIyMDA5MzczM1oweDELMAkGA1UEBhMCRVMxETAP\n" +"BgNVBAoMCEZOTVQtUkNNMQ4wDAYDVQQLDAVDZXJlczEYMBYGA1UEYQwPVkFURVMtUTI4MjYw\n" +"MDRKMSwwKgYDVQQDDCNBQyBSQUlaIEZOTVQtUkNNIFNFUlZJRE9SRVMgU0VHVVJPUzB2MBAG\n" +"ByqGSM49AgEGBSuBBAAiA2IABPa6V1PIyqvfNkpSIeSX0oNnnvBlUdBeh8dHsVnyV0ebAAKT\n" +"RBdp20LHsbI6GA60XYyzZl2hNPk2LEnb80b8s0RpRBNm/dfF/a82Tc4DTQdxz69qBdKiQ1oK\n" +"Um8BA06Oi6NCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE\n" +"FAG5L++/EYZg8k/QQW6rcx/n0m5JMAoGCCqGSM49BAMDA2kAMGYCMQCuSuMrQMN0EfKVrRYj\n" +"3k4MGuZdpSRea0R7/DjiT8ucRRcRTBQnJlU5dUoDzBOQn5ICMQD6SmxgiHPz7riYYqnOK8LZ\n" +"iqZwMR2vsJRM60/G49HzYqc8/5MuB1xJAWdpEgJyv+c=\n" +"-----END CERTIFICATE-----",.len=901}, + +/* GlobalSign Root R46 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUAMEYxCzAJ\n" +"BgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQDExNHbG9iYWxT\n" +"aWduIFJvb3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAwMDAwMFowRjELMAkGA1UE\n" +"BhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24g\n" +"Um9vdCBSNDYwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYd\n" +"pokNRbopiLKkHWPd08EsCVeJOaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+m\n" +"sLZ4j5lUlghYruQGvGIFAha/r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTq\n" +"a1VbkNud316HCkD7rRlr+/fKYIje2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/OrffGFqfUo\n" +"0q3v84RLHIf8E6M6cqJaESvWJ3En7YEtbWaBkoe0G1h6zD8K+kZPTXhc+CtI4wSEy132tGqz\n" +"ZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvjK8Cd+RTyG/FWaha/LIWFzXg4mutCagI0GIMX\n" +"TpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0NXfeD412lPFzYE+cCQYDdF3uYM2HSNrpyibXRd\n" +"Qr4G9dlkbgIQrImwTDsHTUB+JMWKmIJ5jqSngiCNI/onccnfxkF0oE32kRbcRoxfKWMxWXEM\n" +"2G/CtjJ9++ZdU6Z+Ffy7dXxd7Pj2Fxzsx2sZy/N78CsHpdlseVR2bJ0cpm4O6XkMqCNqo98b\n" +"MDGfsVR7/mrLZqrcZdCinkqaByFrgY/bxFn63iLABJzjqls2k+g9vXqhnQt2sQvHnf3PmKgG\n" +"wvgqo6GDoLclcqUC4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB\n" +"/zAdBgNVHQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIhvcNAQEMBQADggIBAHx4\n" +"7PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048p9gkUbJUHJNOxO97k4VgJuoJSOD1\n" +"u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63bEIaZHU1VNaL8FpO7XJqti2kM3S+LGteWygxk6\n" +"x9PbTZ4IevPuzz5i+6zoYMzRx6Fcg0XERczzF2sUyQQCPtIkpnnpHs6i58FZFZ8d4kuaPp92\n" +"CC1r2LpXFNqD6v6MVenQTqnMdzGxRBF6XLE+0xRFFRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J\n" +"3pZmOUdkLG5NrmJ7v2B0GbhWrJKsFjLtrWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSyB\n" +"Q7N0H3qqJZ4d16GLuc1CLgSkZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7fXwgNNgyYMq\n" +"IgXQBztSvwyeqiv5u+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJMbfc2hIkCwU9D9SGuTS\n" +"yxTDYWnP4vkYxboznxSjBF25cfe1lNj2M8FawTSLfJvdkzrnE6JwYZ+vj+vYxXX4M2bUdGc6\n" +"N3ec592kD3ZDZopD8p/7DEJ4Y9HiD2971KE9dJeFt0g5QdYg/NA6s/rob8SKunE3vouXsXgx\n" +"T7PntgMTzlSdriVZzH81Xwj3QEUxeCp6\n" +"-----END CERTIFICATE-----",.len=1911}, + +/* GlobalSign Root E46 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIICCzCCAZGgAwIBAgISEdK7ujNu1LzmJGjFDYQdmOhDMAoGCCqGSM49BAMDMEYxCzAJBgNV\n" +"BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQDExNHbG9iYWxTaWdu\n" +"IFJvb3QgRTQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAwMDAwMFowRjELMAkGA1UEBhMC\n" +"QkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9v\n" +"dCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAScDrHPt+ieUnd1NPqlRqetMhkytAepJ8qU\n" +"uwzSChDH2omwlwxwEwkBjtjqR+q+soArzfwoDdusvKSGN+1wCAB16pMLey5SnCNoIwZD7JIv\n" +"U4Tb+0cUB+hflGddyXqBPCCjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/\n" +"MB0GA1UdDgQWBBQxCpCPtsad0kRLgLWi5h+xEk8blTAKBggqhkjOPQQDAwNoADBlAjEA31SQ\n" +"7Zvvi5QCkxeCmb6zniz2C5GMn0oUsfZkvLtoURMMA/cVi4RguYv/Uo7njLwcAjA8+RHUjE7A\n" +"wWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+CAezNIm8BZ/3Hobui3A=\n" +"-----END CERTIFICATE-----",.len=767}, + +/* GLOBALTRUST 2020 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"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\n" +"vTmR9W0Nv3vXkg==\n" +"-----END CERTIFICATE-----",.len=1968}, + +/* ANF Secure Server Root CA */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIF7zCCA9egAwIBAgIIDdPjvGz5a7EwDQYJKoZIhvcNAQELBQAwgYQxEjAQBgNVBAUTCUc2\n" +"MzI4NzUxMDELMAkGA1UEBhMCRVMxJzAlBgNVBAoTHkFORiBBdXRvcmlkYWQgZGUgQ2VydGlm\n" +"aWNhY2lvbjEUMBIGA1UECxMLQU5GIENBIFJhaXoxIjAgBgNVBAMTGUFORiBTZWN1cmUgU2Vy\n" +"dmVyIFJvb3QgQ0EwHhcNMTkwOTA0MTAwMDM4WhcNMzkwODMwMTAwMDM4WjCBhDESMBAGA1UE\n" +"BRMJRzYzMjg3NTEwMQswCQYDVQQGEwJFUzEnMCUGA1UEChMeQU5GIEF1dG9yaWRhZCBkZSBD\n" +"ZXJ0aWZpY2FjaW9uMRQwEgYDVQQLEwtBTkYgQ0EgUmFpejEiMCAGA1UEAxMZQU5GIFNlY3Vy\n" +"ZSBTZXJ2ZXIgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANvrayvm\n" +"ZFSVgpCjcqQZAZ2cC4Ffc0m6p6zzBE57lgvsEeBbphzOG9INgxwruJ4dfkUyYA8H6XdYfp9q\n" +"yGFOtibBTI3/TO80sh9l2Ll49a2pcbnvT1gdpd50IJeh7WhM3pIXS7yr/2WanvtH2Vdy8wmh\n" +"rnZEE26cLUQ5vPnHO6RYPUG9tMJJo8gN0pcvB2VSAKduyK9o7PQUlrZXH1bDOZ8rbeTzPvY1\n" +"ZNoMHKGESy9LS+IsJJ1tk0DrtSOOMspvRdOoiXsezx76W0OLzc2oD2rKDF65nkeP8Nm2CgtY\n" +"ZRczuSPkdxl9y0oukntPLxB3sY0vaJxizOBQ+OyRp1RMVwnVdmPF6GUe7m1qzwmd+nxPrWAI\n" +"/VaZDxUse6mAq4xhj0oHdkLePfTdsiQzW7i1o0TJrH93PB0j7IKppuLIBkwC/qxcmZkLLxCK\n" +"pvR/1Yd0DVlJRfbwcVw5Kda/SiOL9V8BY9KHcyi1Swr1+KuCLH5zJTIdC2MKF4EA/7Z2Xue0\n" +"sUDKIbvVgFHlSFJnLNJhiQcND85Cd8BEc5xEUKDbEAotlRyBr+Qc5RQe8TZBAQIvfXOn3kLM\n" +"TOmJDVb3n5HUA8ZsyY/b2BzgQJhdZpmYgG4t/wHFzstGH6wCxkPmrqKEPMVOHj1tyRRM4y5B\n" +"u8o5vzY8KhmqQYdOpc5LMnndkEl/AgMBAAGjYzBhMB8GA1UdIwQYMBaAFJxf0Gxjo1+TypOY\n" +"CK2Mh6UsXME3MB0GA1UdDgQWBBScX9BsY6Nfk8qTmAitjIelLFzBNzAOBgNVHQ8BAf8EBAMC\n" +"AYYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEATh65isagmD9uw2nAalxJ\n" +"UqzLK114OMHVVISfk/CHGT0sZonrDUL8zPB1hT+L9IBdeeUXZ701guLyPI59WzbLWoAAKfLO\n" +"Kyzxj6ptBZNscsdW699QIyjlRRA96Gejrw5VD5AJYu9LWaL2U/HANeQvwSS9eS9OICI7/Rog\n" +"sKQOLHDtdD+4E5UGUcjohybKpFtqFiGS3XNgnhAY3jyB6ugYw3yJ8otQPr0R4hUDqDZ9MwFs\n" +"SBXXiJCZBMXM5gf0vPSQ7RPi6ovDj6MzD8EpTBNO2hVWcXNyglD2mjN8orGoGjR0ZVzO0eur\n" +"U+AagNjqOknkJjCb5RyKqKkVMoaZkgoQI1YS4PbOTOK7vtuNknMBZi9iPrJyJ0U27U1W45eZ\n" +"/zo1PqVUSlJZS2Db7v54EX9K3BR5YLZrZAPbFYPhor72I5dQ8AkzNqdxliXzuUJ92zg/LFis\n" +"6ELhDtjTO0wugumDLmsx2d1Hhk9tl5EuT+IocTUW0fJz/iUrB0ckYyfI+PbZa/wSMVYIwFNC\n" +"r5zQM378BvAxRAMU8Vjq8moNqRGyg77FGr8H6lnco4g175x2MjxNBiLOFeXdntiP2t7SxDnl\n" +"F4HPOEfrf4htWRvfn0IUrn7PqLBmZdo3r5+qPeoott7VMVgWglvquxl1AnMaykgaIZOQCo6T\n" +"hKd9OyMYkomgjaw=\n" +"-----END CERTIFICATE-----",.len=2114}, + +/* Certum EC-384 CA */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIICZTCCAeugAwIBAgIQeI8nXIESUiClBNAt3bpz9DAKBggqhkjOPQQDAzB0MQswCQYDVQQG\n" +"EwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0\n" +"dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAXBgNVBAMTEENlcnR1bSBFQy0zODQgQ0Ew\n" +"HhcNMTgwMzI2MDcyNDU0WhcNNDMwMzI2MDcyNDU0WjB0MQswCQYDVQQGEwJQTDEhMB8GA1UE\n" +"ChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNh\n" +"dGlvbiBBdXRob3JpdHkxGTAXBgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwdjAQBgcqhkjOPQIB\n" +"BgUrgQQAIgNiAATEKI6rGFtqvm5kN2PkzeyrOvfMobgOgknXhimfoZTy42B4mIF4Bk3y7JoO\n" +"V2CDn7TmFy8as10CW4kjPMIRBSqniBMY81CE1700LCeJVf/OTOffph8oxPBUw7l8t1Ot68Kj\n" +"QjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI0GZnQkdjrzife81r1HfS+8EF9LMA4G\n" +"A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNoADBlAjADVS2m5hjEfO/JUG7BJw+ch69u1RsI\n" +"GL2SKcHvlJF40jocVYli5RsJHrpka/F2tNQCMQC0QoSZ/6vnnvuRlydd3LBbMHHOXjgaatkl\n" +"5+r3YZJW+OraNsKHZZYuciUvf9/DE8k=\n" +"-----END CERTIFICATE-----",.len=889}, + +/* Certum Trusted Root CA */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFwDCCA6igAwIBAgIQHr9ZULjJgDdMBvfrVU+17TANBgkqhkiG9w0BAQ0FADB6MQswCQYD\n" +"VQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5D\n" +"ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVk\n" +"IFJvb3QgQ0EwHhcNMTgwMzE2MTIxMDEzWhcNNDMwMzE2MTIxMDEzWjB6MQswCQYDVQQGEwJQ\n" +"TDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0g\n" +"Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVkIFJvb3Qg\n" +"Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDRLY67tzbqbTeRn06TpwXkKQMl\n" +"zhyC93yZn0EGze2jusDbCSzBfN8pfktlL5On1AFrAygYo9idBcEq2EXxkd7fO9CAAozPOA/q\n" +"p1x4EaTByIVcJdPTsuclzxFUl6s1wB52HO8AU5853BSlLCIls3Jy/I2z5T4IHhQqNwuIPMqw\n" +"9MjCoa68wb4pZ1Xi/K1ZXP69VyywkI3C7Te2fJmItdUDmj0VDT06qKhF8JVOJVkdzZhpu9PM\n" +"MsmN74H+rX2Ju7pgE8pllWeg8xn2A1bUatMn4qGtg/BKEiJ3HAVz4hlxQsDsdUaakFjgao4r\n" +"pUYwBI4Zshfjvqm6f1bxJAPXsiEodg42MEx51UGamqi4NboMOvJEGyCI98Ul1z3G4z5D3Yf+\n" +"xOr1Uz5MZf87Sst4WmsXXw3Hw09Omiqi7VdNIuJGmj8PkTQkfVXjjJU30xrwCSss0smNtA0A\n" +"q2cpKNgB9RkEth2+dv5yXMSFytKAQd8FqKPVhJBPC/PgP5sZ0jeJP/J7UhyM9uH3PAeXjA6i\n" +"WYEMspA90+NZRu0PqafegGtaqge2Gcu8V/OXIXoMsSt0Puvap2ctTMSYnjYJdmZm/Bo/6khU\n" +"HL4wvYBQv3y1zgD2DGHZ5yQD4OMBgQ692IU0iL2yNqh7XAjlRICMb/gv1SHKHRzQ+8S1h9E6\n" +"Tsd2tTVItQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSM+xx1vALTn04u\n" +"SNn5YFSqxLNP+jAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQENBQADggIBAEii1QALLtA/\n" +"vBzVtVRJHlpr9OTy4EA34MwUe7nJ+jW1dReTagVphZzNTxl4WxmB82M+w85bj/UvXgF2Ez8s\n" +"ALnNllI5SW0ETsXpD4YN4fqzX4IS8TrOZgYkNCvozMrnadyHncI013nR03e4qllY/p0m+jiG\n" +"Pp2Kh2RX5Rc64vmNueMzeMGQ2Ljdt4NR5MTMI9UGfOZR0800McD2RrsLrfw9EAUqO0qRJe6M\n" +"1ISHgCq8CYyqOhNf6DR5UMEQGfnTKB7U0VEwKbOukGfWHwpjscWpxkIxYxeU72nLL/qMFH3E\n" +"QxiJ2fAyQOaA4kZf5ePBAFmo+eggvIksDkc0C+pXwlM2/KfUrzHN/gLldfq5Jwn58/U7yn2f\n" +"qSLLiMmq0Uc9NneoWWRrJ8/vJ8HjJLWG965+Mk2weWjROeiQWMODvA8s1pfrzgzhIMfatz7D\n" +"P78v3DSk+yshzWePS/Tj6tQ/50+6uaWTRRxmHyH6ZF5v4HaUMst19W7l9o/HuKTMqJZ9ZPsk\n" +"WkoDbGs4xugDQ5r3V7mzKWmTOPQD8rv7gmsHINFSH5pkAnuYZttcTVoP0ISVoDwUQwbKytu4\n" +"QTbaakRnh6+v40URFWkIsr4WOZckbxJF0WddCajJFdr60qZfE2Efv4WstK2tBZQIgx51F9Nx\n" +"O5NQI1mg7TyRVJ12AMXDuDjb\n" +"-----END CERTIFICATE-----",.len=2049}, + +/* TunTrust Root CA */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFszCCA5ugAwIBAgIUEwLV4kBMkkaGFmddtLu7sms+/BMwDQYJKoZIhvcNAQELBQAwYTEL\n" +"MAkGA1UEBhMCVE4xNzA1BgNVBAoMLkFnZW5jZSBOYXRpb25hbGUgZGUgQ2VydGlmaWNhdGlv\n" +"biBFbGVjdHJvbmlxdWUxGTAXBgNVBAMMEFR1blRydXN0IFJvb3QgQ0EwHhcNMTkwNDI2MDg1\n" +"NzU2WhcNNDQwNDI2MDg1NzU2WjBhMQswCQYDVQQGEwJUTjE3MDUGA1UECgwuQWdlbmNlIE5h\n" +"dGlvbmFsZSBkZSBDZXJ0aWZpY2F0aW9uIEVsZWN0cm9uaXF1ZTEZMBcGA1UEAwwQVHVuVHJ1\n" +"c3QgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMPN0/y9BFPdDCA6\n" +"1YguBUtB9YOCfvdZn56eY+hz2vYGqU8ftPkLHzmMmiDQfgbU7DTZhrx1W4eI8NLZ1KMKsmwb\n" +"60ksPqxd2JQDoOw05TDENX37Jk0bbjBU2PWARZw5rZzJJQRNmpA+TkBuimvNKWfGzC3gdOgF\n" +"VwpIUPp6Q9p+7FuaDmJ2/uqdHYVy7BG7NegfJ7/Boce7SBbdVtfMTqDhuazb1YMZGoXRlJfX\n" +"yqNlC/M4+QKu3fZnz8k/9YosRxqZbwUN/dAdgjH8KcwAWJeRTIAAHDOFli/LQcKLEITDCSSJ\n" +"H7UP2dl3RxiSlGBcx5kDPP73lad9UKGAwqmDrViWVSHbhlnUr8a83YFuB9tgYv7sEG7aaAH0\n" +"gxupPqJbI9dkxt/con3YS7qC0lH4Zr8GRuR5KiY2eY8fTpkdso8MDhz/yV3A/ZAQprE38806\n" +"JG60hZC/gLkMjNWb1sjxVj8agIl6qeIbMlEsPvLfe/ZdeikZjuXIvTZxi11Mwh0/rViizz1w\n" +"TaZQmCXcI/m4WEEIcb9PuISgjwBUFfyRbVinljvrS5YnzWuioYasDXxU5mZMZl+QviGaAkYt\n" +"5IPCgLnPSz7ofzwB7I9ezX/SKEIBlYrilz0QIX32nRzFNKHsLA4KUiwSVXAkPcvCFDVDXSdO\n" +"vsC9qnyW5/yeYa1E0wCXAgMBAAGjYzBhMB0GA1UdDgQWBBQGmpsfU33x9aTI04Y+oXNZtPdE\n" +"ITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFAaamx9TffH1pMjThj6hc1m090QhMA4G\n" +"A1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAqgVutt0Vyb+zxiD2BkewhpMl0425\n" +"yAA/l/VSJ4hxyXT968pk21vvHl26v9Hr7lxpuhbI87mP0zYuQEkHDVneixCwSQXi/5E/S7fd\n" +"Ao74gShczNxtr18UnH1YeA32gAm56Q6XKRm4t+v4FstVEuTGfbvE7Pi1HE4+Z7/FXxttbUco\n" +"qgRYYdZ2vyJ/0Adqp2RT8JeNnYA/u8EH22Wv5psymsNUk8QcCMNE+3tjEUPRahphanltkE8p\n" +"jkcFwRJpadbGNjHh/PqAulxPxOu3Mqz4dWEX1xAZufHSCe96Qp1bWgvUxpVOKs7/B9dPfhgG\n" +"iPEZtdmYu65xxBzndFlY7wyJz4sfdZMaBBSSSFCp61cpABbjNhzI+L/wM9VBD8TMPN3pM0MB\n" +"kRArHtG5Xc0yGYuPjCB31yLEQtyEFpslbei0VXF/sHyz03FJuc9SpAQ/3D2gu68zngowYI7b\n" +"nV2UqL1g52KAdoGDDIzMMEZJ4gzSqK/rYXHv5yJiqfdcZGyfFoxnNidF9Ql7v/YQCvGwjVRD\n" +"jAS6oz/v4jXH+XTgbzRB0L9zZVcg+ZtnemZoJE6AZb0QmQZZ8mWvuMZHu/2QeItBcy6vVR/c\n" +"O5JyboTT0GFMDcx2V+IthSIVNg3rAZ3r2OvEhJn7wAzMMujjd9qDRIueVSjAi1jTkD5OGwDx\n" +"Fa2DK5o=\n" +"-----END CERTIFICATE-----",.len=2033}, + +/* HARICA TLS RSA Root CA 2021 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"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\n" +"-----END CERTIFICATE-----",.len=2012}, + +/* HARICA TLS ECC Root CA 2021 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIICVDCCAdugAwIBAgIQZ3SdjXfYO2rbIvT/WeK/zjAKBggqhkjOPQQDAzBsMQswCQYDVQQG\n" +"EwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0\n" +"aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBFQ0MgUm9vdCBDQSAyMDIxMB4XDTIxMDIx\n" +"OTExMDExMFoXDTQ1MDIxMzExMDEwOVowbDELMAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxl\n" +"bmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hB\n" +"UklDQSBUTFMgRUNDIFJvb3QgQ0EgMjAyMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDgI/rGg\n" +"ltJ6rK9JOtDA4MM7KKrxcm1lAEeIhPyaJmuqS7psBAqIXhfyVYf8MLA04jRYVxqEU+kw2any\n" +"lnTDUR9YSTHMmE5gEYd103KUkE+bECUqqHgtvpBBWJAVcqeht6NCMEAwDwYDVR0TAQH/BAUw\n" +"AwEB/zAdBgNVHQ4EFgQUyRtTgRL+BNUW0aq8mm+3oJUZbsowDgYDVR0PAQH/BAQDAgGGMAoG\n" +"CCqGSM49BAMDA2cAMGQCMBHervjcToiwqfAircJRQO9gcS3ujwLEXQNwSaSS6sUUiHCm0w2w\n" +"qsosQJz76YJumgIwK0eaB8bRwoF8yguWGEEbo/QwCZ61IygNnxS2PFOiTAZpffpskcYqSUXm\n" +"7LcT4Tps\n" +"-----END CERTIFICATE-----",.len=865}, + +/* Autoridad de Certificacion Firmaprofesional CIF A62634068 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIGFDCCA/ygAwIBAgIIG3Dp0v+ubHEwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCRVMx\n" +"QjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwg\n" +"Q0lGIEE2MjYzNDA2ODAeFw0xNDA5MjMxNTIyMDdaFw0zNjA1MDUxNTIyMDdaMFExCzAJBgNV\n" +"BAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zl\n" +"c2lvbmFsIENJRiBBNjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDK\n" +"lmuO6vj78aI14H9M2uDDUtd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOS\n" +"L/UR5GLXMnE42QQMcas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9\n" +"qFD0sefGL9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i\n" +"NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2\n" +"f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44\n" +"I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCyZ/QYFpM6/EfY0XiWMR+6Kwxf\n" +"XZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy\n" +"9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF\n" +"8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mV\n" +"BngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMB0GA1UdDgQWBBRlzeurNR4A\n" +"Pn7VdMActHNHDhpkLzASBgNVHRMBAf8ECDAGAQH/AgEBMIGmBgNVHSAEgZ4wgZswgZgGBFUd\n" +"IAAwgY8wLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuZmlybWFwcm9mZXNpb25hbC5jb20vY3Bz\n" +"MFwGCCsGAQUFBwICMFAeTgBQAGEAcwBlAG8AIABkAGUAIABsAGEAIABCAG8AbgBhAG4AbwB2\n" +"AGEAIAA0ADcAIABCAGEAcgBjAGUAbABvAG4AYQAgADAAOAAwADEANzAOBgNVHQ8BAf8EBAMC\n" +"AQYwDQYJKoZIhvcNAQELBQADggIBAHSHKAIrdx9miWTtj3QuRhy7qPj4Cx2Dtjqn6EWKB7fg\n" +"PiDL4QjbEwj4KKE1soCzC1HA01aajTNFSa9J8OA9B3pFE1r/yJfY0xgsfZb43aJlQ3CTkBW6\n" +"kN/oGbDbLIpgD7dvlAceHabJhfa9NPhAeGIQcDq+fUs5gakQ1JZBu/hfHAsdCPKxsIl68veg\n" +"4MSPi3i1O1ilI45PVf42O+AMt8oqMEEgtIDNrvx2ZnOorm7hfNoD6JQg5iKj0B+QXSBTFCZX\n" +"2lSX3xZEEAEeiGaPcjiT3SC3NL7X8e5jjkd5KAb881lFJWAiMxujX6i6KtoaPc1A6ozuBRWV\n" +"1aUsIC+nmCjuRfzxuIgALI9C2lHVnOUTaHFFQ4ueCyE8S1wF3BqfmI7avSKecs2tCsvMo2eb\n" +"KHTEm9caPARYpoKdrcd7b/+Alun4jWq9GJAd/0kakFI3ky88Al2CdgtR5xbHV/g4+afNmyJU\n" +"72OwFW1TZQNKXkqgsqeOSQBZONXH9IBk9W6VULgRfhVwOEqwf9DEMnDAGf/JOC0ULGb0QkTm\n" +"VXYbgBVX/8Cnp6o5qtjTcNAuuuuUavpfNIbnYrX9ivAwhZTJryQCL2/W3Wf+47BVTwSYT6RB\n" +"VuKT0Gro1vP7ZeDOdcQxWQzugsgMYDNKGbqEZycPvEJdvSRUDewdcAZfpLz6IHxV\n" +"-----END CERTIFICATE-----",.len=2162}, + +/* vTrus ECC Root CA */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIICDzCCAZWgAwIBAgIUbmq8WapTvpg5Z6LSa6Q75m0c1towCgYIKoZIzj0EAwMwRzELMAkG\n" +"A1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xGjAYBgNVBAMTEXZUcnVz\n" +"IEVDQyBSb290IENBMB4XDTE4MDczMTA3MjY0NFoXDTQzMDczMTA3MjY0NFowRzELMAkGA1UE\n" +"BhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xGjAYBgNVBAMTEXZUcnVzIEVD\n" +"QyBSb290IENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEZVBKrox5lkqqHAjDo6LN/llWQXf9\n" +"JpRCux3NCNtzslt188+cToL0v/hhJoVs1oVbcnDS/dtitN9Ti72xRFhiQgnH+n9bEOf+QP3A\n" +"2MMrMudwpremIFUde4BdS49nTPEQo0IwQDAdBgNVHQ4EFgQUmDnNvtiyjPeyq+GtJK97fKHb\n" +"H88wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwMDaAAwZQIw\n" +"V53dVvHH4+m4SVBrm2nDb+zDfSXkV5UTQJtS0zvzQBm8JsctBp61ezaf9SXUY2sAAjEA6dPG\n" +"nlaaKsyh2j/IZivTWJwghfqrkYpwcBE4YGQLYgmRWAD5Tfs0aNoJrSEGGJTO\n" +"-----END CERTIFICATE-----",.len=771}, + +/* vTrus Root CA */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFVjCCAz6gAwIBAgIUQ+NxE9izWRRdt86M/TX9b7wFjUUwDQYJKoZIhvcNAQELBQAwQzEL\n" +"MAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xFjAUBgNVBAMTDXZU\n" +"cnVzIFJvb3QgQ0EwHhcNMTgwNzMxMDcyNDA1WhcNNDMwNzMxMDcyNDA1WjBDMQswCQYDVQQG\n" +"EwJDTjEcMBoGA1UEChMTaVRydXNDaGluYSBDby4sTHRkLjEWMBQGA1UEAxMNdlRydXMgUm9v\n" +"dCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL1VfGHTuB0EYgWgrmy3cLRB\n" +"6ksDXhA/kFocizuwZotsSKYcIrrVQJLuM7IjWcmOvFjai57QGfIvWcaMY1q6n6MLsLOaXLoR\n" +"uBLpDLvPbmyAhykUAyyNJJrIZIO1aqwTLDPxn9wsYTwaP3BVm60AUn/PBLn+NvqcwBauYv6W\n" +"TEN+VRS+GrPSbcKvdmaVayqwlHeFXgQPYh1jdfdr58tbmnDsPmcF8P4HCIDPKNsFxhQnL4Z9\n" +"8Cfe/+Z+M0jnCx5Y0ScrUw5XSmXX+6KAYPxMvDVTAWqXcoKv8R1w6Jz1717CbMdHflqUhSZN\n" +"O7rrTOiwCcJlwp2dCZtOtZcFrPUGoPc2BX70kLJrxLT5ZOrpGgrIDajtJ8nU57O5q4IikCc9\n" +"Kuh8kO+8T/3iCiSn3mUkpF3qwHYw03dQ+A0Em5Q2AXPKBlim0zvc+gRGE1WKyURHuFE5Gi7o\n" +"NOJ5y1lKCn+8pu8fA2dqWSslYpPZUxlmPCdiKYZNpGvu/9ROutW04o5IWgAZCfEF2c6Rsffr\n" +"6TlP9m8EQ5pV9T4FFL2/s1m02I4zhKOQUqqzApVg+QxMaPnu1RcN+HFXtSXkKe5lXa/R7jwX\n" +"C1pDxaWG6iSe4gUH3DRCEpHWOXSuTEGC2/KmSNGzm/MzqvOmwMVO9fSddmPmAsYiS8GVP1Bk\n" +"LFTltvA8Kc9XAgMBAAGjQjBAMB0GA1UdDgQWBBRUYnBj8XWEQ1iO0RYgscasGrz2iTAPBgNV\n" +"HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAKbqSSaet\n" +"8PFww+SX8J+pJdVrnjT+5hpk9jprUrIQeBqfTNqK2uwcN1LgQkv7bHbKJAs5EhWdnxEt/Hlk\n" +"3ODg9d3gV8mlsnZwUKT+twpw1aA08XXXTUm6EdGz2OyC/+sOxL9kLX1jbhd47F18iMjrjld2\n" +"2VkE+rxSH0Ws8HqA7Oxvdq6R2xCOBNyS36D25q5J08FsEhvMKar5CKXiNxTKsbhm7xqC5PD4\n" +"8acWabfbqWE8n/Uxy+QARsIvdLGx14HuqCaVvIivTDUHKgLKeBRtRytAVunLKmChZwOgzoy8\n" +"sHJnxDHO2zTlJQNgJXtxmOTAGytfdELSS8VZCAeHvsXDf+eW2eHcKJfWjwXj9ZtOyh1QRwVT\n" +"sMo554WgicEFOwE30z9J4nfrI8iIZjs9OXYhRvHsXyO466JmdXTBQPfYaJqT4i2pLr0cox7I\n" +"dMakLXogqzu4sEb9b91fUlV1YvCXoHzXOP0l382gmxDPi7g4Xl7FtKYCNqEeXxzP4padKar9\n" +"mK5S4fNBUvupLnKWnyfjqnN9+BojZns7q2WwMgFLFT49ok8MKzWixtlnEjUwzXYuFrOZnk1P\n" +"Ti07NEPhmg4NpGaXutIcSkwsKouLgU9xGqndXHt7CMUADTdA43x7VF8vhV929vensBxXVsFy\n" +"6K2ir40zSbofitzmdHxghm+Hl3s=\n" +"-----END CERTIFICATE-----",.len=1907}, + +/* ISRG Root X2 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQswCQYDVQQG\n" +"EwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNV\n" +"BAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00MDA5MTcxNjAwMDBaME8xCzAJ\n" +"BgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBTZWN1cml0eSBSZXNlYXJjaCBHcm91cDEV\n" +"MBMGA1UEAxMMSVNSRyBSb290IFgyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJ\n" +"SvMWSj5cz3es3mcFDR0HttwW+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvX\n" +"RdgKam7mAHf7AlF9ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYw\n" +"DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI\n" +"zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdWtL4ndQav\n" +"Ei51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1/q4AaOeMSQ+2b1tb\n" +"FfLn\n" +"-----END CERTIFICATE-----",.len=788}, + +/* HiPKI Root CA - G1 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"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\n" +"-----END CERTIFICATE-----",.len=1935}, + +/* GlobalSign ECC Root CA - R4 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIB3DCCAYOgAwIBAgINAgPlfvU/k/2lCSGypjAKBggqhkjOPQQDAjBQMSQwIgYDVQQLExtH\n" +"bG9iYWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNV\n" +"BAMTCkdsb2JhbFNpZ24wHhcNMTIxMTEzMDAwMDAwWhcNMzgwMTE5MDMxNDA3WjBQMSQwIgYD\n" +"VQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2JhbFNpZ24x\n" +"EzARBgNVBAMTCkdsb2JhbFNpZ24wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS4xnnTj2wl\n" +"Dp8uORkcA6SumuU5BwkWymOxuYb4ilfBV85C+nOh92VC/x7BALJucw7/xyHlGKSq2XE/qNS5\n" +"zowdo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVLB7\n" +"rUW44kB/+wpu+74zyTyjhNUwCgYIKoZIzj0EAwIDRwAwRAIgIk90crlgr/HmnKAWBVBfw147\n" +"bmF0774BxL4YSFlhgjICICadVGNA3jdgUM/I2O2dgq43mLyjj0xMqTQrbO/7lZsm\n" +"-----END CERTIFICATE-----",.len=702}, + +/* GTS Root R1 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQG\n" +"EwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RT\n" +"IFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJV\n" +"UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJv\n" +"b3QgUjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreV\n" +"p0A8of2C+X0yBoJx9vaMf/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4\n" +"IyfLpLGcY9vXmX7wCl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/S\n" +"CYe7zUjwTcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl\n" +"qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaHszVsrBhQ\n" +"f4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8Y/kB+Xj9e1x3+naH\n" +"+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmkMiVOKvFlRNACzqrOSbTqn3yD\n" +"sEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97\n" +"PtbfkSIS5r762DL8EGMUUXLeXdYWk70paDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmx\n" +"lbCluQ0WEdrHbEg8QOB+DVrNVjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACY\n" +"PTrIrnqYNxgFlQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAd\n" +"BgNVHQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb\n" +"C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEeQkEzCzc9\n" +"zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuyh6f88/qBVRRiClmp\n" +"IgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM47HLwEXWdyzRSjeZ2axfG34ar\n" +"J45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8JZgfIPxz88NtFMN9iiMG1D53Dn0reWVlH\n" +"xYciNuaCp+0KueIHoI17eko8cdLiA6EfMgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/b\n" +"al8xa5meLMFrUKTX5hgUvYU/Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAb\n" +"d03OIozUhfJFfbdT6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLs\n" +"QBqvFAnZ0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm\n" +"2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bbbP6MvPJw\n" +"NQzcmRk13NfIRmPVNnGuV/u3gm3c\n" +"-----END CERTIFICATE-----",.len=1907}, + +/* GTS Root R2 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFVzCCAz+gAwIBAgINAgPlrsWNBCUaqxElqjANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQG\n" +"EwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RT\n" +"IFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJV\n" +"UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJv\n" +"b3QgUjIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3\n" +"GTXd98GdVarTzTukk3LvCvptnfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfg\n" +"LFuv5AS/T3KgGjSY6Dlo7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/B\n" +"W9BuXvAuMC6C/Pq8tBcKSOWIm8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7k\n" +"RXuJVfeKH2JShBKzwkCX44ofR5GmdFrS+LFjKBC4swm4VndAoiaYecb+3yXuPuWgf9RhD1FL\n" +"PD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7MkogwTZq9TwtImoS1mKPV+3PBV2HdKFZ1E66H\n" +"jucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJGr61K8YzodDqs5xoic4DSMPclQsciOzsS\n" +"rZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RWIr9qS34BIbIjMt/kmkRtWVtd9QCgHJvG\n" +"eJeNkP+byKq0rxFROV7Z+2et1VsRnTKaG73VululycslaVNVJ1zgyjbLiGH7HrfQy+4W+9Om\n" +"TN6SpdTi3/UGVN4unUu0kzCqgc7dGtxRcw1PcOnlthYhGXmy5okLdWTK1au8CcEYof/UVKGF\n" +"PP0UJAOyh9OktwIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAd\n" +"BgNVHQ4EFgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQADggIBAB/Kzt3H\n" +"vqGf2SdMC9wXmBFqiN495nFWcrKeGk6c1SuYJF2ba3uwM4IJvd8lRuqYnrYb/oM80mJhwQTt\n" +"zuDFycgTE1XnqGOtjHsB/ncw4c5omwX4Eu55MaBBRTUoCnGkJE+M3DyCB19m3H0Q/gxhswWV\n" +"7uGugQ+o+MePTagjAiZrHYNSVc61LwDKgEDg4XSsYPWHgJ2uNmSRXbBoGOqKYcl3qJfEycel\n" +"/FVL8/B/uWU9J2jQzGv6U53hkRrJXRqWbTKH7QMgyALOWr7Z6v2yTcQvG99fevX4i8buMTol\n" +"UVVnjWQye+mew4K6Ki3pHrTgSAai/GevHyICc/sgCq+dVEuhzf9gR7A/Xe8bVr2XIZYtCtFe\n" +"nTgCR2y59PYjJbigapordwj6xLEokCZYCDzifqrXPW+6MYgKBesntaFJ7qBFVHvmJ2WZICGo\n" +"o7z7GJa7Um8M7YNRTOlZ4iBgxcJlkoKM8xAfDoqXvneCbT+PHV28SSe9zE8P4c52hgQjxcCM\n" +"Elv924SgJPFI/2R80L5cFtHvma3AH/vLrrw4IgYmZNralw4/KBVEqE8AyvCazM90arQ+POuV\n" +"7LXTWtiBmelDGDfrs7vRWGJB82bSj6p4lVQgw1oudCvV0b4YacCs1aTPObpRhANl6WLAYv7Y\n" +"TVWW4tAR+kg0Eeye7QUd5MjWHYbL\n" +"-----END CERTIFICATE-----",.len=1907}, + +/* GTS Root R3 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIICCTCCAY6gAwIBAgINAgPluILrIPglJ209ZjAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJV\n" +"UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJv\n" +"b3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEi\n" +"MCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg\n" +"UjMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/\n" +"JlFyb+Kf1qPKzEUURout736GjOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyR\n" +"RK2EE46ajA2ADDL24CejQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0G\n" +"A1UdDgQWBBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEA9uEglRR7\n" +"VKOQFhG/hMjqb2sXnh5GmCCbn9MN2azTL818+FsuVbu/3ZL3pAzcMeGiAjEA/JdmZuVDFhOD\n" +"3cffL74UOO0BzrEXGhF16b0DjyZ+hOXJYKaV11RZt+cRLInUue4X\n" +"-----END CERTIFICATE-----",.len=763}, + +/* GTS Root R4 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJV\n" +"UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJv\n" +"b3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEi\n" +"MCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg\n" +"UjQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvV\n" +"YRg1rkDdc/eJkTBa6zzuhXyiQHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHc\n" +"i8nHc8iMai/lxKvRHYqjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0G\n" +"A1UdDgQWBBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D\n" +"9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/Cr8deVl5c1RxYIigL9zC2L7F8AjEA8GE8p/SgguMh\n" +"1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh4rsUecrNIdSUtUlD\n" +"-----END CERTIFICATE-----",.len=763}, + +/* Telia Root CA v2 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"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\n" +"-----END CERTIFICATE-----",.len=1947}, + +/* D-TRUST BR Root CA 1 2020 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIC2zCCAmCgAwIBAgIQfMmPK4TX3+oPyWWa00tNljAKBggqhkjOPQQDAzBIMQswCQYDVQQG\n" +"EwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRSVVNUIEJSIFJvb3Qg\n" +"Q0EgMSAyMDIwMB4XDTIwMDIxMTA5NDUwMFoXDTM1MDIxMTA5NDQ1OVowSDELMAkGA1UEBhMC\n" +"REUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAGA1UEAxMZRC1UUlVTVCBCUiBSb290IENB\n" +"IDEgMjAyMDB2MBAGByqGSM49AgEGBSuBBAAiA2IABMbLxyjR+4T1mu9CFCDhQ2tuda38KwOE\n" +"1HaTJddZO0Flax7mNCq7dPYSzuht56vkPE4/RAiLzRZxy7+SmfSk1zxQVFKQhYN4lGdnoxwJ\n" +"GT11NIXe7WB9xwy0QVK5buXuQqOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE\n" +"FHOREKv/VbNafAkl1bK6CKBrqx9tMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g\n" +"PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2JyX3Jvb3RfY2FfMV8y\n" +"MDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNU\n" +"JTIwQlIlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1c3QlMjBHbWJILEM9REU/Y2Vy\n" +"dGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjOPQQDAwNpADBmAjEAlJAtE/rhY/hhY+it\n" +"hXhUkZy4kzg+GkHaQBZTQgjKL47xPoFWwKrY7RjEsK70PvomAjEA8yjixtsrmfu3Ubgko6SU\n" +"eho/5jbiA1czijDLgsfWFBHVdWNbFJWcHwHP2NVypw87\n" +"-----END CERTIFICATE-----",.len=1047}, + +/* D-TRUST EV Root CA 1 2020 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIC2zCCAmCgAwIBAgIQXwJB13qHfEwDo6yWjfv/0DAKBggqhkjOPQQDAzBIMQswCQYDVQQG\n" +"EwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRSVVNUIEVWIFJvb3Qg\n" +"Q0EgMSAyMDIwMB4XDTIwMDIxMTEwMDAwMFoXDTM1MDIxMTA5NTk1OVowSDELMAkGA1UEBhMC\n" +"REUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAGA1UEAxMZRC1UUlVTVCBFViBSb290IENB\n" +"IDEgMjAyMDB2MBAGByqGSM49AgEGBSuBBAAiA2IABPEL3YZDIBnfl4XoIkqbz52Yv7QFJsnL\n" +"46bSj8WeeHsxiamJrSc8ZRCC/N/DnU7wMyPE0jL1HLDfMxddxfCxivnvubcUyilKwg+pf3Vl\n" +"SSowZ/Rk99Yad9rDwpdhQntJraOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE\n" +"FH8QARY3OqQo5FD4pPfsazK2/umLMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g\n" +"PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2V2X3Jvb3RfY2FfMV8y\n" +"MDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNU\n" +"JTIwRVYlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1c3QlMjBHbWJILEM9REU/Y2Vy\n" +"dGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjOPQQDAwNpADBmAjEAyjzGKnXCXnViOTYA\n" +"YFqLwZOZzNnbQTs7h5kXO9XMT8oi96CAy/m0sRtW9XLS/BnRAjEAkfcwkz8QRitxpNA7RJvA\n" +"KQIFskF3UfN5Wp6OFKBOQtJbgfM0agPnIjhQW+0ZT0MW\n" +"-----END CERTIFICATE-----",.len=1047}, + +/* DigiCert TLS ECC P384 Root G5 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQswCQYDVQQG\n" +"EwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURpZ2lDZXJ0IFRMUyBF\n" +"Q0MgUDM4NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2MDExNDIzNTk1OVowTjELMAkG\n" +"A1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBU\n" +"TFMgRUNDIFAzODQgUm9vdCBHNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOC\n" +"NQfN0MsYndLxf3c1TzvdlHJS7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cK\n" +"PGEQQil8pQgO4CLp0zVozptjn4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7n\n" +"LFr6ICISB4CIfBFqMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49\n" +"BAMDA2gAMGUCMQCJao1H5+z8blUD2WdsJk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQLgGheQaR\n" +"nUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIxAJSdYsiJvRmEFOml+wG4DXZDjC5Ty3zfDBeW\n" +"UA==\n" +"-----END CERTIFICATE-----",.len=788}, + +/* DigiCert TLS RSA4096 Root G5 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFZjCCA06gAwIBAgIQCPm0eKj6ftpqMzeJ3nzPijANBgkqhkiG9w0BAQwFADBNMQswCQYD\n" +"VQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRM\n" +"UyBSU0E0MDk2IFJvb3QgRzUwHhcNMjEwMTE1MDAwMDAwWhcNNDYwMTE0MjM1OTU5WjBNMQsw\n" +"CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0\n" +"IFRMUyBSU0E0MDk2IFJvb3QgRzUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz\n" +"0PTJeRGd/fxmgefM1eS87IE+ajWOLrfn3q/5B03PMJ3qCQuZvWxX2hhKuHisOjmopkisLnLl\n" +"vevxGs3npAOpPxG02C+JFvuUAT27L/gTBaF4HI4o4EXgg/RZG5Wzrn4DReW+wkL+7vI8toUT\n" +"mDKdFqgpwgscONyfMXdcvyej/Cestyu9dJsXLfKB2l2w4SMXPohKEiPQ6s+d3gMXsUJKoBZM\n" +"pG2T6T867jp8nVid9E6P/DsjyG244gXazOvswzH016cpVIDPRFtMbzCe88zdH5RDnU1/cHAN\n" +"1DrRN/BsnZvAFJNY781BOHW8EwOVfH/jXOnVDdXifBBiqmvwPXbzP6PosMH976pXTayGpxi0\n" +"KcEsDr9kvimM2AItzVwv8n/vFfQMFawKsPHTDU9qTXeXAaDxZre3zu/O7Oyldcqs4+Fj97ih\n" +"BMi8ez9dLRYiVu1ISf6nL3kwJZu6ay0/nTvEF+cdLvvyz6b84xQslpghjLSR6Rlgg/IwKwZz\n" +"UNWYOwbpx4oMYIwo+FKbbuH2TbsGJJvXKyY//SovcfXWJL5/MZ4PbeiPT02jP/816t9JXkGP\n" +"hvnxd3lLG7SjXi/7RgLQZhNeXoVPzthwiHvOAbWWl9fNff2C+MIkwcoBOU+NosEUQB+cZtUM\n" +"CUbW8tDRSHZWOkPLtgoRObqME2wGtZ7P6wIDAQABo0IwQDAdBgNVHQ4EFgQUUTMc7TZArxfT\n" +"Jc1paPKvTiM+s0EwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcN\n" +"AQEMBQADggIBAGCmr1tfV9qJ20tQqcQjNSH/0GEwhJG3PxDPJY7Jv0Y02cEhJhxwGXIeo8mH\n" +"/qlDZJY6yFMECrZBu8RHANmfGBg7sg7zNOok992vIGCukihfNudd5N7HPNtQOa27PShNlnx2\n" +"xlv0wdsUpasZYgcYQF+Xkdycx6u1UQ3maVNVzDl92sURVXLFO4uJ+DQtpBflF+aZfTCIITfN\n" +"MBc9uPK8qHWgQ9w+iUuQrm0D4ByjoJYJu32jtyoQREtGBzRj7TG5BO6jm5qu5jF49OokYTur\n" +"WGT/u4cnYiWB39yhL/btp/96j1EuMPikAdKFOV8BmZZvWltwGUb+hmA+rYAQCd05JS9Yf7vS\n" +"dPD3Rh9GOUrYU9DzLjtxpdRv/PNn5AeP3SYZ4Y1b+qOTEZvpyDrDVWiakuFSdjjo4bq9+0/V\n" +"77PnSIMx8IIh47a+p6tv75/fTM8BuGJqIz3nCU2AG3swpMPdB380vqQmsvZB6Akd4yCYqjdP\n" +"//fx4ilwMUc/dNAUFvohigLVigmUdy7yWSiLfFCSCmZ4OIN1xLVaqBHG5cGdZlXPU8Sv13WF\n" +"qUITVuwhd4GTWgzqltlJyqEI8pc7bZsEGCREjnwB8twl2F6GmrE52/WRMmrRpnCKovfepEWF\n" +"JqgejF0pW8hL2JpqA15w8oVPbEtoL8pU9ozaMv7Da4M/OMZ+\n" +"-----END CERTIFICATE-----",.len=1927}, + +/* Certainly Root R1 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFRzCCAy+gAwIBAgIRAI4P+UuQcWhlM1T01EQ5t+AwDQYJKoZIhvcNAQELBQAwPTELMAkG\n" +"A1UEBhMCVVMxEjAQBgNVBAoTCUNlcnRhaW5seTEaMBgGA1UEAxMRQ2VydGFpbmx5IFJvb3Qg\n" +"UjEwHhcNMjEwNDAxMDAwMDAwWhcNNDYwNDAxMDAwMDAwWjA9MQswCQYDVQQGEwJVUzESMBAG\n" +"A1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0YWlubHkgUm9vdCBSMTCCAiIwDQYJKoZI\n" +"hvcNAQEBBQADggIPADCCAgoCggIBANA21B/q3avk0bbm+yLA3RMNansiExyXPGhjZjKcA7WN\n" +"pIGD2ngwEc/csiu+kr+O5MQTvqRoTNoCaBZ0vrLdBORrKt03H2As2/X3oXyVtwxwhi7xOu9S\n" +"98zTm/mLvg7fMbedaFySpvXl8wo0tf97ouSHocavFwDvA5HtqRxOcT3Si2yJ9HiG5mpJoM61\n" +"0rCrm/b01C7jcvk2xusVtyWMOvwlDbMicyF0yEqWYZL1LwsYpfSt4u5BvQF5+paMjRcCMLT5\n" +"r3gajLQ2EBAHBXDQ9DGQilHFhiZ5shGIXsXwClTNSaa/ApzSRKft43jvRl5tcdF5cBxGX1Hp\n" +"yTfcX35pe0HfNEXgO4T0oYoKNp43zGJS4YkNKPl6I7ENPT2a/Z2B7yyQwHtETrtJ4A5KVpK8\n" +"y7XdeReJkd5hiXSSqOMyhb5OhaRLWcsrxXiOcVTQAjeZjOVJ6uBUcqQRBi8LjMFbvrWhsFNu\n" +"nLhgkR9Za/kt9JQKl7XsxXYDVBtlUrpMklZRNaBA2CnbrlJ2Oy0wQJuK0EJWtLeIAaSHO1OW\n" +"zaMWj/Nmqhexx2DgwUMFDO6bW2BvBlyHWyf5QBGenDPBt+U1VwV/J84XIIwc/PH72jEpSe31\n" +"C4SnT8H2TsIonPru4K8H+zMReiFPCyEQtkA6qyI6BJyLm4SGcprSp6XEtHWRqSsjAgMBAAGj\n" +"QjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTgqj8ljZ9E\n" +"XME66C6ud0yEPmcM9DANBgkqhkiG9w0BAQsFAAOCAgEAuVevuBLaV4OPaAszHQNTVfSVcOQr\n" +"PbA56/qJYv331hgELyE03fFo8NWWWt7CgKPBjcZq91l3rhVkz1t5BXdm6ozTaw3d8VkswTOl\n" +"MIAVRQdFGjEitpIAq5lNOo93r6kiyi9jyhXWx8bwPWz8HA2YEGGeEaIi1wrykXprOQ4vMMM2\n" +"SZ/g6Q8CRFA3lFV96p/2O7qUpUzpvD5RtOjKkjZUbVwlKNrdrRT90+7iIgXr0PK3aBLXWopB\n" +"GsaSpVo7Y0VPv+E6dyIvXL9G+VoDhRNCX8reU9ditaY1BMJH/5n9hN9czulegChB8n3nHpDY\n" +"T3Y+gjwN/KUD+nsa2UUeYNrEjvn8K8l7lcUq/6qJ34IxD3L/DCfXCh5WAFAeDJDBlrXYFIW7\n" +"pw0WwfgHJBu6haEaBQmAupVjyTrsJZ9/nbqkRxWbRHDxakvWOF5D8xh+UG7pWijmZeZ3Gzr9\n" +"Hb4DJqPb1OG7fpYnKx3upPvaJVQTA945xsMfTZDsjxtK0hzthZU4UHlG1sGQUDGpXJpuHfUz\n" +"VounmdLyyCwzk5Iwx06MZTMQZBf9JBeW0Y3COmor6xOLRPIh80oat3df1+2IpHLlOR+Vnb5n\n" +"wXARPbv0+Em34yaXOp/SX3z7wJl8OSngex2/DaeP0ik0biQVy96QXr8axGbqwua6OV+KmalB\n" +"WQewLK8=\n" +"-----END CERTIFICATE-----",.len=1887}, + +/* Certainly Root E1 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIB9zCCAX2gAwIBAgIQBiUzsUcDMydc+Y2aub/M+DAKBggqhkjOPQQDAzA9MQswCQYDVQQG\n" +"EwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0YWlubHkgUm9vdCBFMTAe\n" +"Fw0yMTA0MDEwMDAwMDBaFw00NjA0MDEwMDAwMDBaMD0xCzAJBgNVBAYTAlVTMRIwEAYDVQQK\n" +"EwlDZXJ0YWlubHkxGjAYBgNVBAMTEUNlcnRhaW5seSBSb290IEUxMHYwEAYHKoZIzj0CAQYF\n" +"K4EEACIDYgAE3m/4fxzf7flHh4axpMCK+IKXgOqPyEpeKn2IaKcBYhSRJHpcnqMXfYqGITQY\n" +"UBsQ3tA3SybHGWCA6TS9YBk2QNYphwk8kXr2vBMj3VlOBF7PyAIcGFPBMdjaIOlEjeR2o0Iw\n" +"QDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ygYy2R17ikq\n" +"6+2uI1g4hevIIgcwCgYIKoZIzj0EAwMDaAAwZQIxALGOWiDDshliTd6wT99u0nCK8Z9+aozm\n" +"ut6Dacpps6kFtZaSF4fC0urQe87YQVt8rgIwRt7qy12a7DLCZRawTDBcMPPaTnOGBtjOiQRI\n" +"Nzf43TNRnXCve1XYAS59BWQOhriR\n" +"-----END CERTIFICATE-----",.len=739}, + +/* E-Tugra Global Root CA RSA v3 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"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\n" +"3Qt6f4ueJiDPO++BcYNZ\n" +"-----END CERTIFICATE-----",.len=2118}, + +/* E-Tugra Global Root CA ECC v3 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIICpTCCAiqgAwIBAgIUJkYZdzHhT28oNt45UYbm1JeIIsEwCgYIKoZIzj0EAwMwgYAxCzAJ\n" +"BgNVBAYTAlRSMQ8wDQYDVQQHEwZBbmthcmExGTAXBgNVBAoTEEUtVHVncmEgRUJHIEEuUy4x\n" +"HTAbBgNVBAsTFEUtVHVncmEgVHJ1c3QgQ2VudGVyMSYwJAYDVQQDEx1FLVR1Z3JhIEdsb2Jh\n" +"bCBSb290IENBIEVDQyB2MzAeFw0yMDAzMTgwOTQ2NThaFw00NTAzMTIwOTQ2NThaMIGAMQsw\n" +"CQYDVQQGEwJUUjEPMA0GA1UEBxMGQW5rYXJhMRkwFwYDVQQKExBFLVR1Z3JhIEVCRyBBLlMu\n" +"MR0wGwYDVQQLExRFLVR1Z3JhIFRydXN0IENlbnRlcjEmMCQGA1UEAxMdRS1UdWdyYSBHbG9i\n" +"YWwgUm9vdCBDQSBFQ0MgdjMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASOmCm/xxAeJ9urA8wo\n" +"LNheSBkQKczLWYHMjLiSF4mDKpL2w6QdTGLVn9agRtwcvHbB40fQWxPa56WzZkjnIZpKT4YK\n" +"fWzqTTKACrJ6CZtpS5iB4i7sAnCWH/31Rs7K3IKjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYD\n" +"VR0jBBgwFoAU/4Ixcj75xGZsrTie0bBRiKWQzPUwHQYDVR0OBBYEFP+CMXI++cRmbK04ntGw\n" +"UYilkMz1MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNpADBmAjEA5gVYaWHlLcoNy/EZ\n" +"CL3W/VGSGn5jVASQkZo1kTmZ+gepZpO6yGjUij/67W4WAie3AjEA3VoXK3YdZUKWpqxdinlW\n" +"2Iob35reX8dQj7FbcQwm32pAAOwzkSFxvmjkI6TZraE3\n" +"-----END CERTIFICATE-----",.len=974}, + +/* Security Communication RootCA3 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIIFfzCCA2egAwIBAgIJAOF8N0D9G/5nMA0GCSqGSIb3DQEBDAUAMF0xCzAJBgNVBAYTAkpQ\n" +"MSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMScwJQYDVQQDEx5TZWN1\n" +"cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTMwHhcNMTYwNjE2MDYxNzE2WhcNMzgwMTE4MDYx\n" +"NzE2WjBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4s\n" +"TFRELjEnMCUGA1UEAxMeU2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EzMIICIjANBgkq\n" +"hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48lySfcw3gl8qUCBWNO0Ot26YQ+TUG5pPDXC7ltz\n" +"kBtnTCHsXzW7OT4rCmDvu20rhvtxosis5FaU+cmvsXLUIKx00rgVrVH+hXShuRD+BYD5UpOz\n" +"QD11EKzAlrenfna84xtSGc4RHwsENPXY9Wk8d/Nk9A2qhd7gCVAEF5aEt8iKvE1y/By7z/MG\n" +"TfmfZPd+pmaGNXHIEYBMwXFAWB6+oHP2/D5Q4eAvJj1+XCO1eXDe+uDRpdYMQXF79+qMHIjH\n" +"7Iv10S9VlkZ8WjtYO/u62C21Jdp6Ts9EriGmnpjKIG58u4iFW/vAEGK78vknR+/RiTlDxN/e\n" +"4UG/VHMgly1s2vPUB6PmudhvrvyMGS7TZ2crldtYXLVqAvO4g160a75BflcJdURQVc1aEWEh\n" +"CmHCqYj9E7wtiS/NYeCVvsq1e+F7NGcLH7YMx3weGVPKp7FKFSBWFHA9K4IsD50VHUeAR/94\n" +"mQ4xr28+j+2GaR57GIgUssL8gjMunEst+3A7caoreyYn8xrC3PsXuKHqy6C0rtOUfnrQq8Ps\n" +"OC0RLoi/1D+tEjtCrI8Cbn3M0V9hvqG8OmpI6iZVIhZdXw3/JzOfGAN0iltSIEdrRU0id4xV\n" +"J/CvHozJgyJUt5rQT9nO/NkuHJYosQLTA70lUhw0Zk8jq/R3gpYd0VcwCBEF/VfR2ccCAwEA\n" +"AaNCMEAwHQYDVR0OBBYEFGQUfPxYchamCik0FW8qy7z8r6irMA4GA1UdDwEB/wQEAwIBBjAP\n" +"BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBDAUAA4ICAQDcAiMI4u8hOscNtybSYpOnpSNy\n" +"ByCCYN8Y11StaSWSntkUz5m5UoHPrmyKO1o5yGwBQ8IibQLwYs1OY0PAFNr0Y/Dq9HHuTofj\n" +"can0yVflLl8cebsjqodEV+m9NU1Bu0soo5iyG9kLFwfl9+qd9XbXv8S2gVj/yP9kaWJ5rW4O\n" +"H3/uHWnlt3Jxs/6lATWUVCvAUm2PVcTJ0rjLyjQIUYWg9by0F1jqClx6vWPGOi//lkkZhOpn\n" +"2ASxYfQAW0q3nHE3GYV5v4GwxxMOdnE+OoAGrgYWp421wsTL/0ClXI2lyTrtcoHKXJg80jQD\n" +"dwj98ClZXSEIx2C/pHF7uNkegr4Jr2VvKKu/S7XuPghHJ6APbw+LP6yVGPO5DtxnVW5inkYO\n" +"0QR4ynKudtml+LLfiAlhi+8kTtFZP1rUPcmTPCtk9YENFpb3ksP+MW/oKjJ0DvRMmEoYDjBU\n" +"1cXrvMUVnuiZIesnKwkK2/HmcBhWuwzkvvnoEKQTkrgc4NtnHVMDpCKn3F2SEDzq//wbEBrD\n" +"2NCcnWXL0CsnMQMeNuE9dnUM/0Umud1RvCPHX9jYhxBAEg09ODfnRDwYwFMJZI//1ZqmfHAu\n" +"c1Uh6N//g7kdPjIe1qZ9LPFm6Vwdp6POXiUyK+OVrCoHzrQoeIY8LaadTdJ0MN1kURXbg4NR\n" +"16/9M51NZg==\n" +"-----END CERTIFICATE-----",.len=1964}, + +/* Security Communication ECC RootCA1 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIICODCCAb6gAwIBAgIJANZdm7N4gS7rMAoGCCqGSM49BAMDMGExCzAJBgNVBAYTAkpQMSUw\n" +"IwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMSswKQYDVQQDEyJTZWN1cml0\n" +"eSBDb21tdW5pY2F0aW9uIEVDQyBSb290Q0ExMB4XDTE2MDYxNjA1MTUyOFoXDTM4MDExODA1\n" +"MTUyOFowYTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08u\n" +"LExURC4xKzApBgNVBAMTIlNlY3VyaXR5IENvbW11bmljYXRpb24gRUNDIFJvb3RDQTEwdjAQ\n" +"BgcqhkjOPQIBBgUrgQQAIgNiAASkpW9gAwPDvTH00xecK4R1rOX9PVdu12O/5gSJko6BnOPp\n" +"R27KkBLIE+CnnfdldB9sELLo5OnvbYUymUSxXv3MdhDYW72ixvnWQuRXdtyQwjWpS4g8Ekdt\n" +"XP9JTxpKULGjQjBAMB0GA1UdDgQWBBSGHOf+LaVKiwj+KBH6vqNm+GBZLzAOBgNVHQ8BAf8E\n" +"BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjAVXUI9/Lbu9zuxNuie\n" +"9sRGKEkz0FhDKmMpzE2xtHqiuQ04pV1IKv3LsnNdo4gIxwwCMQDAqy0Obe0YottT6SXbVQjg\n" +"UMzfRGEWgqtJsLKB7HOHeLRMsmIbEvoWTSVLY70eN9k=\n" +"-----END CERTIFICATE-----",.len=828}, + +/* BJCA Global Root CA1 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"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\n" +"-----END CERTIFICATE-----",.len=1947}, + +/* BJCA Global Root CA2 */ +{.str="-----BEGIN CERTIFICATE-----\n" +"MIICJTCCAaugAwIBAgIQLBcIfWQqwP6FGFkGz7RK6zAKBggqhkjOPQQDAzBUMQswCQYDVQQG\n" +"EwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRIT1JJVFkxHTAbBgNVBAMM\n" +"FEJKQ0EgR2xvYmFsIFJvb3QgQ0EyMB4XDTE5MTIxOTAzMTgyMVoXDTQ0MTIxMjAzMTgyMVow\n" +"VDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJFSUpJTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ\n" +"MR0wGwYDVQQDDBRCSkNBIEdsb2JhbCBSb290IENBMjB2MBAGByqGSM49AgEGBSuBBAAiA2IA\n" +"BJ3LgJGNU2e1uVCxA/jlSR9BIgmwUVJY1is0j8USRhTFiy8shP8sbqjV8QnjAyEUxEM9fMEs\n" +"xEtqSs3ph+B99iK++kpRuDCK/eHeGBIK9ke35xe/J4rUQUyWPGCWwf0VHKNCMEAwHQYDVR0O\n" +"BBYEFNJKsVF/BvDRgh9Obl+rg/xI1LCRMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD\n" +"AgEGMAoGCCqGSM49BAMDA2gAMGUCMBq8W9f+qdJUDkpd0m2xQNz0Q9XSSpkZElaA94M04TVO\n" +"SG0ED1cxMDAtsaqdAzjbBgIxAMvMh1PLet8gUXOQwKhbYdDFUDn9hf7B43j4ptZLvZuHjw/l\n" +"1lOWqzzIQNph91Oj9w==\n" +"-----END CERTIFICATE-----",.len=804}, +}; diff --git a/packages/bun-usockets/src/crypto/sni_tree.cpp b/packages/bun-usockets/src/crypto/sni_tree.cpp new file mode 100644 index 000000000..fbe0d38d7 --- /dev/null +++ b/packages/bun-usockets/src/crypto/sni_tree.cpp @@ -0,0 +1,218 @@ +/* + * Authored by Alex Hultman, 2018-2020. + * Intellectual property of third-party. + + * 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 + + * http://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. + */ + +/* This Server Name Indication hostname tree is written in C++ but could be ported to C. + * Overall it looks like crap, but has no memory allocations in fast path and is O(log n). */ + +#ifndef SNI_TREE_H +#define SNI_TREE_H + +#ifndef LIBUS_NO_SSL + +#include <map> +#include <memory> +#include <string_view> +#include <cstring> +#include <cstdlib> +#include <algorithm> + +/* We only handle a maximum of 10 labels per hostname */ +#define MAX_LABELS 10 + +/* This cannot be shared */ +thread_local void (*sni_free_cb)(void *); + +struct sni_node { + /* Empty nodes must always hold null */ + void *user = nullptr; + std::map<std::string_view, std::unique_ptr<sni_node>> children; + + ~sni_node() { + for (auto &p : children) { + /* The data of our string_views are managed by malloc */ + free((void *) p.first.data()); + + /* Call destructor passed to sni_free only if we hold data. + * This is important since sni_remove does not have sni_free_cb set */ + if (p.second.get()->user) { + sni_free_cb(p.second.get()->user); + } + } + } +}; + +// this can only delete ONE single node, but may cull "empty nodes with null as data" +void *removeUser(struct sni_node *root, unsigned int label, std::string_view *labels, unsigned int numLabels) { + + /* If we are in the bottom (past bottom by one), there is nothing to remove */ + if (label == numLabels) { + void *user = root->user; + /* Mark us for culling on the way up */ + root->user = nullptr; + return user; + } + + /* Is this label a child of root? */ + auto it = root->children.find(labels[label]); + if (it == root->children.end()) { + /* We cannot continue */ + return nullptr; + } + + void *removedUser = removeUser(it->second.get(), label + 1, labels, numLabels); + + /* On the way back up, we may cull empty nodes with no children. + * This ends up being where we remove all nodes */ + if (it->second.get()->children.empty() && it->second.get()->user == nullptr) { + + /* The data of our string_views are managed by malloc */ + free((void *) it->first.data()); + + /* This can only happen with user set to null, otherwise we use sni_free_cb which is unset by sni_remove */ + root->children.erase(it); + } + + return removedUser; +} + +void *getUser(struct sni_node *root, unsigned int label, std::string_view *labels, unsigned int numLabels) { + + /* Do we have labels to match? Otherwise, return where we stand */ + if (label == numLabels) { + return root->user; + } + + /* Try and match by our label */ + auto it = root->children.find(labels[label]); + if (it != root->children.end()) { + void *user = getUser(it->second.get(), label + 1, labels, numLabels); + if (user) { + return user; + } + } + + /* Try and match by wildcard */ + it = root->children.find("*"); + if (it == root->children.end()) { + /* Matching has failed for both label and wildcard */ + return nullptr; + } + + /* We matched by wildcard */ + return getUser(it->second.get(), label + 1, labels, numLabels); +} + +extern "C" { + + void *sni_new() { + return new sni_node; + } + + void sni_free(void *sni, void (*cb)(void *)) { + /* We want to run this callback for every remaining name */ + sni_free_cb = cb; + + delete (sni_node *) sni; + } + + /* Returns non-null if this name already exists */ + int sni_add(void *sni, const char *hostname, void *user) { + struct sni_node *root = (struct sni_node *) sni; + + /* Traverse all labels in hostname */ + for (std::string_view view(hostname, strlen(hostname)), label; + view.length(); view.remove_prefix(std::min(view.length(), label.length() + 1))) { + /* Label is the token separated by dot */ + label = view.substr(0, view.find('.', 0)); + + auto it = root->children.find(label); + if (it == root->children.end()) { + /* Duplicate this label for our kept string_view of it */ + void *labelString = malloc(label.length()); + memcpy(labelString, label.data(), label.length()); + + it = root->children.emplace(std::string_view((char *) labelString, label.length()), + std::make_unique<sni_node>()).first; + } + + root = it->second.get(); + } + + /* We must never add multiple contexts for the same name, as that would overwrite and leak */ + if (root->user) { + return 1; + } + + root->user = user; + + return 0; + } + + /* Removes the exact match. Wildcards are treated as the verbatim asterisk char, not as an actual wildcard */ + void *sni_remove(void *sni, const char *hostname) { + struct sni_node *root = (struct sni_node *) sni; + + /* I guess 10 labels is an okay limit */ + std::string_view labels[10]; + unsigned int numLabels = 0; + + /* We traverse all labels first of all */ + for (std::string_view view(hostname, strlen(hostname)), label; + view.length(); view.remove_prefix(std::min(view.length(), label.length() + 1))) { + /* Label is the token separated by dot */ + label = view.substr(0, view.find('.', 0)); + + /* Anything longer than 10 labels is forbidden */ + if (numLabels == 10) { + return nullptr; + } + + labels[numLabels++] = label; + } + + return removeUser(root, 0, labels, numLabels); + } + + void *sni_find(void *sni, const char *hostname) { + struct sni_node *root = (struct sni_node *) sni; + + /* I guess 10 labels is an okay limit */ + std::string_view labels[10]; + unsigned int numLabels = 0; + + /* We traverse all labels first of all */ + for (std::string_view view(hostname, strlen(hostname)), label; + view.length(); view.remove_prefix(std::min(view.length(), label.length() + 1))) { + /* Label is the token separated by dot */ + label = view.substr(0, view.find('.', 0)); + + /* Anything longer than 10 labels is forbidden */ + if (numLabels == 10) { + return nullptr; + } + + labels[numLabels++] = label; + } + + return getUser(root, 0, labels, numLabels); + } + +} + +#endif + +#endif
\ No newline at end of file diff --git a/packages/bun-usockets/src/eventing/epoll_kqueue.c b/packages/bun-usockets/src/eventing/epoll_kqueue.c new file mode 100644 index 000000000..0e2c1f92b --- /dev/null +++ b/packages/bun-usockets/src/eventing/epoll_kqueue.c @@ -0,0 +1,568 @@ +/* + * Authored by Alex Hultman, 2018-2019. + * Intellectual property of third-party. + + * 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 + + * http://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 "libusockets.h" +#include "internal/internal.h" +#include <stdlib.h> + +#if defined(LIBUS_USE_EPOLL) || defined(LIBUS_USE_KQUEUE) + +void Bun__internal_dispatch_ready_poll(void* loop, void* poll); +// void Bun__internal_dispatch_ready_poll(void* loop, void* poll) {} + +void us_loop_run_bun_tick(struct us_loop_t *loop); + +/* Cannot include this one on Windows */ +#include <unistd.h> + +/* Pointer tags are used to indicate a Bun pointer versus a uSockets pointer */ +#define UNSET_BITS_49_UNTIL_64 0x0000FFFFFFFFFFFF +#define CLEAR_POINTER_TAG(p) ((void *) ((uintptr_t) (p) & UNSET_BITS_49_UNTIL_64)) +#define LIKELY(cond) __builtin_expect((uint64_t)(void*)cond, 1) +#define UNLIKELY(cond) __builtin_expect((uint64_t)(void*)cond, 0) + +#ifdef LIBUS_USE_EPOLL +#define GET_READY_POLL(loop, index) (struct us_poll_t *) loop->ready_polls[index].data.ptr +#define SET_READY_POLL(loop, index, poll) loop->ready_polls[index].data.ptr = (void*)poll +#else +#define GET_READY_POLL(loop, index) (struct us_poll_t *) loop->ready_polls[index].udata +#define SET_READY_POLL(loop, index, poll) loop->ready_polls[index].udata = (uint64_t)poll +#endif + +/* Loop */ +void us_loop_free(struct us_loop_t *loop) { + us_internal_loop_data_free(loop); + close(loop->fd); + us_free(loop); +} + +/* Poll */ +struct us_poll_t *us_create_poll(struct us_loop_t *loop, int fallthrough, unsigned int ext_size) { + if (!fallthrough) { + loop->num_polls++; + } + return CLEAR_POINTER_TAG(us_malloc(sizeof(struct us_poll_t) + ext_size)); +} + +/* Todo: this one should be us_internal_poll_free */ +void us_poll_free(struct us_poll_t *p, struct us_loop_t *loop) { + loop->num_polls--; + us_free(p); +} + +void *us_poll_ext(struct us_poll_t *p) { + return p + 1; +} + +/* Todo: why have us_poll_create AND us_poll_init!? libuv legacy! */ +void us_poll_init(struct us_poll_t *p, LIBUS_SOCKET_DESCRIPTOR fd, int poll_type) { + p->state.fd = fd; + p->state.poll_type = poll_type; +} + +int us_poll_events(struct us_poll_t *p) { + return ((p->state.poll_type & POLL_TYPE_POLLING_IN) ? LIBUS_SOCKET_READABLE : 0) | ((p->state.poll_type & POLL_TYPE_POLLING_OUT) ? LIBUS_SOCKET_WRITABLE : 0); +} + +LIBUS_SOCKET_DESCRIPTOR us_poll_fd(struct us_poll_t *p) { + return p->state.fd; +} + +/* Returns any of listen socket, socket, shut down socket or callback */ +int us_internal_poll_type(struct us_poll_t *p) { + return p->state.poll_type & 3; +} + +/* Bug: doesn't really SET, rather read and change, so needs to be inited first! */ +void us_internal_poll_set_type(struct us_poll_t *p, int poll_type) { + p->state.poll_type = poll_type | (p->state.poll_type & 12); +} + +/* Timer */ +void *us_timer_ext(struct us_timer_t *timer) { + return ((struct us_internal_callback_t *) timer) + 1; +} + +struct us_loop_t *us_timer_loop(struct us_timer_t *t) { + struct us_internal_callback_t *internal_cb = (struct us_internal_callback_t *) t; + + return internal_cb->loop; +} + +/* Loop */ +struct us_loop_t *us_create_loop(void *hint, void (*wakeup_cb)(struct us_loop_t *loop), void (*pre_cb)(struct us_loop_t *loop), void (*post_cb)(struct us_loop_t *loop), unsigned int ext_size) { + struct us_loop_t *loop = (struct us_loop_t *) us_malloc(sizeof(struct us_loop_t) + ext_size); + loop->num_polls = 0; + /* These could be accessed if we close a poll before starting the loop */ + loop->num_ready_polls = 0; + loop->current_ready_poll = 0; + + loop->bun_polls = 0; + +#ifdef LIBUS_USE_EPOLL + loop->fd = epoll_create1(EPOLL_CLOEXEC); +#else + loop->fd = kqueue(); +#endif + + us_internal_loop_data_init(loop, wakeup_cb, pre_cb, post_cb); + return loop; +} + +void us_loop_run(struct us_loop_t *loop) { + us_loop_integrate(loop); + + /* While we have non-fallthrough polls we shouldn't fall through */ + while (loop->num_polls) { + /* Emit pre callback */ + us_internal_loop_pre(loop); + + /* Fetch ready polls */ +#ifdef LIBUS_USE_EPOLL + loop->num_ready_polls = epoll_wait(loop->fd, loop->ready_polls, 1024, -1); +#else + loop->num_ready_polls = kevent64(loop->fd, NULL, 0, loop->ready_polls, 1024, 0, NULL); +#endif + + /* Iterate ready polls, dispatching them by type */ + for (loop->current_ready_poll = 0; loop->current_ready_poll < loop->num_ready_polls; loop->current_ready_poll++) { + struct us_poll_t *poll = GET_READY_POLL(loop, loop->current_ready_poll); + /* Any ready poll marked with nullptr will be ignored */ + if (LIKELY(poll)) { + if (CLEAR_POINTER_TAG(poll) != poll) { + Bun__internal_dispatch_ready_poll(loop, poll); + continue; + } +#ifdef LIBUS_USE_EPOLL + int events = loop->ready_polls[loop->current_ready_poll].events; + int error = loop->ready_polls[loop->current_ready_poll].events & (EPOLLERR | EPOLLHUP); +#else + /* EVFILT_READ, EVFILT_TIME, EVFILT_USER are all mapped to LIBUS_SOCKET_READABLE */ + int events = LIBUS_SOCKET_READABLE; + if (loop->ready_polls[loop->current_ready_poll].filter == EVFILT_WRITE) { + events = LIBUS_SOCKET_WRITABLE; + } + int error = loop->ready_polls[loop->current_ready_poll].flags & (EV_ERROR | EV_EOF); +#endif + /* Always filter all polls by what they actually poll for (callback polls always poll for readable) */ + events &= us_poll_events(poll); + if (events || error) { + us_internal_dispatch_ready_poll(poll, error, events); + } + } + } + + /* Emit post callback */ + us_internal_loop_post(loop); + } +} + + +void us_loop_run_bun_tick(struct us_loop_t *loop) { + us_loop_integrate(loop); + + if (loop->num_polls == 0) + return; + + /* Emit pre callback */ + us_internal_loop_pre(loop); + + /* Fetch ready polls */ +#ifdef LIBUS_USE_EPOLL + loop->num_ready_polls = epoll_wait(loop->fd, loop->ready_polls, 1024, -1); +#else + struct timespec ts = {0, 0}; + loop->num_ready_polls = kevent64(loop->fd, NULL, 0, loop->ready_polls, 1024, 0, NULL); +#endif + + /* Iterate ready polls, dispatching them by type */ + for (loop->current_ready_poll = 0; loop->current_ready_poll < loop->num_ready_polls; loop->current_ready_poll++) { + struct us_poll_t *poll = GET_READY_POLL(loop, loop->current_ready_poll); + /* Any ready poll marked with nullptr will be ignored */ + if (LIKELY(poll)) { + if (CLEAR_POINTER_TAG(poll) != poll) { + Bun__internal_dispatch_ready_poll(loop, poll); + continue; + } +#ifdef LIBUS_USE_EPOLL + int events = loop->ready_polls[loop->current_ready_poll].events; + int error = loop->ready_polls[loop->current_ready_poll].events & (EPOLLERR | EPOLLHUP); +#else + /* EVFILT_READ, EVFILT_TIME, EVFILT_USER are all mapped to LIBUS_SOCKET_READABLE */ + int events = LIBUS_SOCKET_READABLE; + if (loop->ready_polls[loop->current_ready_poll].filter == EVFILT_WRITE) { + events = LIBUS_SOCKET_WRITABLE; + } + int error = loop->ready_polls[loop->current_ready_poll].flags & (EV_ERROR | EV_EOF); +#endif + /* Always filter all polls by what they actually poll for (callback polls always poll for readable) */ + events &= us_poll_events(poll); + if (events || error) { + us_internal_dispatch_ready_poll(poll, error, events); + } + } + } + + /* Emit post callback */ + us_internal_loop_post(loop); +} + +void us_internal_loop_update_pending_ready_polls(struct us_loop_t *loop, struct us_poll_t *old_poll, struct us_poll_t *new_poll, int old_events, int new_events) { +#ifdef LIBUS_USE_EPOLL + /* Epoll only has one ready poll per poll */ + int num_entries_possibly_remaining = 1; +#else + /* Ready polls may contain same poll twice under kqueue, as one poll may hold two filters */ + int num_entries_possibly_remaining = 2;//((old_events & LIBUS_SOCKET_READABLE) ? 1 : 0) + ((old_events & LIBUS_SOCKET_WRITABLE) ? 1 : 0); +#endif + + /* Todo: for kqueue if we track things in us_change_poll it is possible to have a fast path with no seeking in cases of: + * current poll being us AND we only poll for one thing */ + + for (int i = loop->current_ready_poll; i < loop->num_ready_polls && num_entries_possibly_remaining; i++) { + if (GET_READY_POLL(loop, i) == old_poll) { + + // if new events does not contain the ready events of this poll then remove (no we filter that out later on) + SET_READY_POLL(loop, i, new_poll); + + num_entries_possibly_remaining--; + } + } +} + +/* Poll */ + +#ifdef LIBUS_USE_KQUEUE +/* Helper function for setting or updating EVFILT_READ and EVFILT_WRITE */ +int kqueue_change(int kqfd, int fd, int old_events, int new_events, void *user_data) { + struct kevent64_s change_list[2]; + int change_length = 0; + + /* Do they differ in readable? */ + if ((new_events & LIBUS_SOCKET_READABLE) != (old_events & LIBUS_SOCKET_READABLE)) { + EV_SET64(&change_list[change_length++], fd, EVFILT_READ, (new_events & LIBUS_SOCKET_READABLE) ? EV_ADD : EV_DELETE, 0, 0, (uint64_t)(void*)user_data, 0, 0); + } + + /* Do they differ in writable? */ + if ((new_events & LIBUS_SOCKET_WRITABLE) != (old_events & LIBUS_SOCKET_WRITABLE)) { + EV_SET64(&change_list[change_length++], fd, EVFILT_WRITE, (new_events & LIBUS_SOCKET_WRITABLE) ? EV_ADD : EV_DELETE, 0, 0, (uint64_t)(void*)user_data, 0, 0); + } + + int ret = kevent64(kqfd, change_list, change_length, NULL, 0, 0, NULL); + + // ret should be 0 in most cases (not guaranteed when removing async) + + return ret; +} +#endif + +struct us_poll_t *us_poll_resize(struct us_poll_t *p, struct us_loop_t *loop, unsigned int ext_size) { + int events = us_poll_events(p); + + struct us_poll_t *new_p = us_realloc(p, sizeof(struct us_poll_t) + ext_size); + if (p != new_p && events) { +#ifdef LIBUS_USE_EPOLL + /* Hack: forcefully update poll by stripping away already set events */ + new_p->state.poll_type = us_internal_poll_type(new_p); + us_poll_change(new_p, loop, events); +#else + /* Forcefully update poll by resetting them with new_p as user data */ + kqueue_change(loop->fd, new_p->state.fd, 0, events, new_p); +#endif + + /* This is needed for epoll also (us_change_poll doesn't update the old poll) */ + us_internal_loop_update_pending_ready_polls(loop, p, new_p, events, events); + } + + return new_p; +} + +void us_poll_start(struct us_poll_t *p, struct us_loop_t *loop, int events) { + p->state.poll_type = us_internal_poll_type(p) | ((events & LIBUS_SOCKET_READABLE) ? POLL_TYPE_POLLING_IN : 0) | ((events & LIBUS_SOCKET_WRITABLE) ? POLL_TYPE_POLLING_OUT : 0); + +#ifdef LIBUS_USE_EPOLL + struct epoll_event event; + event.events = events; + event.data.ptr = p; + epoll_ctl(loop->fd, EPOLL_CTL_ADD, p->state.fd, &event); +#else + kqueue_change(loop->fd, p->state.fd, 0, events, p); +#endif +} + +void us_poll_change(struct us_poll_t *p, struct us_loop_t *loop, int events) { + int old_events = us_poll_events(p); + if (old_events != events) { + + p->state.poll_type = us_internal_poll_type(p) | ((events & LIBUS_SOCKET_READABLE) ? POLL_TYPE_POLLING_IN : 0) | ((events & LIBUS_SOCKET_WRITABLE) ? POLL_TYPE_POLLING_OUT : 0); + +#ifdef LIBUS_USE_EPOLL + struct epoll_event event; + event.events = events; + event.data.ptr = p; + epoll_ctl(loop->fd, EPOLL_CTL_MOD, p->state.fd, &event); +#else + kqueue_change(loop->fd, p->state.fd, old_events, events, p); +#endif + /* Set all removed events to null-polls in pending ready poll list */ + //us_internal_loop_update_pending_ready_polls(loop, p, p, old_events, events); + } +} + +void us_poll_stop(struct us_poll_t *p, struct us_loop_t *loop) { + int old_events = us_poll_events(p); + int new_events = 0; +#ifdef LIBUS_USE_EPOLL + struct epoll_event event; + epoll_ctl(loop->fd, EPOLL_CTL_DEL, p->state.fd, &event); +#else + if (old_events) { + kqueue_change(loop->fd, p->state.fd, old_events, new_events, NULL); + } +#endif + + /* Disable any instance of us in the pending ready poll list */ + us_internal_loop_update_pending_ready_polls(loop, p, 0, old_events, new_events); +} + +unsigned int us_internal_accept_poll_event(struct us_poll_t *p) { +#ifdef LIBUS_USE_EPOLL + int fd = us_poll_fd(p); + uint64_t buf; + int read_length = read(fd, &buf, 8); + (void)read_length; + return buf; +#else + /* Kqueue has no underlying FD for timers or user events */ + return 0; +#endif +} + +/* Timer */ +#ifdef LIBUS_USE_EPOLL +struct us_timer_t *us_create_timer(struct us_loop_t *loop, int fallthrough, unsigned int ext_size) { + struct us_poll_t *p = us_create_poll(loop, fallthrough, sizeof(struct us_internal_callback_t) + ext_size); + int timerfd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC); + if (timerfd == -1) { + return NULL; + } + us_poll_init(p, timerfd, POLL_TYPE_CALLBACK); + + struct us_internal_callback_t *cb = (struct us_internal_callback_t *) p; + cb->loop = loop; + cb->cb_expects_the_loop = 0; + cb->leave_poll_ready = 0; + + return (struct us_timer_t *) cb; +} +#else +struct us_timer_t *us_create_timer(struct us_loop_t *loop, int fallthrough, unsigned int ext_size) { + struct us_internal_callback_t *cb = us_malloc(sizeof(struct us_internal_callback_t) + ext_size); + + cb->loop = loop; + cb->cb_expects_the_loop = 0; + cb->leave_poll_ready = 0; + + /* Bug: us_internal_poll_set_type does not SET the type, it only CHANGES it */ + cb->p.state.poll_type = POLL_TYPE_POLLING_IN; + us_internal_poll_set_type((struct us_poll_t *) cb, POLL_TYPE_CALLBACK); + + if (!fallthrough) { + loop->num_polls++; + } + + return (struct us_timer_t *) cb; +} +#endif + +#ifdef LIBUS_USE_EPOLL +void us_timer_close(struct us_timer_t *timer) { + struct us_internal_callback_t *cb = (struct us_internal_callback_t *) timer; + + us_poll_stop(&cb->p, cb->loop); + close(us_poll_fd(&cb->p)); + + /* (regular) sockets are the only polls which are not freed immediately */ + us_poll_free((struct us_poll_t *) timer, cb->loop); +} + +void us_timer_set(struct us_timer_t *t, void (*cb)(struct us_timer_t *t), int ms, int repeat_ms) { + struct us_internal_callback_t *internal_cb = (struct us_internal_callback_t *) t; + + internal_cb->cb = (void (*)(struct us_internal_callback_t *)) cb; + + struct itimerspec timer_spec = { + {repeat_ms / 1000, (long) (repeat_ms % 1000) * (long) 1000000}, + {ms / 1000, (long) (ms % 1000) * (long) 1000000} + }; + + timerfd_settime(us_poll_fd((struct us_poll_t *) t), 0, &timer_spec, NULL); + us_poll_start((struct us_poll_t *) t, internal_cb->loop, LIBUS_SOCKET_READABLE); +} +#else +void us_timer_close(struct us_timer_t *timer) { + struct us_internal_callback_t *internal_cb = (struct us_internal_callback_t *) timer; + + struct kevent64_s event; + EV_SET64(&event, (uint64_t) (void*) internal_cb, EVFILT_TIMER, EV_DELETE, 0, 0, (uint64_t)internal_cb, 0, 0); + kevent64(internal_cb->loop->fd, &event, 1, NULL, 0, 0, NULL); + + /* (regular) sockets are the only polls which are not freed immediately */ + us_poll_free((struct us_poll_t *) timer, internal_cb->loop); +} + +void us_timer_set(struct us_timer_t *t, void (*cb)(struct us_timer_t *t), int ms, int repeat_ms) { + struct us_internal_callback_t *internal_cb = (struct us_internal_callback_t *) t; + + internal_cb->cb = (void (*)(struct us_internal_callback_t *)) cb; + + /* Bug: repeat_ms must be the same as ms, or 0 */ + struct kevent64_s event; + uint64_t ptr = (uint64_t)(void*)internal_cb; + EV_SET64(&event, ptr, EVFILT_TIMER, EV_ADD | (repeat_ms ? 0 : EV_ONESHOT), 0, ms, (uint64_t)internal_cb, 0, 0); + kevent64(internal_cb->loop->fd, &event, 1, NULL, 0, 0, NULL); +} +#endif + +/* Async (internal helper for loop's wakeup feature) */ +#ifdef LIBUS_USE_EPOLL +struct us_internal_async *us_internal_create_async(struct us_loop_t *loop, int fallthrough, unsigned int ext_size) { + struct us_poll_t *p = us_create_poll(loop, fallthrough, sizeof(struct us_internal_callback_t) + ext_size); + us_poll_init(p, eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC), POLL_TYPE_CALLBACK); + + struct us_internal_callback_t *cb = (struct us_internal_callback_t *) p; + cb->loop = loop; + cb->cb_expects_the_loop = 1; + cb->leave_poll_ready = 0; + + return (struct us_internal_async *) cb; +} + +// identical code as for timer, make it shared for "callback types" +void us_internal_async_close(struct us_internal_async *a) { + struct us_internal_callback_t *cb = (struct us_internal_callback_t *) a; + + us_poll_stop(&cb->p, cb->loop); + close(us_poll_fd(&cb->p)); + + /* (regular) sockets are the only polls which are not freed immediately */ + us_poll_free((struct us_poll_t *) a, cb->loop); +} + +void us_internal_async_set(struct us_internal_async *a, void (*cb)(struct us_internal_async *)) { + struct us_internal_callback_t *internal_cb = (struct us_internal_callback_t *) a; + + internal_cb->cb = (void (*)(struct us_internal_callback_t *)) cb; + + us_poll_start((struct us_poll_t *) a, internal_cb->loop, LIBUS_SOCKET_READABLE); +} + +void us_internal_async_wakeup(struct us_internal_async *a) { + uint64_t one = 1; + int written = write(us_poll_fd((struct us_poll_t *) a), &one, 8); + (void)written; +} +#else + +#define MACHPORT_BUF_LEN 1024 + +struct us_internal_async *us_internal_create_async(struct us_loop_t *loop, int fallthrough, unsigned int ext_size) { + struct us_internal_callback_t *cb = us_malloc(sizeof(struct us_internal_callback_t) + ext_size); + + cb->loop = loop; + cb->cb_expects_the_loop = 1; + cb->leave_poll_ready = 0; + + /* Bug: us_internal_poll_set_type does not SET the type, it only CHANGES it */ + cb->p.state.poll_type = POLL_TYPE_POLLING_IN; + us_internal_poll_set_type((struct us_poll_t *) cb, POLL_TYPE_CALLBACK); + + if (!fallthrough) { + loop->num_polls++; + } + + cb->machport_buf = us_malloc(MACHPORT_BUF_LEN); + kern_return_t kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &cb->port); + + if (UNLIKELY(kr != KERN_SUCCESS)) { + return NULL; + } + + return (struct us_internal_async *) cb; +} + +// identical code as for timer, make it shared for "callback types" +void us_internal_async_close(struct us_internal_async *a) { + struct us_internal_callback_t *internal_cb = (struct us_internal_callback_t *) a; + + struct kevent64_s event; + uint64_t ptr = (uint64_t)(void*)internal_cb; + EV_SET64(&event, ptr, EVFILT_MACHPORT, EV_DELETE, 0, 0, (uint64_t)(void*)internal_cb, 0,0); + kevent64(internal_cb->loop->fd, &event, 1, NULL, 0, 0, NULL); + + mach_port_deallocate(mach_task_self(), internal_cb->port); + us_free(internal_cb->machport_buf); + + /* (regular) sockets are the only polls which are not freed immediately */ + us_poll_free((struct us_poll_t *) a, internal_cb->loop); +} + +void us_internal_async_set(struct us_internal_async *a, void (*cb)(struct us_internal_async *)) { + struct us_internal_callback_t *internal_cb = (struct us_internal_callback_t *) a; + + internal_cb->cb = (void (*)(struct us_internal_callback_t *)) cb; + + // EVFILT_MACHPORT benchmarks faster than EVFILT_USER when using multiple threads + // Very old versions of macOS required them to be portsets instead of ports + // but that is no longer the case + // There are not many examples on the internet of using machports this way + // you can find one in Chromium's codebase. + struct kevent64_s event; + event.ident = internal_cb->port; + event.filter = EVFILT_MACHPORT; + event.flags = EV_ADD | EV_ENABLE; + event.fflags = MACH_RCV_MSG | MACH_RCV_OVERWRITE; + event.ext[0] = (uint64_t)(void*)internal_cb->machport_buf; + event.ext[1] = MACHPORT_BUF_LEN; + event.udata = (uint64_t)(void*)internal_cb; + + int ret = kevent64(internal_cb->loop->fd, &event, 1, NULL, 0, 0, NULL); + + if (UNLIKELY(ret == -1)) { + abort(); + } +} + +void us_internal_async_wakeup(struct us_internal_async *a) { + struct us_internal_callback_t *internal_cb = (struct us_internal_callback_t *) a; + mach_msg_empty_send_t message; + memset(&message, 0, sizeof(message)); + message.header.msgh_size = sizeof(message); + message.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MAKE_SEND_ONCE); + message.header.msgh_remote_port = internal_cb->port; + kern_return_t kr = mach_msg_send(&message.header); + if (kr != KERN_SUCCESS) { + // If us_internal_async_wakeup is being called by other threads faster + // than the pump can dispatch work, the kernel message queue for the wakeup + // port can fill The kernel does return a SEND_ONCE right in the case of + // failure, which must be destroyed to avoid leaking. + mach_msg_destroy(&message.header); + } +} +#endif + +#endif diff --git a/packages/bun-usockets/src/internal/eventing/epoll_kqueue.h b/packages/bun-usockets/src/internal/eventing/epoll_kqueue.h new file mode 100644 index 000000000..ae46e91e7 --- /dev/null +++ b/packages/bun-usockets/src/internal/eventing/epoll_kqueue.h @@ -0,0 +1,72 @@ +/* + * Authored by Alex Hultman, 2018-2019. + * Intellectual property of third-party. + + * 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 + + * http://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. + */ + +#ifndef EPOLL_KQUEUE_H +#define EPOLL_KQUEUE_H + +#include "internal/loop_data.h" + +#ifdef LIBUS_USE_EPOLL +#include <sys/epoll.h> +#include <sys/timerfd.h> +#include <sys/eventfd.h> +#define LIBUS_SOCKET_READABLE EPOLLIN +#define LIBUS_SOCKET_WRITABLE EPOLLOUT +#else +#include <sys/event.h> +/* Kqueue's EVFILT_ is NOT a bitfield, you cannot OR together them. + * We therefore have our own bitfield we then translate in every call */ +#define LIBUS_SOCKET_READABLE 1 +#define LIBUS_SOCKET_WRITABLE 2 + +#include <mach/mach.h> +#endif + +struct us_loop_t { + alignas(LIBUS_EXT_ALIGNMENT) struct us_internal_loop_data_t data; + + /* Number of non-fallthrough polls in the loop */ + int num_polls; + + /* Number of ready polls this iteration */ + int num_ready_polls; + + /* Current index in list of ready polls */ + int current_ready_poll; + + /* Loop's own file descriptor */ + int fd; + + /* Number of polls owned by bun */ + unsigned int bun_polls; + + /* The list of ready polls */ +#ifdef LIBUS_USE_EPOLL + alignas(LIBUS_EXT_ALIGNMENT) struct epoll_event ready_polls[1024]; +#else + alignas(LIBUS_EXT_ALIGNMENT) struct kevent64_s ready_polls[1024]; +#endif +}; + +struct us_poll_t { + alignas(LIBUS_EXT_ALIGNMENT) struct { + signed int fd : 28; // we could have this unsigned if we wanted to, -1 should never be used + unsigned int poll_type : 4; + } state; +}; + +#endif // EPOLL_KQUEUE_H diff --git a/packages/bun-usockets/src/internal/internal.h b/packages/bun-usockets/src/internal/internal.h new file mode 100644 index 000000000..2d4719571 --- /dev/null +++ b/packages/bun-usockets/src/internal/internal.h @@ -0,0 +1,245 @@ +/* + * Authored by Alex Hultman, 2018-2019. + * Intellectual property of third-party. + + * 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 + + * http://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. + */ + +#ifndef INTERNAL_H +#define INTERNAL_H + + +#if defined(_MSC_VER) +#define alignas(x) __declspec(align(x)) +#else +#include <stdalign.h> +#endif + +#if defined(LIBUS_USE_KQUEUE) +#include <mach/mach.h> +#endif + +#if defined(LIBUS_USE_EPOLL) || defined(LIBUS_USE_KQUEUE) +#define LIBUS_MAX_READY_POLLS 1024 + +void us_internal_loop_update_pending_ready_polls(struct us_loop_t *loop, struct us_poll_t *old_poll, struct us_poll_t *new_poll, int old_events, int new_events); +#endif + +/* We only have one networking implementation so far */ +#include "internal/networking/bsd.h" + +/* We have many different eventing implementations */ +#if defined(LIBUS_USE_EPOLL) || defined(LIBUS_USE_KQUEUE) +#include "internal/eventing/epoll_kqueue.h" +#endif + +/* Poll type and what it polls for */ +enum { + /* Two first bits */ + POLL_TYPE_SOCKET = 0, + POLL_TYPE_SOCKET_SHUT_DOWN = 1, + POLL_TYPE_SEMI_SOCKET = 2, + POLL_TYPE_CALLBACK = 3, + + /* Two last bits */ + POLL_TYPE_POLLING_OUT = 4, + POLL_TYPE_POLLING_IN = 8 +}; + +/* Loop related */ +void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int events); +void us_internal_timer_sweep(struct us_loop_t *loop); +void us_internal_free_closed_sockets(struct us_loop_t *loop); +void us_internal_loop_link(struct us_loop_t *loop, struct us_socket_context_t *context); +void us_internal_loop_unlink(struct us_loop_t *loop, struct us_socket_context_t *context); +void us_internal_loop_data_init(struct us_loop_t *loop, void (*wakeup_cb)(struct us_loop_t *loop), + void (*pre_cb)(struct us_loop_t *loop), void (*post_cb)(struct us_loop_t *loop)); +void us_internal_loop_data_free(struct us_loop_t *loop); +void us_internal_loop_pre(struct us_loop_t *loop); +void us_internal_loop_post(struct us_loop_t *loop); + +/* Asyncs (old) */ +struct us_internal_async *us_internal_create_async(struct us_loop_t *loop, int fallthrough, unsigned int ext_size); +void us_internal_async_close(struct us_internal_async *a); +void us_internal_async_set(struct us_internal_async *a, void (*cb)(struct us_internal_async *)); +void us_internal_async_wakeup(struct us_internal_async *a); + +/* Eventing related */ +unsigned int us_internal_accept_poll_event(struct us_poll_t *p); +int us_internal_poll_type(struct us_poll_t *p); +void us_internal_poll_set_type(struct us_poll_t *p, int poll_type); + +/* SSL loop data */ +void us_internal_init_loop_ssl_data(struct us_loop_t *loop); +void us_internal_free_loop_ssl_data(struct us_loop_t *loop); + +/* Socket context related */ +void us_internal_socket_context_link_socket(struct us_socket_context_t *context, struct us_socket_t *s); +void us_internal_socket_context_unlink_socket(struct us_socket_context_t *context, struct us_socket_t *s); + +/* Sockets are polls */ +struct us_socket_t { + alignas(LIBUS_EXT_ALIGNMENT) struct us_poll_t p; // 4 bytes + unsigned char timeout; // 1 byte + unsigned char long_timeout; // 1 byte + unsigned short low_prio_state; /* 0 = not in low-prio queue, 1 = is in low-prio queue, 2 = was in low-prio queue in this iteration */ + struct us_socket_context_t *context; + struct us_socket_t *prev, *next; +}; + +struct us_wrapped_socket_context_t { + struct us_socket_events_t events; + struct us_socket_events_t old_events; +}; + +#if defined(LIBUS_USE_KQUEUE) +/* Internal callback types are polls just like sockets */ +struct us_internal_callback_t { + alignas(LIBUS_EXT_ALIGNMENT) struct us_poll_t p; + struct us_loop_t *loop; + int cb_expects_the_loop; + int leave_poll_ready; + void (*cb)(struct us_internal_callback_t *cb); + mach_port_t port; + void* machport_buf; +}; + +#else + +struct us_internal_callback_t { + alignas(LIBUS_EXT_ALIGNMENT) struct us_poll_t p; + struct us_loop_t *loop; + int cb_expects_the_loop; + int leave_poll_ready; + void (*cb)(struct us_internal_callback_t *cb); +}; + +#endif + +/* Listen sockets are sockets */ +struct us_listen_socket_t { + alignas(LIBUS_EXT_ALIGNMENT) struct us_socket_t s; + unsigned int socket_ext_size; +}; + +/* Listen sockets are keps in their own list */ +void us_internal_socket_context_link_listen_socket(struct us_socket_context_t *context, struct us_listen_socket_t *s); +void us_internal_socket_context_unlink_listen_socket(struct us_socket_context_t *context, struct us_listen_socket_t *s); + +struct us_socket_context_t { + alignas(LIBUS_EXT_ALIGNMENT) struct us_loop_t *loop; + uint32_t global_tick; + unsigned char timestamp; + unsigned char long_timestamp; + struct us_socket_t *head_sockets; + struct us_listen_socket_t *head_listen_sockets; + struct us_socket_t *iterator; + struct us_socket_context_t *prev, *next; + + struct us_socket_t *(*on_open)(struct us_socket_t *, int is_client, char *ip, int ip_length); + struct us_socket_t *(*on_data)(struct us_socket_t *, char *data, int length); + struct us_socket_t *(*on_writable)(struct us_socket_t *); + struct us_socket_t *(*on_close)(struct us_socket_t *, int code, void *reason); + //void (*on_timeout)(struct us_socket_context *); + struct us_socket_t *(*on_socket_timeout)(struct us_socket_t *); + struct us_socket_t *(*on_socket_long_timeout)(struct us_socket_t *); + struct us_socket_t *(*on_end)(struct us_socket_t *); + struct us_socket_t *(*on_connect_error)(struct us_socket_t *, int code); + int (*is_low_prio)(struct us_socket_t *); +}; + +/* Internal SSL interface */ +#ifndef LIBUS_NO_SSL + +struct us_internal_ssl_socket_context_t; +struct us_internal_ssl_socket_t; + +/* SNI functions */ +void us_internal_ssl_socket_context_add_server_name(struct us_internal_ssl_socket_context_t *context, const char *hostname_pattern, struct us_socket_context_options_t options, void *user); +void us_bun_internal_ssl_socket_context_add_server_name(struct us_internal_ssl_socket_context_t *context, const char *hostname_pattern, struct us_bun_socket_context_options_t options, void *user); +void us_internal_ssl_socket_context_remove_server_name(struct us_internal_ssl_socket_context_t *context, const char *hostname_pattern); +void us_internal_ssl_socket_context_on_server_name(struct us_internal_ssl_socket_context_t *context, void (*cb)(struct us_internal_ssl_socket_context_t *, const char *)); +void *us_internal_ssl_socket_get_sni_userdata(struct us_internal_ssl_socket_t *s); +void *us_internal_ssl_socket_context_find_server_name_userdata(struct us_internal_ssl_socket_context_t *context, const char *hostname_pattern); + +void *us_internal_ssl_socket_get_native_handle(struct us_internal_ssl_socket_t *s); +void *us_internal_ssl_socket_context_get_native_handle(struct us_internal_ssl_socket_context_t *context); +struct us_bun_verify_error_t us_internal_verify_error(struct us_internal_ssl_socket_t *s); +struct us_internal_ssl_socket_context_t *us_internal_create_ssl_socket_context(struct us_loop_t *loop, + int context_ext_size, struct us_socket_context_options_t options); +struct us_internal_ssl_socket_context_t *us_internal_bun_create_ssl_socket_context(struct us_loop_t *loop, + int context_ext_size, struct us_bun_socket_context_options_t options); + +void us_internal_ssl_socket_context_free(struct us_internal_ssl_socket_context_t *context); +void us_internal_ssl_socket_context_on_open(struct us_internal_ssl_socket_context_t *context, + struct us_internal_ssl_socket_t *(*on_open)(struct us_internal_ssl_socket_t *s, int is_client, char *ip, int ip_length)); + +void us_internal_ssl_socket_context_on_close(struct us_internal_ssl_socket_context_t *context, + struct us_internal_ssl_socket_t *(*on_close)(struct us_internal_ssl_socket_t *s, int code, void *reason)); + +void us_internal_ssl_socket_context_on_data(struct us_internal_ssl_socket_context_t *context, + struct us_internal_ssl_socket_t *(*on_data)(struct us_internal_ssl_socket_t *s, char *data, int length)); + +void us_internal_ssl_handshake(struct us_internal_ssl_socket_t *s, void (*on_handshake)(struct us_internal_ssl_socket_t *, int success, struct us_bun_verify_error_t verify_error, void* custom_data), void* custom_data); +void us_internal_on_ssl_handshake(struct us_internal_ssl_socket_context_t * context, void (*on_handshake)(struct us_internal_ssl_socket_t *, int success, struct us_bun_verify_error_t verify_error, void* custom_data), void* custom_data); + +void us_internal_ssl_socket_context_on_writable(struct us_internal_ssl_socket_context_t *context, + struct us_internal_ssl_socket_t *(*on_writable)(struct us_internal_ssl_socket_t *s)); + +void us_internal_ssl_socket_context_on_timeout(struct us_internal_ssl_socket_context_t *context, + struct us_internal_ssl_socket_t *(*on_timeout)(struct us_internal_ssl_socket_t *s)); + +void us_internal_ssl_socket_context_on_long_timeout(struct us_internal_ssl_socket_context_t *context, + struct us_internal_ssl_socket_t *(*on_timeout)(struct us_internal_ssl_socket_t *s)); + +void us_internal_ssl_socket_context_on_end(struct us_internal_ssl_socket_context_t *context, + struct us_internal_ssl_socket_t *(*on_end)(struct us_internal_ssl_socket_t *s)); + +void us_internal_ssl_socket_context_on_connect_error(struct us_internal_ssl_socket_context_t *context, + struct us_internal_ssl_socket_t *(*on_connect_error)(struct us_internal_ssl_socket_t *s, int code)); + +struct us_listen_socket_t *us_internal_ssl_socket_context_listen(struct us_internal_ssl_socket_context_t *context, + const char *host, int port, int options, int socket_ext_size); + +struct us_listen_socket_t *us_internal_ssl_socket_context_listen_unix(struct us_internal_ssl_socket_context_t *context, + const char *path, int options, int socket_ext_size); + +struct us_internal_ssl_socket_t *us_internal_ssl_socket_context_connect(struct us_internal_ssl_socket_context_t *context, + const char *host, int port, const char *source_host, int options, int socket_ext_size); + + +struct us_internal_ssl_socket_t *us_internal_ssl_socket_context_connect_unix(struct us_internal_ssl_socket_context_t *context, + const char *server_path, int options, int socket_ext_size); + +int us_internal_ssl_socket_write(struct us_internal_ssl_socket_t *s, const char *data, int length, int msg_more); +int us_internal_ssl_socket_raw_write(struct us_internal_ssl_socket_t *s, const char *data, int length, int msg_more); + +void us_internal_ssl_socket_timeout(struct us_internal_ssl_socket_t *s, unsigned int seconds); +void *us_internal_ssl_socket_context_ext(struct us_internal_ssl_socket_context_t *s); +struct us_internal_ssl_socket_context_t *us_internal_ssl_socket_get_context(struct us_internal_ssl_socket_t *s); +void *us_internal_ssl_socket_ext(struct us_internal_ssl_socket_t *s); +int us_internal_ssl_socket_is_shut_down(struct us_internal_ssl_socket_t *s); +void us_internal_ssl_socket_shutdown(struct us_internal_ssl_socket_t *s); + +struct us_internal_ssl_socket_t *us_internal_ssl_socket_context_adopt_socket(struct us_internal_ssl_socket_context_t *context, + struct us_internal_ssl_socket_t *s, int ext_size); + +struct us_internal_ssl_socket_t *us_internal_ssl_socket_wrap_with_tls(struct us_socket_t *s, struct us_bun_socket_context_options_t options, struct us_socket_events_t events, int socket_ext_size); +struct us_internal_ssl_socket_context_t *us_internal_create_child_ssl_socket_context(struct us_internal_ssl_socket_context_t *context, int context_ext_size); +struct us_loop_t *us_internal_ssl_socket_context_loop(struct us_internal_ssl_socket_context_t *context); +struct us_internal_ssl_socket_t* us_internal_ssl_socket_open(struct us_internal_ssl_socket_t * s, int is_client, char* ip, int ip_length); + +int us_raw_root_certs(struct us_cert_string_t**out); +#endif + +#endif // INTERNAL_H diff --git a/packages/bun-usockets/src/internal/loop_data.h b/packages/bun-usockets/src/internal/loop_data.h new file mode 100644 index 000000000..468c5235b --- /dev/null +++ b/packages/bun-usockets/src/internal/loop_data.h @@ -0,0 +1,38 @@ +/* + * Authored by Alex Hultman, 2018-2019. + * Intellectual property of third-party. + + * 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 + + * http://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. + */ + +#ifndef LOOP_DATA_H +#define LOOP_DATA_H + +struct us_internal_loop_data_t { + struct us_timer_t *sweep_timer; + struct us_internal_async *wakeup_async; + int last_write_failed; + struct us_socket_context_t *head; + struct us_socket_context_t *iterator; + char *recv_buf; + void *ssl_data; + void (*pre_cb)(struct us_loop_t *); + void (*post_cb)(struct us_loop_t *); + struct us_socket_t *closed_head; + struct us_socket_t *low_prio_head; + int low_prio_budget; + /* We do not care if this flips or not, it doesn't matter */ + long long iteration_nr; +}; + +#endif // LOOP_DATA_H diff --git a/packages/bun-usockets/src/internal/networking/bsd.h b/packages/bun-usockets/src/internal/networking/bsd.h new file mode 100644 index 000000000..d1a7be9e9 --- /dev/null +++ b/packages/bun-usockets/src/internal/networking/bsd.h @@ -0,0 +1,108 @@ +/* + * Authored by Alex Hultman, 2018-2019. + * Intellectual property of third-party. + + * 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 + + * http://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. + */ + +#ifndef BSD_H +#define BSD_H + +// top-most wrapper of bsd-like syscalls + +// holds everything you need from the bsd/winsock interfaces, only included by internal libusockets.h +// here everything about the syscalls are inline-wrapped and included + +#include "libusockets.h" + +#ifdef _WIN32 +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include <winsock2.h> +#include <ws2tcpip.h> +#pragma comment(lib, "ws2_32.lib") +#define SETSOCKOPT_PTR_TYPE const char * +#define LIBUS_SOCKET_ERROR INVALID_SOCKET +#else +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +/* For socklen_t */ +#include <sys/socket.h> +#define SETSOCKOPT_PTR_TYPE int * +#define LIBUS_SOCKET_ERROR -1 +#endif + +#define LIBUS_UDP_MAX_SIZE (64 * 1024) +#define LIBUS_UDP_MAX_NUM 1024 + +struct bsd_addr_t { + struct sockaddr_storage mem; + socklen_t len; + char *ip; + int ip_length; + int port; +}; + +int bsd_sendmmsg(LIBUS_SOCKET_DESCRIPTOR fd, void *msgvec, unsigned int vlen, int flags); +int bsd_recvmmsg(LIBUS_SOCKET_DESCRIPTOR fd, void *msgvec, unsigned int vlen, int flags, void *timeout); +int bsd_udp_packet_buffer_payload_length(void *msgvec, int index); +char *bsd_udp_packet_buffer_payload(void *msgvec, int index); +char *bsd_udp_packet_buffer_peer(void *msgvec, int index); +int bsd_udp_packet_buffer_local_ip(void *msgvec, int index, char *ip); +int bsd_udp_packet_buffer_ecn(void *msgvec, int index); +void *bsd_create_udp_packet_buffer(); +void bsd_udp_buffer_set_packet_payload(struct us_udp_packet_buffer_t *send_buf, int index, int offset, void *payload, int length, void *peer_addr); + +LIBUS_SOCKET_DESCRIPTOR apple_no_sigpipe(LIBUS_SOCKET_DESCRIPTOR fd); +LIBUS_SOCKET_DESCRIPTOR bsd_set_nonblocking(LIBUS_SOCKET_DESCRIPTOR fd); +void bsd_socket_nodelay(LIBUS_SOCKET_DESCRIPTOR fd, int enabled); +void bsd_socket_flush(LIBUS_SOCKET_DESCRIPTOR fd); +LIBUS_SOCKET_DESCRIPTOR bsd_create_socket(int domain, int type, int protocol); + +void bsd_close_socket(LIBUS_SOCKET_DESCRIPTOR fd); +void bsd_shutdown_socket(LIBUS_SOCKET_DESCRIPTOR fd); +void bsd_shutdown_socket_read(LIBUS_SOCKET_DESCRIPTOR fd); + +void internal_finalize_bsd_addr(struct bsd_addr_t *addr); + +int bsd_local_addr(LIBUS_SOCKET_DESCRIPTOR fd, struct bsd_addr_t *addr); +int bsd_remote_addr(LIBUS_SOCKET_DESCRIPTOR fd, struct bsd_addr_t *addr); + +char *bsd_addr_get_ip(struct bsd_addr_t *addr); +int bsd_addr_get_ip_length(struct bsd_addr_t *addr); + +int bsd_addr_get_port(struct bsd_addr_t *addr); + +// called by dispatch_ready_poll +LIBUS_SOCKET_DESCRIPTOR bsd_accept_socket(LIBUS_SOCKET_DESCRIPTOR fd, struct bsd_addr_t *addr); + +int bsd_recv(LIBUS_SOCKET_DESCRIPTOR fd, void *buf, int length, int flags); +int bsd_send(LIBUS_SOCKET_DESCRIPTOR fd, const char *buf, int length, int msg_more); +int bsd_would_block(); + +// return LIBUS_SOCKET_ERROR or the fd that represents listen socket +// listen both on ipv6 and ipv4 +LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket(const char *host, int port, int options); + +LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket_unix(const char *path, int options); + +/* Creates an UDP socket bound to the hostname and port */ +LIBUS_SOCKET_DESCRIPTOR bsd_create_udp_socket(const char *host, int port); + +LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket(const char *host, int port, const char *source_host, int options); + +LIBUS_SOCKET_DESCRIPTOR bsd_create_connect_socket_unix(const char *server_path, int options); + +#endif // BSD_H diff --git a/packages/bun-usockets/src/libusockets.h b/packages/bun-usockets/src/libusockets.h new file mode 100644 index 000000000..130e6f6ab --- /dev/null +++ b/packages/bun-usockets/src/libusockets.h @@ -0,0 +1,408 @@ +/* + * Authored by Alex Hultman, 2018-2019. + * Intellectual property of third-party. + + * 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 + + * http://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. + */ + +#ifndef us_malloc +#define us_malloc malloc +#endif + +#ifndef us_realloc +#define us_realloc realloc +#endif + +#ifndef us_free +#define us_free free +#endif + +#ifndef LIBUSOCKETS_H +#define LIBUSOCKETS_H + + +/* 512kb shared receive buffer */ +#define LIBUS_RECV_BUFFER_LENGTH 524288 +/* A timeout granularity of 4 seconds means give or take 4 seconds from set timeout */ +#define LIBUS_TIMEOUT_GRANULARITY 4 +/* 32 byte padding of receive buffer ends */ +#define LIBUS_RECV_BUFFER_PADDING 32 +/* Guaranteed alignment of extension memory */ +#define LIBUS_EXT_ALIGNMENT 16 + +/* Define what a socket descriptor is based on platform */ +#ifdef _WIN32 +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include <winsock2.h> +#define LIBUS_SOCKET_DESCRIPTOR SOCKET +#else +#define LIBUS_SOCKET_DESCRIPTOR int +#endif + +#include "stddef.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + /* No meaning, default listen option */ + LIBUS_LISTEN_DEFAULT, + /* We exclusively own this port, do not share it */ + LIBUS_LISTEN_EXCLUSIVE_PORT +}; + +/* Library types publicly available */ +struct us_socket_t; +struct us_timer_t; +struct us_socket_context_t; +struct us_loop_t; +struct us_poll_t; +struct us_udp_socket_t; +struct us_udp_packet_buffer_t; + + +struct us_cert_string_t { + const char* str; + size_t len; +}; + +/* Public interface for UDP sockets */ + +/* Peeks data and length of UDP payload */ +char *us_udp_packet_buffer_payload(struct us_udp_packet_buffer_t *buf, int index); +int us_udp_packet_buffer_payload_length(struct us_udp_packet_buffer_t *buf, int index); + +/* Copies out local (received destination) ip (4 or 16 bytes) of received packet */ +int us_udp_packet_buffer_local_ip(struct us_udp_packet_buffer_t *buf, int index, char *ip); + +/* Get the bound port in host byte order */ +int us_udp_socket_bound_port(struct us_udp_socket_t *s); + +/* Peeks peer addr (sockaddr) of received packet */ +char *us_udp_packet_buffer_peer(struct us_udp_packet_buffer_t *buf, int index); + +/* Peeks ECN of received packet */ +int us_udp_packet_buffer_ecn(struct us_udp_packet_buffer_t *buf, int index); + +/* Receives a set of packets into specified packet buffer */ +int us_udp_socket_receive(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf); + +void us_udp_buffer_set_packet_payload(struct us_udp_packet_buffer_t *send_buf, int index, int offset, void *payload, int length, void *peer_addr); + +int us_udp_socket_send(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int num); + +/* Allocates a packet buffer that is reuable per thread. Mutated by us_udp_socket_receive. */ +struct us_udp_packet_buffer_t *us_create_udp_packet_buffer(); + +/* Creates a (heavy-weight) UDP socket with a user space ring buffer. Again, this one is heavy weight and + * shoud be reused. One entire QUIC server can be implemented using only one single UDP socket so weight + * is not a concern as is the case for TCP sockets which are 1-to-1 with TCP connections. */ +//struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, void (*read_cb)(struct us_udp_socket_t *), unsigned short port); + +//struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int), void (*drain_cb)(struct us_udp_socket_t *), char *host, unsigned short port); + +struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, struct us_udp_packet_buffer_t *buf, void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int), void (*drain_cb)(struct us_udp_socket_t *), const char *host, unsigned short port, void *user); + +/* This one is ugly, should be ext! not user */ +void *us_udp_socket_user(struct us_udp_socket_t *s); + +/* Binds the UDP socket to an interface and port */ +int us_udp_socket_bind(struct us_udp_socket_t *s, const char *hostname, unsigned int port); + +/* Public interfaces for timers */ + +/* Create a new high precision, low performance timer. May fail and return null */ +struct us_timer_t *us_create_timer(struct us_loop_t *loop, int fallthrough, unsigned int ext_size); + +/* Returns user data extension for this timer */ +void *us_timer_ext(struct us_timer_t *timer); + +/* */ +void us_timer_close(struct us_timer_t *timer); + +/* Arm a timer with a delay from now and eventually a repeat delay. + * Specify 0 as repeat delay to disable repeating. Specify both 0 to disarm. */ +void us_timer_set(struct us_timer_t *timer, void (*cb)(struct us_timer_t *t), int ms, int repeat_ms); + +/* Returns the loop for this timer */ +struct us_loop_t *us_timer_loop(struct us_timer_t *t); + +/* Public interfaces for contexts */ + +struct us_socket_context_options_t { + const char *key_file_name; + const char *cert_file_name; + const char *passphrase; + const char *dh_params_file_name; + const char *ca_file_name; + const char *ssl_ciphers; + int ssl_prefer_low_memory_usage; /* Todo: rename to prefer_low_memory_usage and apply for TCP as well */ +}; + +struct us_socket_events_t { + struct us_socket_t *(*on_open)(struct us_socket_t *, int is_client, char *ip, int ip_length); + struct us_socket_t *(*on_data)(struct us_socket_t *, char *data, int length); + struct us_socket_t *(*on_writable)(struct us_socket_t *); + struct us_socket_t *(*on_close)(struct us_socket_t *, int code, void *reason); + //void (*on_timeout)(struct us_socket_context *); + struct us_socket_t *(*on_timeout)(struct us_socket_t *); + struct us_socket_t *(*on_long_timeout)(struct us_socket_t *); + struct us_socket_t *(*on_end)(struct us_socket_t *); + struct us_socket_t *(*on_connect_error)(struct us_socket_t *, int code); + void (*on_handshake)(struct us_socket_t*, int success, struct us_bun_verify_error_t verify_error, void* custom_data); +}; + +struct us_bun_verify_error_t { + long error; + const char* code; + const char* reason; +}; + +struct us_bun_socket_context_options_t { + const char *key_file_name; + const char *cert_file_name; + const char *passphrase; + const char *dh_params_file_name; + const char *ca_file_name; + const char *ssl_ciphers; + int ssl_prefer_low_memory_usage; /* Todo: rename to prefer_low_memory_usage and apply for TCP as well */ + const char **key; + unsigned int key_count; + const char **cert; + unsigned int cert_count; + const char **ca; + unsigned int ca_count; + unsigned int secure_options; + int reject_unauthorized; + int request_cert; +}; + +/* Return 15-bit timestamp for this context */ +unsigned short us_socket_context_timestamp(int ssl, struct us_socket_context_t *context); + +/* Adds SNI domain and cert in asn1 format */ +void us_socket_context_add_server_name(int ssl, struct us_socket_context_t *context, const char *hostname_pattern, struct us_socket_context_options_t options, void *user); +void us_bun_socket_context_add_server_name(int ssl, struct us_socket_context_t *context, const char *hostname_pattern, struct us_bun_socket_context_options_t options, void *user); +void us_socket_context_remove_server_name(int ssl, struct us_socket_context_t *context, const char *hostname_pattern); +void us_socket_context_on_server_name(int ssl, struct us_socket_context_t *context, void (*cb)(struct us_socket_context_t *, const char *hostname)); +void *us_socket_server_name_userdata(int ssl, struct us_socket_t *s); +void *us_socket_context_find_server_name_userdata(int ssl, struct us_socket_context_t *context, const char *hostname_pattern); + +/* Returns the underlying SSL native handle, such as SSL_CTX or nullptr */ +void *us_socket_context_get_native_handle(int ssl, struct us_socket_context_t *context); + +/* A socket context holds shared callbacks and user data extension for associated sockets */ +struct us_socket_context_t *us_create_socket_context(int ssl, struct us_loop_t *loop, + int ext_size, struct us_socket_context_options_t options); +struct us_socket_context_t *us_create_bun_socket_context(int ssl, struct us_loop_t *loop, + int ext_size, struct us_bun_socket_context_options_t options); + +/* Delete resources allocated at creation time. */ +void us_socket_context_free(int ssl, struct us_socket_context_t *context); +struct us_bun_verify_error_t us_socket_verify_error(int ssl, struct us_socket_t *context); +/* Setters of various async callbacks */ +void us_socket_context_on_open(int ssl, struct us_socket_context_t *context, + struct us_socket_t *(*on_open)(struct us_socket_t *s, int is_client, char *ip, int ip_length)); +void us_socket_context_on_close(int ssl, struct us_socket_context_t *context, + struct us_socket_t *(*on_close)(struct us_socket_t *s, int code, void *reason)); +void us_socket_context_on_data(int ssl, struct us_socket_context_t *context, + struct us_socket_t *(*on_data)(struct us_socket_t *s, char *data, int length)); +void us_socket_context_on_writable(int ssl, struct us_socket_context_t *context, + struct us_socket_t *(*on_writable)(struct us_socket_t *s)); +void us_socket_context_on_timeout(int ssl, struct us_socket_context_t *context, + struct us_socket_t *(*on_timeout)(struct us_socket_t *s)); +void us_socket_context_on_long_timeout(int ssl, struct us_socket_context_t *context, + struct us_socket_t *(*on_timeout)(struct us_socket_t *s)); +/* This one is only used for when a connecting socket fails in a late stage. */ +void us_socket_context_on_connect_error(int ssl, struct us_socket_context_t *context, + struct us_socket_t *(*on_connect_error)(struct us_socket_t *s, int code)); + +void us_socket_context_on_handshake(int ssl, struct us_socket_context_t *context, void (*on_handshake)(struct us_socket_context_t *, int success, struct us_bun_verify_error_t verify_error, void* custom_data), void* custom_data); + +/* Emitted when a socket has been half-closed */ +void us_socket_context_on_end(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_end)(struct us_socket_t *s)); + +/* Returns user data extension for this socket context */ +void *us_socket_context_ext(int ssl, struct us_socket_context_t *context); + +/* Closes all open sockets, including listen sockets. Does not invalidate the socket context. */ +void us_socket_context_close(int ssl, struct us_socket_context_t *context); + +/* Listen for connections. Acts as the main driving cog in a server. Will call set async callbacks. */ +struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_context_t *context, + const char *host, int port, int options, int socket_ext_size); + +struct us_listen_socket_t *us_socket_context_listen_unix(int ssl, struct us_socket_context_t *context, + const char *path, int options, int socket_ext_size); + +/* listen_socket.c/.h */ +void us_listen_socket_close(int ssl, struct us_listen_socket_t *ls); + +/* Land in on_open or on_connection_error or return null or return socket */ +struct us_socket_t *us_socket_context_connect(int ssl, struct us_socket_context_t *context, + const char *host, int port, const char *source_host, int options, int socket_ext_size); + +struct us_socket_t *us_socket_context_connect_unix(int ssl, struct us_socket_context_t *context, + const char *server_path, int options, int socket_ext_size); + +/* Is this socket established? Can be used to check if a connecting socket has fired the on_open event yet. + * Can also be used to determine if a socket is a listen_socket or not, but you probably know that already. */ +int us_socket_is_established(int ssl, struct us_socket_t *s); + +/* Cancel a connecting socket. Can be used together with us_socket_timeout to limit connection times. + * Entirely destroys the socket - this function works like us_socket_close but does not trigger on_close event since + * you never got the on_open event first. */ +struct us_socket_t *us_socket_close_connecting(int ssl, struct us_socket_t *s); + +/* Returns the loop for this socket context. */ +struct us_loop_t *us_socket_context_loop(int ssl, struct us_socket_context_t *context); + +/* Invalidates passed socket, returning a new resized socket which belongs to a different socket context. + * Used mainly for "socket upgrades" such as when transitioning from HTTP to WebSocket. */ +struct us_socket_t *us_socket_context_adopt_socket(int ssl, struct us_socket_context_t *context, struct us_socket_t *s, int ext_size); + +/* Create a child socket context which acts much like its own socket context with its own callbacks yet still relies on the + * parent socket context for some shared resources. Child socket contexts should be used together with socket adoptions and nothing else. */ +struct us_socket_context_t *us_create_child_socket_context(int ssl, struct us_socket_context_t *context, int context_ext_size); + +/* Public interfaces for loops */ + +/* Returns a new event loop with user data extension */ +struct us_loop_t *us_create_loop(void *hint, void (*wakeup_cb)(struct us_loop_t *loop), + void (*pre_cb)(struct us_loop_t *loop), void (*post_cb)(struct us_loop_t *loop), unsigned int ext_size); + +/* Frees the loop immediately */ +void us_loop_free(struct us_loop_t *loop); + +/* Returns the loop user data extension */ +void *us_loop_ext(struct us_loop_t *loop); + +/* Blocks the calling thread and drives the event loop until no more non-fallthrough polls are scheduled */ +void us_loop_run(struct us_loop_t *loop); + + +/* Signals the loop from any thread to wake up and execute its wakeup handler from the loop's own running thread. + * This is the only fully thread-safe function and serves as the basis for thread safety */ +void us_wakeup_loop(struct us_loop_t *loop); + +/* Hook up timers in existing loop */ +void us_loop_integrate(struct us_loop_t *loop); + +/* Returns the loop iteration number */ +long long us_loop_iteration_number(struct us_loop_t *loop); + +/* Public interfaces for polls */ + +/* A fallthrough poll does not keep the loop running, it falls through */ +struct us_poll_t *us_create_poll(struct us_loop_t *loop, int fallthrough, unsigned int ext_size); + +/* After stopping a poll you must manually free the memory */ +void us_poll_free(struct us_poll_t *p, struct us_loop_t *loop); + +/* Associate this poll with a socket descriptor and poll type */ +void us_poll_init(struct us_poll_t *p, LIBUS_SOCKET_DESCRIPTOR fd, int poll_type); + +/* Start, change and stop polling for events */ +void us_poll_start(struct us_poll_t *p, struct us_loop_t *loop, int events); +void us_poll_change(struct us_poll_t *p, struct us_loop_t *loop, int events); +void us_poll_stop(struct us_poll_t *p, struct us_loop_t *loop); + +/* Return what events we are polling for */ +int us_poll_events(struct us_poll_t *p); + +/* Returns the user data extension of this poll */ +void *us_poll_ext(struct us_poll_t *p); + +/* Get associated socket descriptor from a poll */ +LIBUS_SOCKET_DESCRIPTOR us_poll_fd(struct us_poll_t *p); + +/* Resize an active poll */ +struct us_poll_t *us_poll_resize(struct us_poll_t *p, struct us_loop_t *loop, unsigned int ext_size); + +/* Public interfaces for sockets */ + +/* Returns the underlying native handle for a socket, such as SSL or file descriptor. + * In the case of file descriptor, the value of pointer is fd. */ +void *us_socket_get_native_handle(int ssl, struct us_socket_t *s); + +/* Write up to length bytes of data. Returns actual bytes written. + * Will call the on_writable callback of active socket context on failure to write everything off in one go. + * Set hint msg_more if you have more immediate data to write. */ +int us_socket_write(int ssl, struct us_socket_t *s, const char *data, int length, int msg_more); + +/* Set a low precision, high performance timer on a socket. A socket can only have one single active timer + * at any given point in time. Will remove any such pre set timer */ +void us_socket_timeout(int ssl, struct us_socket_t *s, unsigned int seconds); + +/* Set a low precision, high performance timer on a socket. Suitable for per-minute precision. */ +void us_socket_long_timeout(int ssl, struct us_socket_t *s, unsigned int minutes); + +/* Return the user data extension of this socket */ +void *us_socket_ext(int ssl, struct us_socket_t *s); + +/* Return the socket context of this socket */ +struct us_socket_context_t *us_socket_context(int ssl, struct us_socket_t *s); + +/* Withdraw any msg_more status and flush any pending data */ +void us_socket_flush(int ssl, struct us_socket_t *s); + +/* Shuts down the connection by sending FIN and/or close_notify */ +void us_socket_shutdown(int ssl, struct us_socket_t *s); + +/* Shuts down the connection in terms of read, meaning next event loop + * iteration will catch the socket being closed. Can be used to defer closing + * to next event loop iteration. */ +void us_socket_shutdown_read(int ssl, struct us_socket_t *s); + +/* Returns whether the socket has been shut down or not */ +int us_socket_is_shut_down(int ssl, struct us_socket_t *s); + +/* Returns whether this socket has been closed. Only valid if memory has not yet been released. */ +int us_socket_is_closed(int ssl, struct us_socket_t *s); + +/* Immediately closes the socket */ +struct us_socket_t *us_socket_close(int ssl, struct us_socket_t *s, int code, void *reason); + +/* Returns local port or -1 on failure. */ +int us_socket_local_port(int ssl, struct us_socket_t *s); + +/* Copy remote (IP) address of socket, or fail with zero length. */ +void us_socket_remote_address(int ssl, struct us_socket_t *s, char *buf, int *length); + +/* Bun extras */ +struct us_socket_t *us_socket_detach(int ssl, struct us_socket_t *s); +struct us_socket_t *us_socket_attach(int ssl, LIBUS_SOCKET_DESCRIPTOR client_fd, struct us_socket_context_t *ctx, int flags, int socket_ext_size); +struct us_socket_t *us_socket_wrap_with_tls(int ssl, struct us_socket_t *s, struct us_bun_socket_context_options_t options, struct us_socket_events_t events, int socket_ext_size); +int us_socket_raw_write(int ssl, struct us_socket_t *s, const char *data, int length, int msg_more); +struct us_socket_t* us_socket_open(int ssl, struct us_socket_t * s, int is_client, char* ip, int ip_length); +int us_raw_root_certs(struct us_cert_string_t**out); + +#ifdef __cplusplus +} +#endif + +/* Decide what eventing system to use by default */ +#if !defined(LIBUS_USE_EPOLL) && !defined(LIBUS_USE_LIBUV) && !defined(LIBUS_USE_GCD) && !defined(LIBUS_USE_KQUEUE) && !defined(LIBUS_USE_ASIO) +#if defined(_WIN32) +#define LIBUS_USE_LIBUV +#elif defined(__APPLE__) || defined(__FreeBSD__) +#define LIBUS_USE_KQUEUE +#else +#define LIBUS_USE_EPOLL +#endif +#endif + +#endif // LIBUSOCKETS_H diff --git a/packages/bun-usockets/src/loop.c b/packages/bun-usockets/src/loop.c new file mode 100644 index 000000000..9ad1e64bf --- /dev/null +++ b/packages/bun-usockets/src/loop.c @@ -0,0 +1,364 @@ +/* + * Authored by Alex Hultman, 2018-2021. + * Intellectual property of third-party. + + * 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 + + * http://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 "libusockets.h" +#include "internal/internal.h" +#include <stdlib.h> +#include <sys/ioctl.h> + +/* The loop has 2 fallthrough polls */ +void us_internal_loop_data_init(struct us_loop_t *loop, void (*wakeup_cb)(struct us_loop_t *loop), + void (*pre_cb)(struct us_loop_t *loop), void (*post_cb)(struct us_loop_t *loop)) { + loop->data.sweep_timer = us_create_timer(loop, 1, 0); + loop->data.recv_buf = malloc(LIBUS_RECV_BUFFER_LENGTH + LIBUS_RECV_BUFFER_PADDING * 2); + loop->data.ssl_data = 0; + loop->data.head = 0; + loop->data.iterator = 0; + loop->data.closed_head = 0; + loop->data.low_prio_head = 0; + loop->data.low_prio_budget = 0; + + loop->data.pre_cb = pre_cb; + loop->data.post_cb = post_cb; + loop->data.iteration_nr = 0; + + loop->data.wakeup_async = us_internal_create_async(loop, 1, 0); + us_internal_async_set(loop->data.wakeup_async, (void (*)(struct us_internal_async *)) wakeup_cb); +} + +void us_internal_loop_data_free(struct us_loop_t *loop) { +#ifndef LIBUS_NO_SSL + us_internal_free_loop_ssl_data(loop); +#endif + + free(loop->data.recv_buf); + + us_timer_close(loop->data.sweep_timer); + us_internal_async_close(loop->data.wakeup_async); +} + +void us_wakeup_loop(struct us_loop_t *loop) { + us_internal_async_wakeup(loop->data.wakeup_async); +} + +void us_internal_loop_link(struct us_loop_t *loop, struct us_socket_context_t *context) { + /* Insert this context as the head of loop */ + context->next = loop->data.head; + context->prev = 0; + if (loop->data.head) { + loop->data.head->prev = context; + } + loop->data.head = context; +} + +/* Unlink is called before free */ +void us_internal_loop_unlink(struct us_loop_t *loop, struct us_socket_context_t *context) { + if (loop->data.head == context) { + loop->data.head = context->next; + if (loop->data.head) { + loop->data.head->prev = 0; + } + } else { + context->prev->next = context->next; + if (context->next) { + context->next->prev = context->prev; + } + } +} + +/* This functions should never run recursively */ +void us_internal_timer_sweep(struct us_loop_t *loop) { + struct us_internal_loop_data_t *loop_data = &loop->data; + /* For all socket contexts in this loop */ + for (loop_data->iterator = loop_data->head; loop_data->iterator; loop_data->iterator = loop_data->iterator->next) { + + struct us_socket_context_t *context = loop_data->iterator; + + /* Update this context's timestamps (this could be moved to loop and done once) */ + context->global_tick++; + unsigned char short_ticks = context->timestamp = context->global_tick % 240; + unsigned char long_ticks = context->long_timestamp = (context->global_tick / 15) % 240; + + /* Begin at head */ + struct us_socket_t *s = context->head_sockets; + while (s) { + /* Seek until end or timeout found (tightest loop) */ + while (1) { + /* We only read from 1 random cache line here */ + if (short_ticks == s->timeout || long_ticks == s->long_timeout) { + break; + } + + /* Did we reach the end without a find? */ + if ((s = s->next) == 0) { + goto next_context; + } + } + + /* Here we have a timeout to emit (slow path) */ + context->iterator = s; + + if (short_ticks == s->timeout) { + s->timeout = 255; + context->on_socket_timeout(s); + } + + if (context->iterator == s && long_ticks == s->long_timeout) { + s->long_timeout = 255; + context->on_socket_long_timeout(s); + } + + /* Check for unlink / link (if the event handler did not modify the chain, we step 1) */ + if (s == context->iterator) { + s = s->next; + } else { + /* The iterator was changed by event handler */ + s = context->iterator; + } + } + /* We always store a 0 to context->iterator here since we are no longer iterating this context */ + next_context: + context->iterator = 0; + } +} + +/* We do not want to block the loop with tons and tons of CPU-intensive work for SSL handshakes. + * Spread it out during many loop iterations, prioritizing already open connections, they are far + * easier on CPU */ +static const int MAX_LOW_PRIO_SOCKETS_PER_LOOP_ITERATION = 5; + +void us_internal_handle_low_priority_sockets(struct us_loop_t *loop) { + struct us_internal_loop_data_t *loop_data = &loop->data; + struct us_socket_t *s; + + loop_data->low_prio_budget = MAX_LOW_PRIO_SOCKETS_PER_LOOP_ITERATION; + + for (s = loop_data->low_prio_head; s && loop_data->low_prio_budget > 0; s = loop_data->low_prio_head, loop_data->low_prio_budget--) { + /* Unlink this socket from the low-priority queue */ + loop_data->low_prio_head = s->next; + if (s->next) s->next->prev = 0; + s->next = 0; + + us_internal_socket_context_link_socket(s->context, s); + us_poll_change(&s->p, us_socket_context(0, s)->loop, us_poll_events(&s->p) | LIBUS_SOCKET_READABLE); + + s->low_prio_state = 2; + } +} + +/* Note: Properly takes the linked list and timeout sweep into account */ +void us_internal_free_closed_sockets(struct us_loop_t *loop) { + /* Free all closed sockets (maybe it is better to reverse order?) */ + if (loop->data.closed_head) { + for (struct us_socket_t *s = loop->data.closed_head; s; ) { + struct us_socket_t *next = s->next; + us_poll_free((struct us_poll_t *) s, loop); + s = next; + } + loop->data.closed_head = 0; + } +} + +void sweep_timer_cb(struct us_internal_callback_t *cb) { + us_internal_timer_sweep(cb->loop); +} + +long long us_loop_iteration_number(struct us_loop_t *loop) { + return loop->data.iteration_nr; +} + +/* These may have somewhat different meaning depending on the underlying event library */ +void us_internal_loop_pre(struct us_loop_t *loop) { + loop->data.iteration_nr++; + us_internal_handle_low_priority_sockets(loop); + loop->data.pre_cb(loop); +} + +void us_internal_loop_post(struct us_loop_t *loop) { + us_internal_free_closed_sockets(loop); + loop->data.post_cb(loop); +} + +void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int events) { + switch (us_internal_poll_type(p)) { + case POLL_TYPE_CALLBACK: { + struct us_internal_callback_t *cb = (struct us_internal_callback_t *) p; + /* Timers, asyncs should accept (read), while UDP sockets should obviously not */ + if (!cb->leave_poll_ready) { + /* Let's just have this macro to silence the CodeQL alert regarding empty function when using libuv */ + #ifndef LIBUS_USE_LIBUV + us_internal_accept_poll_event(p); + #endif + } + cb->cb(cb->cb_expects_the_loop ? (struct us_internal_callback_t *) cb->loop : (struct us_internal_callback_t *) &cb->p); + } + break; + case POLL_TYPE_SEMI_SOCKET: { + /* Both connect and listen sockets are semi-sockets + * but they poll for different events */ + if (us_poll_events(p) == LIBUS_SOCKET_WRITABLE) { + struct us_socket_t *s = (struct us_socket_t *) p; + + /* It is perfectly possible to come here with an error */ + if (error) { + /* Emit error, close without emitting on_close */ + s->context->on_connect_error(s, 0); + us_socket_close_connecting(0, s); + } else { + /* All sockets poll for readable */ + us_poll_change(p, s->context->loop, LIBUS_SOCKET_READABLE); + + /* We always use nodelay */ + bsd_socket_nodelay(us_poll_fd(p), 1); + + /* We are now a proper socket */ + us_internal_poll_set_type(p, POLL_TYPE_SOCKET); + + /* If we used a connection timeout we have to reset it here */ + us_socket_timeout(0, s, 0); + + s->context->on_open(s, 1, 0, 0); + } + } else { + struct us_listen_socket_t *listen_socket = (struct us_listen_socket_t *) p; + struct bsd_addr_t addr; + + LIBUS_SOCKET_DESCRIPTOR client_fd = bsd_accept_socket(us_poll_fd(p), &addr); + if (client_fd == LIBUS_SOCKET_ERROR) { + /* Todo: start timer here */ + + } else { + + /* Todo: stop timer if any */ + + do { + struct us_poll_t *accepted_p = us_create_poll(us_socket_context(0, &listen_socket->s)->loop, 0, sizeof(struct us_socket_t) - sizeof(struct us_poll_t) + listen_socket->socket_ext_size); + us_poll_init(accepted_p, client_fd, POLL_TYPE_SOCKET); + us_poll_start(accepted_p, listen_socket->s.context->loop, LIBUS_SOCKET_READABLE); + + struct us_socket_t *s = (struct us_socket_t *) accepted_p; + + s->context = listen_socket->s.context; + s->timeout = 255; + s->long_timeout = 255; + s->low_prio_state = 0; + + /* We always use nodelay */ + bsd_socket_nodelay(client_fd, 1); + + us_internal_socket_context_link_socket(listen_socket->s.context, s); + + listen_socket->s.context->on_open(s, 0, bsd_addr_get_ip(&addr), bsd_addr_get_ip_length(&addr)); + + /* Exit accept loop if listen socket was closed in on_open handler */ + if (us_socket_is_closed(0, &listen_socket->s)) { + break; + } + + } while ((client_fd = bsd_accept_socket(us_poll_fd(p), &addr)) != LIBUS_SOCKET_ERROR); + } + } + } + break; + case POLL_TYPE_SOCKET_SHUT_DOWN: + case POLL_TYPE_SOCKET: { + /* We should only use s, no p after this point */ + struct us_socket_t *s = (struct us_socket_t *) p; + + if (events & LIBUS_SOCKET_WRITABLE && !error) { + /* Note: if we failed a write as a socket of one loop then adopted + * to another loop, this will be wrong. Absurd case though */ + s->context->loop->data.last_write_failed = 0; + + s = s->context->on_writable(s); + + if (us_socket_is_closed(0, s)) { + return; + } + + /* If we have no failed write or if we shut down, then stop polling for more writable */ + if (!s->context->loop->data.last_write_failed || us_socket_is_shut_down(0, s)) { + us_poll_change(&s->p, us_socket_context(0, s)->loop, us_poll_events(&s->p) & LIBUS_SOCKET_READABLE); + } + } + + if (events & LIBUS_SOCKET_READABLE) { + /* Contexts may prioritize down sockets that are currently readable, e.g. when SSL handshake has to be done. + * SSL handshakes are CPU intensive, so we limit the number of handshakes per loop iteration, and move the rest + * to the low-priority queue */ + if (s->context->is_low_prio(s)) { + if (s->low_prio_state == 2) { + s->low_prio_state = 0; /* Socket has been delayed and now it's time to process incoming data for one iteration */ + } else if (s->context->loop->data.low_prio_budget > 0) { + s->context->loop->data.low_prio_budget--; /* Still having budget for this iteration - do normal processing */ + } else { + us_poll_change(&s->p, us_socket_context(0, s)->loop, us_poll_events(&s->p) & LIBUS_SOCKET_WRITABLE); + us_internal_socket_context_unlink_socket(s->context, s); + + /* Link this socket to the low-priority queue - we use a LIFO queue, to prioritize newer clients that are + * maybe not already timeouted - sounds unfair, but works better in real-life with smaller client-timeouts + * under high load */ + s->prev = 0; + s->next = s->context->loop->data.low_prio_head; + if (s->next) s->next->prev = s; + s->context->loop->data.low_prio_head = s; + + s->low_prio_state = 1; + + break; + } + } + + int length = bsd_recv(us_poll_fd(&s->p), s->context->loop->data.recv_buf + LIBUS_RECV_BUFFER_PADDING, LIBUS_RECV_BUFFER_LENGTH, 0); + if (length > 0) { + s = s->context->on_data(s, s->context->loop->data.recv_buf + LIBUS_RECV_BUFFER_PADDING, length); + } else if (!length) { + if (us_socket_is_shut_down(0, s)) { + /* We got FIN back after sending it */ + /* Todo: We should give "CLEAN SHUTDOWN" as reason here */ + s = us_socket_close(0, s, 0, NULL); + } else { + /* We got FIN, so stop polling for readable */ + us_poll_change(&s->p, us_socket_context(0, s)->loop, us_poll_events(&s->p) & LIBUS_SOCKET_WRITABLE); + s = s->context->on_end(s); + } + } else if (length == LIBUS_SOCKET_ERROR && !bsd_would_block()) { + /* Todo: decide also here what kind of reason we should give */ + s = us_socket_close(0, s, 0, NULL); + return; + } + } + + /* Such as epollerr epollhup */ + if (error) { + /* Todo: decide what code we give here */ + s = us_socket_close(0, s, 0, NULL); + return; + } + } + break; + } +} + +/* Integration only requires the timer to be set up */ +void us_loop_integrate(struct us_loop_t *loop) { + us_timer_set(loop->data.sweep_timer, (void (*)(struct us_timer_t *)) sweep_timer_cb, LIBUS_TIMEOUT_GRANULARITY * 1000, LIBUS_TIMEOUT_GRANULARITY * 1000); +} + +void *us_loop_ext(struct us_loop_t *loop) { + return loop + 1; +} diff --git a/packages/bun-usockets/src/quic.c b/packages/bun-usockets/src/quic.c new file mode 100644 index 000000000..529074632 --- /dev/null +++ b/packages/bun-usockets/src/quic.c @@ -0,0 +1,1071 @@ +#ifdef LIBUS_USE_QUIC + +/* Todo: quic layer should not use bsd layer directly (sendmmsg) */ +#include "internal/networking/bsd.h" + +#include "quic.h" + + + +#include "lsquic.h" +#include "lsquic_types.h" +#include "lsxpack_header.h" + +/* Todo: remove these */ +#ifndef _WIN32 +#include <netinet/in.h> +#include <errno.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +void leave_all(); + +/* +struct sockaddr_in client_addr = { + AF_INET, + 1, + 1 +}; + +struct sockaddr_in server_addr = { + AF_INET, + 2, + 2 +};*/ + + // used in process_quic + lsquic_engine_t *global_engine; + lsquic_engine_t *global_client_engine; + +/* Socket context */ +struct us_quic_socket_context_s { + + struct us_udp_packet_buffer_t *recv_buf; + //struct us_udp_packet_buffer_t *send_buf; + int outgoing_packets; + + //struct us_udp_socket_t *udp_socket; + struct us_loop_t *loop; + lsquic_engine_t *engine; + lsquic_engine_t *client_engine; + + // we store the options the context was created with here + us_quic_socket_context_options_t options; + + void(*on_stream_data)(us_quic_stream_t *s, char *data, int length); + void(*on_stream_end)(us_quic_stream_t *s); + void(*on_stream_headers)(us_quic_stream_t *s); + void(*on_stream_open)(us_quic_stream_t *s, int is_client); + void(*on_stream_close)(us_quic_stream_t *s); + void(*on_stream_writable)(us_quic_stream_t *s); + void(*on_open)(us_quic_socket_t *s, int is_client); + void(*on_close)(us_quic_socket_t *s); +}; + +/* Setters */ +void us_quic_socket_context_on_stream_data(us_quic_socket_context_t *context, void(*on_stream_data)(us_quic_stream_t *s, char *data, int length)) { + context->on_stream_data = on_stream_data; +} +void us_quic_socket_context_on_stream_end(us_quic_socket_context_t *context, void(*on_stream_end)(us_quic_stream_t *s)) { + context->on_stream_end = on_stream_end; +} +void us_quic_socket_context_on_stream_headers(us_quic_socket_context_t *context, void(*on_stream_headers)(us_quic_stream_t *s)) { + context->on_stream_headers = on_stream_headers; +} +void us_quic_socket_context_on_stream_open(us_quic_socket_context_t *context, void(*on_stream_open)(us_quic_stream_t *s, int is_client)) { + context->on_stream_open = on_stream_open; +} +void us_quic_socket_context_on_stream_close(us_quic_socket_context_t *context, void(*on_stream_close)(us_quic_stream_t *s)) { + context->on_stream_close = on_stream_close; +} +void us_quic_socket_context_on_open(us_quic_socket_context_t *context, void(*on_open)(us_quic_socket_t *s, int is_client)) { + context->on_open = on_open; +} +void us_quic_socket_context_on_close(us_quic_socket_context_t *context, void(*on_close)(us_quic_socket_t *s)) { + context->on_close = on_close; +} +void us_quic_socket_context_on_stream_writable(us_quic_socket_context_t *context, void(*on_stream_writable)(us_quic_stream_t *s)) { + context->on_stream_writable = on_stream_writable; +} + +/* UDP handlers */ +void on_udp_socket_writable(struct us_udp_socket_t *s) { + /* Need context from socket here */ + us_quic_socket_context_t *context = us_udp_socket_user(s); + + /* We just continue now */ + lsquic_engine_send_unsent_packets(context->engine); +} + +// we need two differetn handlers to know to put it in client or servcer context +void on_udp_socket_data_client(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int packets) { + + int fd = us_poll_fd((struct us_poll_t *) s); + //printf("Reading on fd: %d\n", fd); + + //printf("UDP (client) socket got data: %p\n", s); + + /* We need to lookup the context from the udp socket */ + //us_udpus_udp_socket_context(s); + // do we have udp socket contexts? or do we just have user data? + + us_quic_socket_context_t *context = us_udp_socket_user(s); + + /* We just shove it to lsquic */ + for (int i = 0; i < packets; i++) { + char *payload = us_udp_packet_buffer_payload(buf, i); + int length = us_udp_packet_buffer_payload_length(buf, i); + int ecn = us_udp_packet_buffer_ecn(buf, i); + void *peer_addr = us_udp_packet_buffer_peer(buf, i); + + //printf("Reading UDP of size %d\n", length); + + char ip[16]; + int ip_length = us_udp_packet_buffer_local_ip(buf, i, ip); + if (!ip_length) { + printf("We got no ip on received packet!\n"); + exit(0); + } + + //printf("Our received destination IP length is: %d\n", ip_length); + + int port = us_udp_socket_bound_port(s); + //printf("We received packet on port: %d\n", port); + + /* We build our address based on what the dest addr is */ + struct sockaddr_storage local_addr = {0}; + if (ip_length == 16) { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *) &local_addr; + + ipv6->sin6_family = AF_INET6; + ipv6->sin6_port = ntohs(port); + memcpy(ipv6->sin6_addr.s6_addr, ip, 16); + } else { + struct sockaddr_in *ipv4 = (struct sockaddr_in *) &local_addr; + + ipv4->sin_family = AF_INET; + ipv4->sin_port = ntohs(port); + memcpy(&ipv4->sin_addr.s_addr, ip, 4); + } + + + int ret = lsquic_engine_packet_in(context->client_engine, payload, length, (struct sockaddr *) &local_addr, peer_addr, (void *) s, 0); + //printf("Engine returned: %d\n", ret); + + + } + + lsquic_engine_process_conns(context->client_engine); + +} + +void on_udp_socket_data(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int packets) { + + + //printf("UDP socket got data: %p\n", s); + + /* We need to lookup the context from the udp socket */ + //us_udpus_udp_socket_context(s); + // do we have udp socket contexts? or do we just have user data? + + us_quic_socket_context_t *context = us_udp_socket_user(s); + + // process conns now? to accept new connections? + lsquic_engine_process_conns(context->engine); + + /* We just shove it to lsquic */ + for (int i = 0; i < packets; i++) { + char *payload = us_udp_packet_buffer_payload(buf, i); + int length = us_udp_packet_buffer_payload_length(buf, i); + int ecn = us_udp_packet_buffer_ecn(buf, i); + void *peer_addr = us_udp_packet_buffer_peer(buf, i); + + //printf("Reading UDP of size %d\n", length); + + char ip[16]; + int ip_length = us_udp_packet_buffer_local_ip(buf, i, ip); + if (!ip_length) { + printf("We got no ip on received packet!\n"); + exit(0); + } + + //printf("Our received destination IP length is: %d\n", ip_length); + + int port = us_udp_socket_bound_port(s); + //printf("We received packet on port: %d\n", port); + + /* We build our address based on what the dest addr is */ + struct sockaddr_storage local_addr = {0}; + if (ip_length == 16) { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *) &local_addr; + + ipv6->sin6_family = AF_INET6; + ipv6->sin6_port = ntohs(port); + memcpy(ipv6->sin6_addr.s6_addr, ip, 16); + } else { + + struct sockaddr_in *ipv4 = (struct sockaddr_in *) &local_addr; + + ipv4->sin_family = AF_INET; + ipv4->sin_port = ntohs(port); + memcpy(&ipv4->sin_addr.s_addr, ip, 4); + } + + + int ret = lsquic_engine_packet_in(context->engine, payload, length, (struct sockaddr *) &local_addr, peer_addr, (void *) s, 0); + //printf("Engine returned: %d\n", ret); + + + } + + lsquic_engine_process_conns(context->engine); + +} + +/* Let's use this on Windows and macOS where it is not defined (todo: put in bsd.h) */ +#ifndef UIO_MAXIOV +#define UIO_MAXIOV 1024 + +#ifndef _WIN32 +struct mmsghdr { + struct msghdr msg_hdr; /* Message header */ + unsigned int msg_len; /* Number of bytes transmitted */ +}; +#endif +#endif + +/* Server and client packet out is identical */ +int send_packets_out(void *ctx, const struct lsquic_out_spec *specs, unsigned n_specs) { +#ifndef _WIN32 + us_quic_socket_context_t *context = ctx; + + /* A run is at most UIO_MAXIOV datagrams long */ + struct mmsghdr hdrs[UIO_MAXIOV]; + int run_length = 0; + + /* We assume that thiss whole cb will never be called with 0 specs */ + struct us_udp_socket_t *last_socket = (struct us_udp_socket_t *) specs[0].peer_ctx; + + int sent = 0; + for (int i = 0; i < n_specs; i++) { + /* Send this run if we need to */ + if (run_length == UIO_MAXIOV || specs[i].peer_ctx != last_socket) { + int ret = bsd_sendmmsg(us_poll_fd((struct us_poll_t *) last_socket), hdrs, run_length, 0); + if (ret != run_length) { + if (ret == -1) { + printf("unhandled udp backpressure!\n"); + return sent; + } else { + printf("unhandled udp backpressure!\n"); + errno = EAGAIN; + return sent + ret; + } + } + sent += ret; + run_length = 0; + last_socket = specs[i].peer_ctx; + //printf("different socket breask run!\n"); + } + + /* Continue existing run or start a new one */ + //memset(&hdrs[i].msg_hdr, 0, sizeof(hdrs[i].msg_hdr)); + memset(&hdrs[run_length], 0, sizeof(hdrs[run_length])); + + hdrs[run_length].msg_hdr.msg_name = (void *) specs[i].dest_sa; + hdrs[run_length].msg_hdr.msg_namelen = (AF_INET == specs[i].dest_sa->sa_family ? + sizeof(struct sockaddr_in) : + sizeof(struct sockaddr_in6)), + hdrs[run_length].msg_hdr.msg_iov = specs[i].iov; + hdrs[run_length].msg_hdr.msg_iovlen = specs[i].iovlen; + hdrs[run_length].msg_hdr.msg_flags = 0; + + run_length++; + } + + /* Send last run */ + if (run_length) { + int ret = bsd_sendmmsg(us_poll_fd((struct us_poll_t *) last_socket), hdrs, run_length, 0); + if (ret == -1) { + printf("backpressure! A\n"); + return sent; + } + if (sent + ret != n_specs) { + printf("backpressure! B\n"); + printf("errno is: %d\n", errno); + errno = EAGAIN; + } + //printf("Returning %d of %d\n", sent + ret, n_specs); + return sent + ret; + } + + //printf("Returning %d\n", n_specs); + +#endif + + return n_specs; +} + +lsquic_conn_ctx_t *on_new_conn(void *stream_if_ctx, lsquic_conn_t *c) { + us_quic_socket_context_t *context = stream_if_ctx; + + printf("Context is: %p\n", context); + + /* We need to create some kind of socket here */ + + int is_client = 0; + if (lsquic_conn_get_engine(c) == context->client_engine) { + is_client = 1; + } + + context->on_open((us_quic_socket_t *) c, is_client); + + return (lsquic_conn_ctx_t *) context; +} + +void us_quic_socket_create_stream(us_quic_socket_t *s, int ext_size) { + lsquic_conn_make_stream((lsquic_conn_t *) s); + + // here we need to allocate and attach the user data + +} + +void on_conn_closed(lsquic_conn_t *c) { + us_quic_socket_context_t *context = (us_quic_socket_context_t *) lsquic_conn_get_ctx(c); + + printf("on_conn_closed!\n"); + + context->on_close((us_quic_socket_t *) c); +} + +lsquic_stream_ctx_t *on_new_stream(void *stream_if_ctx, lsquic_stream_t *s) { + + /* In true usockets style we always want read */ + lsquic_stream_wantread(s, 1); + + us_quic_socket_context_t *context = stream_if_ctx; + + // the conn's ctx should point at the udp socket and the socket context + // the ext size of streams and conn's are set by the listen/connect calls, which + // are the calls that create the UDP socket so we need conn to point to the UDP socket + // to get that ext_size set in listen/connect calls, back here. + // todo: hardcoded for now + + int ext_size = 256; + + void *ext = malloc(ext_size); + // yes hello + strcpy(ext, "Hello I am ext!"); + + int is_client = 0; + if (lsquic_conn_get_engine(lsquic_stream_conn(s)) == context->client_engine) { + is_client = 1; + } + + // luckily we can set the ext before we return + lsquic_stream_set_ctx(s, ext); + context->on_stream_open((us_quic_stream_t *) s, is_client); + + return ext; +} + +//#define V(v) (v), strlen(v) + +// header bug is really just an offset buffer - perfect for per context! +// could even use cork buffer or similar +struct header_buf +{ + unsigned off; + char buf[UINT16_MAX]; +}; + +int +header_set_ptr (struct lsxpack_header *hdr, struct header_buf *header_buf, + const char *name, size_t name_len, + const char *val, size_t val_len) +{ + if (header_buf->off + name_len + val_len <= sizeof(header_buf->buf)) + { + memcpy(header_buf->buf + header_buf->off, name, name_len); + memcpy(header_buf->buf + header_buf->off + name_len, val, val_len); + lsxpack_header_set_offset2(hdr, header_buf->buf + header_buf->off, + 0, name_len, name_len, val_len); + header_buf->off += name_len + val_len; + return 0; + } + else + return -1; +} + +/* Static storage should be per context or really per loop */ +struct header_buf hbuf; +struct lsxpack_header headers_arr[10]; + +void us_quic_socket_context_set_header(us_quic_socket_context_t *context, int index, const char *key, int key_length, const char *value, int value_length) { + if (header_set_ptr(&headers_arr[index], &hbuf, key, key_length, value, value_length) != 0) { + printf("CANNOT FORMAT HEADER!\n"); + exit(0); + } +} + +void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_quic_stream_t *s, int num, int has_body) { + + lsquic_http_headers_t headers = { + .count = num, + .headers = headers_arr, + }; + // last here is whether this is eof or not (has body) + if (lsquic_stream_send_headers((lsquic_stream_t *) s, &headers, has_body ? 0 : 1)) {// pass 0 if data + printf("CANNOT SEND HEADERS!\n"); + exit(0); + } + + /* Reset header offset */ + hbuf.off = 0; +} + +int us_quic_stream_is_client(us_quic_stream_t *s) { + us_quic_socket_context_t *context = (us_quic_socket_context_t *) lsquic_conn_get_ctx(lsquic_stream_conn((lsquic_stream_t *) s)); + + int is_client = 0; + if (lsquic_conn_get_engine(lsquic_stream_conn((lsquic_stream_t *) s)) == context->client_engine) { + is_client = 1; + } + return is_client; +} + +us_quic_socket_t *us_quic_stream_socket(us_quic_stream_t *s) { + return (us_quic_socket_t *) lsquic_stream_conn((lsquic_stream_t *) s); +} + +//#include <errno.h> + + +// only for servers? +static void on_read(lsquic_stream_t *s, lsquic_stream_ctx_t *h) { + + /* The user data of the connection owning the stream, points to the socket context */ + us_quic_socket_context_t *context = (us_quic_socket_context_t *) lsquic_conn_get_ctx(lsquic_stream_conn(s)); + + /* This object is (and must be) fetched from a stream by + * calling lsquic_stream_get_hset() before the stream can be read. */ + /* This call must precede calls to lsquic_stream_read(), lsquic_stream_readv(), and lsquic_stream_readf(). */ + void *header_set = lsquic_stream_get_hset(s); + if (header_set) { + context->on_stream_headers((us_quic_stream_t *) s); + // header management is obviously broken and needs to be per-stream + leave_all(); + } + + // all of this logic should be moved to uws and WE here should only hand over the data + + char temp[4096] = {0}; + int nr = lsquic_stream_read(s, temp, 4096); + + // emit on_end when we receive fin, regardless of whether we emitted data yet + if (nr == 0) { + // any time we read EOF we stop reading + lsquic_stream_wantread(s, 0); + context->on_stream_end((us_quic_stream_t *) s); + } else if (nr == -1) { + if (errno != EWOULDBLOCK) { + // error handling should not be needed if we use lsquic correctly + printf("UNHANDLED ON_READ ERROR\n"); + exit(0); + } + // if we for some reason could not read even though we were told to read, we just ignore it + // this should not really happen but whatever + } else { + // otherwise if we have data, then emit it + context->on_stream_data((us_quic_stream_t *) s, temp, nr); + } + + // that's it + return; + + //lsquic_stream_readf + + printf("read returned: %d\n", nr); + + // we will get 9, ebadf if we read from a closed stream + if (nr == -1) { + printf("Error in reading! errno is: %d\n", errno); + if (errno != EWOULDBLOCK) { + printf("Errno is not EWOULDBLOCK\n"); + } else { + printf("Errno is would block, fine!\n"); + } + exit(0); + return; + } + + /* We have reached EOF */ + if (nr == 0) { + + /* Are we polling for writable (todo: make this check faster)? */ + if (lsquic_stream_wantwrite(s, 1)) { + + // we happened to be polling for writable so leave the connection open until on_write eventually closes it + printf("we are polling for write, so leaving the stream open!\n"); + + // stop reading though! + lsquic_stream_wantread(s, 0); // I hope this is fine? half open? + + } else { + // we weren't polling for writable so reset it to old value + lsquic_stream_wantwrite(s, 0); + + // I guess we can close it since we have called shutdown before this so data should flow out + lsquic_stream_close(s); + } + + // reached the EOF + //lsquic_stream_close(s); + //lsquic_stream_wantread(s, 0); + return; + } + + //printf("read: %d\n", nr); + + //printf("%s\n", temp); + + // why do we get tons of zero reads? + // maybe it doesn't matter, if we can parse this input then we are fine + //lsquic_stream_wantread(s, 0); + //lsquic_stream_wantwrite(s, 1); + + printf("on_stream_data: %d\n", nr); + context->on_stream_data((us_quic_stream_t *) s, temp, nr); +} + +int us_quic_stream_write(us_quic_stream_t *s, char *data, int length) { + lsquic_stream_t *stream = (lsquic_stream_t *) s; + int ret = lsquic_stream_write((lsquic_stream_t *) s, data, length); + // just like otherwise, we automatically poll for writable when failed + if (ret != length) { + lsquic_stream_wantwrite((lsquic_stream_t *) s, 1); + } else { + lsquic_stream_wantwrite((lsquic_stream_t *) s, 0); + } + return ret; +} + +static void on_write (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { + + us_quic_socket_context_t *context = (us_quic_socket_context_t *) lsquic_conn_get_ctx(lsquic_stream_conn(s)); + + context->on_stream_writable((us_quic_stream_t *) s); + + // here we might want to check if the user did write to failure or not, and if the user did not write, stop polling for writable + // i think that is what we do for http1 +} + +static void on_stream_close (lsquic_stream_t *s, lsquic_stream_ctx_t *h) { + //printf("STREAM CLOSED!\n"); +} + +#include "openssl/ssl.h" + +static char s_alpn[0x100]; + +int add_alpn (const char *alpn) +{ + size_t alpn_len, all_len; + + alpn_len = strlen(alpn); + if (alpn_len > 255) + return -1; + + all_len = strlen(s_alpn); + if (all_len + 1 + alpn_len + 1 > sizeof(s_alpn)) + return -1; + + s_alpn[all_len] = alpn_len; + memcpy(&s_alpn[all_len + 1], alpn, alpn_len); + s_alpn[all_len + 1 + alpn_len] = '\0'; + return 0; +} + +static int select_alpn(SSL *ssl, const unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, void *arg) { + int r; + + printf("select_alpn\n"); + + r = SSL_select_next_proto((unsigned char **) out, outlen, in, inlen, + (unsigned char *) s_alpn, strlen(s_alpn)); + if (r == OPENSSL_NPN_NEGOTIATED) { + printf("OPENSSL_NPN_NEGOTIATED\n"); + return SSL_TLSEXT_ERR_OK; + } + else + { + printf("no supported protocol can be selected!\n"); + //LSQ_WARN("no supported protocol can be selected from %.*s", + //(int) inlen, (char *) in); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } +} + +SSL_CTX *old_ctx; + +int server_name_cb(SSL *s, int *al, void *arg) { + printf("yolo SNI server_name_cb\n"); + + SSL_set_SSL_CTX(s, old_ctx); + + printf("existing name is: %s\n", SSL_get_servername(s, TLSEXT_NAMETYPE_host_name)); + + if (!SSL_get_servername(s, TLSEXT_NAMETYPE_host_name)) { + SSL_set_tlsext_host_name(s, "YOLO NAME!"); + printf("set name is: %s\n", SSL_get_servername(s, TLSEXT_NAMETYPE_host_name)); + } + + + return SSL_TLSEXT_ERR_OK; +} + +// this one is required for servers +struct ssl_ctx_st *get_ssl_ctx(void *peer_ctx, const struct sockaddr *local) { + printf("getting ssl ctx now, peer_ctx: %p\n", peer_ctx); + + // peer_ctx point to the us_udp_socket_t that passed the UDP packet in via + // lsquic_engine_packet_in (it got passed as peer_ctx) + // we want the per-context ssl cert from this udp socket + struct us_udp_socket_t *udp_socket = (struct us_udp_socket_t *) peer_ctx; + + // the udp socket of a server points to the context + struct us_quic_socket_context_s *context = us_udp_socket_user(udp_socket); + + if (old_ctx) { + return old_ctx; + } + + // peer_ctx should be the options struct! + us_quic_socket_context_options_t *options = &context->options; + + + SSL_CTX *ctx = SSL_CTX_new(TLS_method()); + + old_ctx = ctx; + + SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION); + SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION); + + //SSL_CTX_set_default_verify_paths(ctx); + + // probably cannot use this when http is in use? + // alpn is needed + SSL_CTX_set_alpn_select_cb(ctx, select_alpn, NULL); + + // sni is needed + SSL_CTX_set_tlsext_servername_callback(ctx, server_name_cb); + //long SSL_CTX_set_tlsext_servername_arg(SSL_CTX *ctx, void *arg); + + printf("Key: %s\n", options->key_file_name); + printf("Cert: %s\n", options->cert_file_name); + + int a = SSL_CTX_use_certificate_chain_file(ctx, options->cert_file_name); + int b = SSL_CTX_use_PrivateKey_file(ctx, options->key_file_name, SSL_FILETYPE_PEM); + + printf("loaded cert and key? %d, %d\n", a, b); + + return ctx; +} + +SSL_CTX *sni_lookup(void *lsquic_cert_lookup_ctx, const struct sockaddr *local, const char *sni) { + printf("simply returning old ctx in sni\n"); + return old_ctx; +} + +int log_buf_cb(void *logger_ctx, const char *buf, size_t len) { + printf("%.*s\n", (int) len, buf); + return 0; +} + +int us_quic_stream_shutdown_read(us_quic_stream_t *s) { + lsquic_stream_t *stream = (lsquic_stream_t *) s; + + int ret = lsquic_stream_shutdown((lsquic_stream_t *) s, 0); + if (ret != 0) { + printf("cannot shutdown stream!\n"); + exit(0); + } + + return 0; +} + +void *us_quic_stream_ext(us_quic_stream_t *s) { + return lsquic_stream_get_ctx((lsquic_stream_t *) s); +} + +void us_quic_stream_close(us_quic_stream_t *s) { + lsquic_stream_t *stream = (lsquic_stream_t *) s; + + int ret = lsquic_stream_close((lsquic_stream_t *) s); + if (ret != 0) { + printf("cannot close stream!\n"); + exit(0); + } + + return; +} + +int us_quic_stream_shutdown(us_quic_stream_t *s) { + lsquic_stream_t *stream = (lsquic_stream_t *) s; + + int ret = lsquic_stream_shutdown((lsquic_stream_t *) s, 1); + if (ret != 0) { + printf("cannot shutdown stream!\n"); + exit(0); + } + + return 0; +} + +// header of header set +struct header_set_hd { + int offset; +}; + +// let's just store last header set here +struct header_set_hd *last_hset; + +// just a shitty marker for now +struct processed_header { + void *name, *value; + int name_length, value_length; +}; + +int us_quic_socket_context_get_header(us_quic_socket_context_t *context, int index, char **name, int *name_length, char **value, int *value_length) { + + if (index < last_hset->offset) { + + struct processed_header *pd = (struct processed_header *) (last_hset + 1); + + pd = pd + index; + + *name = pd->name; + *value = pd->value; + *value_length = pd->value_length; + *name_length = pd->name_length; + + return 1; + } + + return 0; + +} + +char pool[1000][4096]; +int pool_top = 0; + +void *take() { + if (pool_top == 1000) { + printf("out of memory\n"); + exit(0); + } + return pool[pool_top++]; +} + +void leave_all() { + pool_top = 0; +} + + +// header set callbacks +void *hsi_create_header_set(void *hsi_ctx, lsquic_stream_t *stream, int is_push_promise) { + + //printf("hsi_create_header_set\n"); + + void *hset = take();//malloc(1024); + memset(hset, 0, sizeof(struct header_set_hd)); + + // hsi_ctx is set in engine creation below + + // I guess we just return whatever here, what we return here is gettable via the stream + + // gettable via lsquic_stream_get_hset + + // return user defined header set + + return hset; +} + +void hsi_discard_header_set(void *hdr_set) { + // this is pretty much the destructor of above constructor + + printf("hsi_discard_header!\n"); +} + +// one header set allocates one 8kb buffer from a linked list of available buffers + + +// 8kb of preallocated heap for headers +char header_decode_heap[1024 * 8]; +int header_decode_heap_offset = 0; + +struct lsxpack_header *hsi_prepare_decode(void *hdr_set, struct lsxpack_header *hdr, size_t space) { + + //printf("hsi_prepare_decode\n"); + + if (!hdr) { + char *mem = take(); + hdr = (struct lsxpack_header *) mem;//malloc(sizeof(struct lsxpack_header)); + memset(hdr, 0, sizeof(struct lsxpack_header)); + hdr->buf = mem + sizeof(struct lsxpack_header);//take();//malloc(space); + lsxpack_header_prepare_decode(hdr, hdr->buf, 0, space); + } else { + + if (space > 4096 - sizeof(struct lsxpack_header)) { + printf("not hanlded!\n"); + exit(0); + } + + hdr->val_len = space; + //hdr->buf = realloc(hdr->buf, space); + } + + return hdr; +} + +int hsi_process_header(void *hdr_set, struct lsxpack_header *hdr) { + + // I guess this is the emitting of the header to app space + + //printf("hsi_process_header: %p\n", hdr); + + struct header_set_hd *hd = hdr_set; + struct processed_header *proc_hdr = (struct processed_header *) (hd + 1); + + if (!hdr) { + //printf("end of headers!\n"); + + last_hset = hd; + + // mark end, well we can also just read the offset! + //memset(&proc_hdr[hd->offset], 0, sizeof(struct processed_header)); + + return 0; + } + + /*if (hdr->hpack_index) { + printf("header has hpack index: %d\n", hdr->hpack_index); + } + + if (hdr->qpack_index) { + printf("header has qpack index: %d\n", hdr->qpack_index); + }*/ + + proc_hdr[hd->offset].value = &hdr->buf[hdr->val_offset]; + proc_hdr[hd->offset].name = &hdr->buf[hdr->name_offset]; + proc_hdr[hd->offset].value_length = hdr->val_len; + proc_hdr[hd->offset].name_length = hdr->name_len; + + //printf("header %.*s = %.*s\n", hdr->name_len, &hdr->buf[hdr->name_offset], hdr->val_len, &hdr->buf[hdr->val_offset]); + + hd->offset++; + + return 0; +} + +//extern us_quic_socket_context_t *context; + +void timer_cb(struct us_timer_t *t) { + //printf("Processing conns from timer\n"); + lsquic_engine_process_conns(global_engine); + lsquic_engine_process_conns(global_client_engine); + + // these are handled by this timer, should be polling for udp writable + lsquic_engine_send_unsent_packets(global_engine); + lsquic_engine_send_unsent_packets(global_client_engine); +} + +// lsquic_conn +us_quic_socket_context_t *us_quic_socket_context(us_quic_socket_t *s) { + return (us_quic_socket_context_t *) lsquic_conn_get_ctx((lsquic_conn_t *) s); +} + +void *us_quic_socket_context_ext(us_quic_socket_context_t *context) { + return context + 1; +} + +// this will be for both client and server, but will be only for either h3 or raw quic +us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_socket_context_options_t options, int ext_size) { + + + printf("Creating socket context with ssl: %s\n", options.key_file_name); + + // every _listen_ call creates a new udp socket that feeds inputs to the engine in the context + // every context has its own send buffer and udp send socket (not bound to any port or ip?) + + // or just make it so that once you listen, it will listen on that port for input, and the context will use + // the first udp socket for output as it doesn't matter which one is used + + /* Holds all callbacks */ + us_quic_socket_context_t *context = malloc(sizeof(struct us_quic_socket_context_s) + ext_size); + + // the option is put on the socket context + context->options = options; + + context->loop = loop; + //context->udp_socket = 0; + + /* Allocate per thread, UDP packet buffers */ + context->recv_buf = us_create_udp_packet_buffer(); + //context->send_buf = us_create_udp_packet_buffer(); + + /* Init lsquic engine */ + if (0 != lsquic_global_init(LSQUIC_GLOBAL_CLIENT|LSQUIC_GLOBAL_SERVER)) { + exit(EXIT_FAILURE); + } + + static struct lsquic_stream_if stream_callbacks = { + .on_close = on_stream_close, + .on_conn_closed = on_conn_closed, + .on_write = on_write, + .on_read = on_read, + .on_new_stream = on_new_stream, + .on_new_conn = on_new_conn + }; + + //memset(&stream_callbacks, 13, sizeof(struct lsquic_stream_if)); + + static struct lsquic_hset_if hset_if = { + .hsi_discard_header_set = hsi_discard_header_set, + .hsi_create_header_set = hsi_create_header_set, + .hsi_prepare_decode = hsi_prepare_decode, + .hsi_process_header = hsi_process_header + }; + + + add_alpn("h3"); + + struct lsquic_engine_api engine_api = { + .ea_packets_out = send_packets_out, + .ea_packets_out_ctx = (void *) context, /* For example */ + .ea_stream_if = &stream_callbacks, + .ea_stream_if_ctx = context, + + .ea_get_ssl_ctx = get_ssl_ctx, + + // lookup certificate + .ea_lookup_cert = sni_lookup, + .ea_cert_lu_ctx = 0, + + // these are zero anyways + .ea_hsi_ctx = 0, + .ea_hsi_if = &hset_if, + }; + + ///printf("log: %d\n", lsquic_set_log_level("debug")); + + static struct lsquic_logger_if logger = { + .log_buf = log_buf_cb, + }; + + + + //lsquic_logger_init(&logger, 0, LLTS_NONE); + + /* Create an engine in server mode with HTTP behavior: */ + context->engine = lsquic_engine_new(LSENG_SERVER | LSENG_HTTP, &engine_api); + + struct lsquic_engine_api engine_api_client = { + .ea_packets_out = send_packets_out, + .ea_packets_out_ctx = (void *) context, /* For example */ + .ea_stream_if = &stream_callbacks, + .ea_stream_if_ctx = context, + + //.ea_get_ssl_ctx = get_ssl_ctx, // for client? + + // lookup certificate + //.ea_lookup_cert = sni_lookup, // for client? + //.ea_cert_lu_ctx = 13, // for client? + + // these are zero anyways + .ea_hsi_ctx = 0, + .ea_hsi_if = &hset_if, + }; + + context->client_engine = lsquic_engine_new(LSENG_HTTP, &engine_api_client); + + printf("Engine: %p\n", context->engine); + printf("Client Engine: %p\n", context->client_engine); + + // start a timer to handle connections + struct us_timer_t *delayTimer = us_create_timer(loop, 0, 0); + us_timer_set(delayTimer, timer_cb, 50, 50); + + // used by process_quic + global_engine = context->engine; + global_client_engine = context->client_engine; + + return context; +} + +us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t *context, const char *host, int port, int ext_size) { + /* We literally do create a listen socket */ + return (us_quic_listen_socket_t *) us_create_udp_socket(context->loop, /*context->recv_buf*/ NULL, on_udp_socket_data, on_udp_socket_writable, host, port, context); + //return NULL; +} + +/* A client connection is its own UDP socket, while a server connection makes use of the shared listen UDP socket */ +us_quic_socket_t *us_quic_socket_context_connect(us_quic_socket_context_t *context, const char *host, int port, int ext_size) { + printf("Connecting..\n"); + + + // localhost 9004 ipv4 + struct sockaddr_storage storage = {0}; + // struct sockaddr_in *addr = (struct sockaddr_in *) &storage; + // addr->sin_addr.s_addr = 16777343; + // addr->sin_port = htons(9004); + // addr->sin_family = AF_INET; + + struct sockaddr_in6 *addr = (struct sockaddr_in6 *) &storage; + addr->sin6_addr.s6_addr[15] = 1; + addr->sin6_port = htons(9004); + addr->sin6_family = AF_INET6; + + // Create the UDP socket binding to ephemeral port + struct us_udp_socket_t *udp_socket = us_create_udp_socket(context->loop, /*context->recv_buf*/ NULL, on_udp_socket_data_client, on_udp_socket_writable, 0, 0, context); + + // Determine what port we got, creating the local sockaddr + int ephemeral = us_udp_socket_bound_port(udp_socket); + + printf("Connecting with udp socket bound to port: %d\n", ephemeral); + + printf("Client udp socket is: %p\n", udp_socket); + + + // let's call ourselves an ipv6 client and see if that solves anything + struct sockaddr_storage local_storage = {0}; + // struct sockaddr_in *local_addr = (struct sockaddr_in *) &local_storage; + // local_addr->sin_addr.s_addr = 16777343; + // local_addr->sin_port = htons(ephemeral); + // local_addr->sin_family = AF_INET; + + struct sockaddr_in6 *local_addr = (struct sockaddr_in6 *) &local_storage; + local_addr->sin6_addr.s6_addr[15] = 1; + local_addr->sin6_port = htons(ephemeral); + local_addr->sin6_family = AF_INET6; + + // Refer to the UDP socket, and from that, get the context? + + // Create an UDP socket with host-picked port, or well, any port for now + + // we need 1 socket for servers, then we bind multiple ports to that one socket + + void *client = lsquic_engine_connect(context->client_engine, LSQVER_I001, (struct sockaddr *) local_addr, (struct sockaddr *) addr, udp_socket, (lsquic_conn_ctx_t *) udp_socket, "sni", 0, 0, 0, 0, 0); + + printf("Client: %p\n", client); + + // this is requiored to even have packetgs sending out (run this in post) + lsquic_engine_process_conns(context->client_engine); + + return client; +} + +#endif diff --git a/packages/bun-usockets/src/quic.h b/packages/bun-usockets/src/quic.h new file mode 100644 index 000000000..6d33d27b6 --- /dev/null +++ b/packages/bun-usockets/src/quic.h @@ -0,0 +1,68 @@ +#ifdef LIBUS_USE_QUIC + +#ifndef LIBUS_QUIC_H +#define LIBUS_QUIC_H + +/* Experimental QUIC functions */ + +#include "libusockets.h" + +typedef struct { + const char *cert_file_name; + const char *key_file_name; + const char *passphrase; +} us_quic_socket_context_options_t; + + +typedef struct { + /* Refers to either the shared listen socket or the client UDP socket */ + void *udp_socket; +} us_quic_socket_t; + +struct us_quic_socket_context_s; +struct us_quic_listen_socket_s; +struct us_quic_stream_s; + +typedef struct us_quic_socket_context_s us_quic_socket_context_t; +typedef struct us_quic_listen_socket_s us_quic_listen_socket_t; +typedef struct us_quic_stream_s us_quic_stream_t; + + +void *us_quic_stream_ext(us_quic_stream_t *s); +int us_quic_stream_write(us_quic_stream_t *s, char *data, int length); +int us_quic_stream_shutdown(us_quic_stream_t *s); +int us_quic_stream_shutdown_read(us_quic_stream_t *s); +void us_quic_stream_close(us_quic_stream_t *s); + +int us_quic_socket_context_get_header(us_quic_socket_context_t *context, int index, char **name, int *name_length, char **value, int *value_length); + + +void us_quic_socket_context_set_header(us_quic_socket_context_t *context, int index, const char *key, int key_length, const char *value, int value_length); +void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_quic_stream_t *s, int num, int has_body); + +us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_socket_context_options_t options, int ext_size); +us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t *context, const char *host, int port, int ext_size); +us_quic_socket_t *us_quic_socket_context_connect(us_quic_socket_context_t *context, const char *host, int port, int ext_size); + +void us_quic_socket_create_stream(us_quic_socket_t *s, int ext_size); +us_quic_socket_t *us_quic_stream_socket(us_quic_stream_t *s); + +/* This one is ugly and is only used to make clean examples */ +int us_quic_stream_is_client(us_quic_stream_t *s); + +void us_quic_socket_context_on_stream_data(us_quic_socket_context_t *context, void(*on_stream_data)(us_quic_stream_t *s, char *data, int length)); +void us_quic_socket_context_on_stream_end(us_quic_socket_context_t *context, void(*on_stream_data)(us_quic_stream_t *s)); +void us_quic_socket_context_on_stream_headers(us_quic_socket_context_t *context, void(*on_stream_headers)(us_quic_stream_t *s)); +void us_quic_socket_context_on_stream_open(us_quic_socket_context_t *context, void(*on_stream_open)(us_quic_stream_t *s, int is_client)); +void us_quic_socket_context_on_stream_close(us_quic_socket_context_t *context, void(*on_stream_close)(us_quic_stream_t *s)); +void us_quic_socket_context_on_open(us_quic_socket_context_t *context, void(*on_open)(us_quic_socket_t *s, int is_client)); +void us_quic_socket_context_on_close(us_quic_socket_context_t *context, void(*on_close)(us_quic_socket_t *s)); +void us_quic_socket_context_on_stream_writable(us_quic_socket_context_t *context, void(*on_stream_writable)(us_quic_stream_t *s)); + + + +void *us_quic_socket_context_ext(us_quic_socket_context_t *context); +us_quic_socket_context_t *us_quic_socket_context(us_quic_socket_t *s); + +#endif +#endif
\ No newline at end of file diff --git a/packages/bun-usockets/src/socket.c b/packages/bun-usockets/src/socket.c new file mode 100644 index 000000000..61cbb2c0f --- /dev/null +++ b/packages/bun-usockets/src/socket.c @@ -0,0 +1,295 @@ +/* + * Authored by Alex Hultman, 2018-2021. + * Intellectual property of third-party. + + * 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 + + * http://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 "libusockets.h" +#include "internal/internal.h" +#include <stdlib.h> +#include <string.h> +#include <stdint.h> + +/* Shared with SSL */ + +int us_socket_local_port(int ssl, struct us_socket_t *s) { + struct bsd_addr_t addr; + if (bsd_local_addr(us_poll_fd(&s->p), &addr)) { + return -1; + } else { + return bsd_addr_get_port(&addr); + } +} + +void us_socket_shutdown_read(int ssl, struct us_socket_t *s) { + /* This syscall is idempotent so no extra check is needed */ + bsd_shutdown_socket_read(us_poll_fd((struct us_poll_t *) s)); +} + +void us_socket_remote_address(int ssl, struct us_socket_t *s, char *buf, int *length) { + struct bsd_addr_t addr; + if (bsd_remote_addr(us_poll_fd(&s->p), &addr) || *length < bsd_addr_get_ip_length(&addr)) { + *length = 0; + } else { + *length = bsd_addr_get_ip_length(&addr); + memcpy(buf, bsd_addr_get_ip(&addr), *length); + } +} + +struct us_socket_context_t *us_socket_context(int ssl, struct us_socket_t *s) { + return s->context; +} + +void us_socket_timeout(int ssl, struct us_socket_t *s, unsigned int seconds) { + if (seconds) { + s->timeout = ((unsigned int)s->context->timestamp + ((seconds + 3) >> 2)) % 240; + } else { + s->timeout = 255; + } +} + +void us_socket_long_timeout(int ssl, struct us_socket_t *s, unsigned int minutes) { + if (minutes) { + s->long_timeout = ((unsigned int)s->context->long_timestamp + minutes) % 240; + } else { + s->long_timeout = 255; + } +} + +void us_socket_flush(int ssl, struct us_socket_t *s) { + if (!us_socket_is_shut_down(0, s)) { + bsd_socket_flush(us_poll_fd((struct us_poll_t *) s)); + } +} + +int us_socket_is_closed(int ssl, struct us_socket_t *s) { + return s->prev == (struct us_socket_t *) s->context; +} + +int us_socket_is_established(int ssl, struct us_socket_t *s) { + /* Everything that is not POLL_TYPE_SEMI_SOCKET is established */ + return us_internal_poll_type((struct us_poll_t *) s) != POLL_TYPE_SEMI_SOCKET; +} + +/* Exactly the same as us_socket_close but does not emit on_close event */ +struct us_socket_t *us_socket_close_connecting(int ssl, struct us_socket_t *s) { + if (!us_socket_is_closed(0, s)) { + us_internal_socket_context_unlink_socket(s->context, s); + us_poll_stop((struct us_poll_t *) s, s->context->loop); + bsd_close_socket(us_poll_fd((struct us_poll_t *) s)); + + /* Link this socket to the close-list and let it be deleted after this iteration */ + s->next = s->context->loop->data.closed_head; + s->context->loop->data.closed_head = s; + + /* Any socket with prev = context is marked as closed */ + s->prev = (struct us_socket_t *) s->context; + + //return s->context->on_close(s, code, reason); + } + return s; +} + +/* Same as above but emits on_close */ +struct us_socket_t *us_socket_close(int ssl, struct us_socket_t *s, int code, void *reason) { + if (!us_socket_is_closed(0, s)) { + if (s->low_prio_state == 1) { + /* Unlink this socket from the low-priority queue */ + if (!s->prev) s->context->loop->data.low_prio_head = s->next; + else s->prev->next = s->next; + + if (s->next) s->next->prev = s->prev; + + s->prev = 0; + s->next = 0; + s->low_prio_state = 0; + } else { + us_internal_socket_context_unlink_socket(s->context, s); + } + #ifdef LIBUS_USE_KQUEUE + // kqueue automatically removes the fd from the set on close + // we can skip the system call for that case + us_internal_loop_update_pending_ready_polls(s->context->loop, (struct us_poll_t *)s, 0, us_poll_events((struct us_poll_t*)s), 0); + #else + /* Disable any instance of us in the pending ready poll list */ + us_poll_stop((struct us_poll_t *) s, s->context->loop); + #endif + bsd_close_socket(us_poll_fd((struct us_poll_t *) s)); + + /* Link this socket to the close-list and let it be deleted after this iteration */ + s->next = s->context->loop->data.closed_head; + s->context->loop->data.closed_head = s; + + /* Any socket with prev = context is marked as closed */ + s->prev = (struct us_socket_t *) s->context; + + return s->context->on_close(s, code, reason); + } + return s; +} + +// This function is the same as us_socket_close but: +// - does not emit on_close event +// - does not close +struct us_socket_t *us_socket_detach(int ssl, struct us_socket_t *s) { + if (!us_socket_is_closed(0, s)) { + if (s->low_prio_state == 1) { + /* Unlink this socket from the low-priority queue */ + if (!s->prev) s->context->loop->data.low_prio_head = s->next; + else s->prev->next = s->next; + + if (s->next) s->next->prev = s->prev; + + s->prev = 0; + s->next = 0; + s->low_prio_state = 0; + } else { + us_internal_socket_context_unlink(s->context, s); + } + us_poll_stop((struct us_poll_t *) s, s->context->loop); + + /* Link this socket to the close-list and let it be deleted after this iteration */ + s->next = s->context->loop->data.closed_head; + s->context->loop->data.closed_head = s; + + /* Any socket with prev = context is marked as closed */ + s->prev = (struct us_socket_t *) s->context; + + return s; + } + return s; +} + +// This function is used for moving a socket between two different event loops +struct us_socket_t *us_socket_attach(int ssl, LIBUS_SOCKET_DESCRIPTOR client_fd, struct us_socket_context_t *ctx, int flags, int socket_ext_size) { + struct us_poll_t *accepted_p = us_create_poll(ctx->loop, 0, sizeof(struct us_socket_t) - sizeof(struct us_poll_t) + socket_ext_size); + us_poll_init(accepted_p, client_fd, POLL_TYPE_SOCKET); + us_poll_start(accepted_p, ctx->loop, flags); + + struct us_socket_t *s = (struct us_socket_t *) accepted_p; + + s->context = ctx; + s->timeout = 0; + s->low_prio_state = 0; + + /* We always use nodelay */ + bsd_socket_nodelay(client_fd, 1); + us_internal_socket_context_link(ctx, s); + + if (ctx->on_open) ctx->on_open(s, 0, 0, 0); + + return s; +} + +/* Not shared with SSL */ + +void *us_socket_get_native_handle(int ssl, struct us_socket_t *s) { +#ifndef LIBUS_NO_SSL + if (ssl) { + return us_internal_ssl_socket_get_native_handle((struct us_internal_ssl_socket_t *) s); + } +#endif + + return (void *) (uintptr_t) us_poll_fd((struct us_poll_t *) s); +} + +int us_socket_write(int ssl, struct us_socket_t *s, const char *data, int length, int msg_more) { +#ifndef LIBUS_NO_SSL + if (ssl) { + return us_internal_ssl_socket_write((struct us_internal_ssl_socket_t *) s, data, length, msg_more); + } +#endif + + if (us_socket_is_closed(ssl, s) || us_socket_is_shut_down(ssl, s)) { + return 0; + } + + int written = bsd_send(us_poll_fd(&s->p), data, length, msg_more); + if (written != length) { + s->context->loop->data.last_write_failed = 1; + us_poll_change(&s->p, s->context->loop, LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE); + } + + return written < 0 ? 0 : written; +} + +void *us_socket_ext(int ssl, struct us_socket_t *s) { +#ifndef LIBUS_NO_SSL + if (ssl) { + return us_internal_ssl_socket_ext((struct us_internal_ssl_socket_t *) s); + } +#endif + + return s + 1; +} + +int us_socket_is_shut_down(int ssl, struct us_socket_t *s) { +#ifndef LIBUS_NO_SSL + if (ssl) { + return us_internal_ssl_socket_is_shut_down((struct us_internal_ssl_socket_t *) s); + } +#endif + + return us_internal_poll_type(&s->p) == POLL_TYPE_SOCKET_SHUT_DOWN; +} + +void us_socket_shutdown(int ssl, struct us_socket_t *s) { +#ifndef LIBUS_NO_SSL + if (ssl) { + us_internal_ssl_socket_shutdown((struct us_internal_ssl_socket_t *) s); + return; + } +#endif + + /* Todo: should we emit on_close if calling shutdown on an already half-closed socket? + * We need more states in that case, we need to track RECEIVED_FIN + * so far, the app has to track this and call close as needed */ + if (!us_socket_is_closed(ssl, s) && !us_socket_is_shut_down(ssl, s)) { + us_internal_poll_set_type(&s->p, POLL_TYPE_SOCKET_SHUT_DOWN); + us_poll_change(&s->p, s->context->loop, us_poll_events(&s->p) & LIBUS_SOCKET_READABLE); + bsd_shutdown_socket(us_poll_fd((struct us_poll_t *) s)); + } +} + +/* + Note: this assumes that the socket is non-TLS and will be adopted and wrapped with a new TLS context + context ext will not be copied to the new context, new context will contain us_wrapped_socket_context_t on ext +*/ +struct us_socket_t *us_socket_wrap_with_tls(int ssl, struct us_socket_t *s, struct us_bun_socket_context_options_t options, struct us_socket_events_t events, int socket_ext_size) { + // only accepts non-TLS sockets + if (ssl) { + return NULL; + } + + return(struct us_socket_t *) us_internal_ssl_socket_wrap_with_tls(s, options, events, socket_ext_size); +} + +// if a TLS socket calls this, it will start SSL call open event and TLS handshake if required +// will have no effect if the socket is closed or is not TLS +struct us_socket_t* us_socket_open(int ssl, struct us_socket_t * s, int is_client, char* ip, int ip_length) { + if (ssl) { + return(struct us_socket_t *) us_internal_ssl_socket_open((struct us_internal_ssl_socket_t *)s, is_client, ip, ip_length); + } + return s; +} + +int us_socket_raw_write(int ssl, struct us_socket_t *s, const char *data, int length, int msg_more) { +#ifndef LIBUS_NO_SSL + if (ssl) { + return us_internal_ssl_socket_raw_write((struct us_internal_ssl_socket_t *) s, data, length, msg_more); + } +#endif + // non-TLS is always raw + return us_socket_write(ssl, s, data, length, msg_more); +} diff --git a/packages/bun-usockets/src/udp.c b/packages/bun-usockets/src/udp.c new file mode 100644 index 000000000..70a7d3a00 --- /dev/null +++ b/packages/bun-usockets/src/udp.c @@ -0,0 +1,147 @@ +/* + * Authored by Alex Hultman, 2018-2021. + * Intellectual property of third-party. + + * 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 + + * http://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 "libusockets.h" +#include "internal/internal.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int us_udp_packet_buffer_ecn(struct us_udp_packet_buffer_t *buf, int index) { + return bsd_udp_packet_buffer_ecn(buf, index); +} + +int us_udp_packet_buffer_local_ip(struct us_udp_packet_buffer_t *buf, int index, char *ip) { + return bsd_udp_packet_buffer_local_ip(buf, index, ip); +} + +char *us_udp_packet_buffer_peer(struct us_udp_packet_buffer_t *buf, int index) { + return bsd_udp_packet_buffer_peer(buf, index); +} + +char *us_udp_packet_buffer_payload(struct us_udp_packet_buffer_t *buf, int index) { + return bsd_udp_packet_buffer_payload(buf, index); +} + +int us_udp_packet_buffer_payload_length(struct us_udp_packet_buffer_t *buf, int index) { + return bsd_udp_packet_buffer_payload_length(buf, index); +} + +// what should we return? number of sent datagrams? +int us_udp_socket_send(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf, int num) { + int fd = us_poll_fd((struct us_poll_t *) s); + + // we need to poll out if we failed + + return bsd_sendmmsg(fd, buf, num, 0); +} + +int us_udp_socket_receive(struct us_udp_socket_t *s, struct us_udp_packet_buffer_t *buf) { + int fd = us_poll_fd((struct us_poll_t *) s); + return bsd_recvmmsg(fd, buf, LIBUS_UDP_MAX_NUM, 0, 0); +} + +void us_udp_buffer_set_packet_payload(struct us_udp_packet_buffer_t *send_buf, int index, int offset, void *payload, int length, void *peer_addr) { + bsd_udp_buffer_set_packet_payload(send_buf, index, offset, payload, length, peer_addr); +} + +struct us_udp_packet_buffer_t *us_create_udp_packet_buffer() { + return (struct us_udp_packet_buffer_t *) bsd_create_udp_packet_buffer(); +} + +struct us_internal_udp_t { + struct us_internal_callback_t cb; + struct us_udp_packet_buffer_t *receive_buf; + void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int); + void (*drain_cb)(struct us_udp_socket_t *); + void *user; + /* An UDP socket can only ever be bound to one single port regardless of how + * many interfaces it may listen to. Therefore we cache the port after creation + * and use it to build a proper and full sockaddr_in or sockaddr_in6 for every received packet */ + int port; +}; + +int us_udp_socket_bound_port(struct us_udp_socket_t *s) { + return ((struct us_internal_udp_t *) s)->port; +} + +/* Internal wrapper, move from here */ +void internal_on_udp_read(struct us_udp_socket_t *s) { + + // lookup receive buffer and callback here + struct us_internal_udp_t *udp = (struct us_internal_udp_t *) s; + + int packets = us_udp_socket_receive(s, udp->receive_buf); + //printf("Packets: %d\n", packets); + + // we need to get the socket data and lookup its callback here + + + udp->data_cb(s, udp->receive_buf, packets); +} + +void *us_udp_socket_user(struct us_udp_socket_t *s) { + struct us_internal_udp_t *udp = (struct us_internal_udp_t *) s; + + return udp->user; +} + +struct us_udp_socket_t *us_create_udp_socket(struct us_loop_t *loop, struct us_udp_packet_buffer_t *buf, void (*data_cb)(struct us_udp_socket_t *, struct us_udp_packet_buffer_t *, int), void (*drain_cb)(struct us_udp_socket_t *), const char *host, unsigned short port, void *user) { + + LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_udp_socket(host, port); + if (fd == LIBUS_SOCKET_ERROR) { + return 0; + } + + /* If buf is 0 then create one here */ + if (!buf) { + buf = us_create_udp_packet_buffer(); + } + + int ext_size = 0; + int fallthrough = 0; + + struct us_poll_t *p = us_create_poll(loop, fallthrough, sizeof(struct us_internal_udp_t) + ext_size); + us_poll_init(p, fd, POLL_TYPE_CALLBACK); + + struct us_internal_udp_t *cb = (struct us_internal_udp_t *) p; + cb->cb.loop = loop; + cb->cb.cb_expects_the_loop = 0; + cb->cb.leave_poll_ready = 1; + + /* Get and store the port once */ + struct bsd_addr_t tmp; + bsd_local_addr(fd, &tmp); + cb->port = bsd_addr_get_port(&tmp); + + printf("The port of UDP is: %d\n", cb->port); + + /* There is no udp socket context, only user data */ + /* This should really be ext like everything else */ + cb->user = user; + + cb->data_cb = data_cb; + cb->receive_buf = buf; + cb->drain_cb = drain_cb; + + cb->cb.cb = (void (*)(struct us_internal_callback_t *)) internal_on_udp_read; + + us_poll_start((struct us_poll_t *) cb, cb->cb.loop, LIBUS_SOCKET_READABLE); + + return (struct us_udp_socket_t *) cb; +}
\ No newline at end of file |