summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/ouroboros/atomics.h39
-rw-r--r--include/ouroboros/crc16.h43
-rw-r--r--include/ouroboros/crc64.h44
-rw-r--r--include/ouroboros/crc8.h43
-rw-r--r--include/ouroboros/crypt.h81
-rw-r--r--include/ouroboros/errno.h1
-rw-r--r--include/ouroboros/fccntl.h13
-rw-r--r--include/ouroboros/flow.h3
-rw-r--r--include/ouroboros/fqueue.h3
-rw-r--r--include/ouroboros/hash.h6
-rw-r--r--include/ouroboros/ipcp-dev.h7
-rw-r--r--include/ouroboros/ipcp.h8
-rw-r--r--include/ouroboros/irm.h6
-rw-r--r--include/ouroboros/logs.h7
-rw-r--r--include/ouroboros/name.h6
-rw-r--r--include/ouroboros/np1_flow.h6
-rw-r--r--include/ouroboros/pthread.h6
-rw-r--r--include/ouroboros/qos.h57
-rw-r--r--include/ouroboros/rcu.h110
-rw-r--r--include/ouroboros/serdes-irm.h14
-rw-r--r--include/ouroboros/ssm_pk_buff.h24
-rw-r--r--include/ouroboros/ssm_pool.h12
-rw-r--r--include/ouroboros/ssm_rbuff.h23
-rw-r--r--include/ouroboros/time.h6
-rw-r--r--include/ouroboros/tpm.h1
-rw-r--r--include/ouroboros/tw.h77
-rw-r--r--include/test/certs/ecdsa.h37
-rw-r--r--include/test/test.h6
28 files changed, 618 insertions, 71 deletions
diff --git a/include/ouroboros/atomics.h b/include/ouroboros/atomics.h
new file mode 100644
index 00000000..8e667522
--- /dev/null
+++ b/include/ouroboros/atomics.h
@@ -0,0 +1,39 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * Atomic helpers
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef OUROBOROS_LIB_ATOMICS_H
+#define OUROBOROS_LIB_ATOMICS_H
+
+#define LOAD_RELAXED(p) (__atomic_load_n(p, __ATOMIC_RELAXED))
+#define LOAD_ACQUIRE(p) (__atomic_load_n(p, __ATOMIC_ACQUIRE))
+#define LOAD(p) (__atomic_load_n(p, __ATOMIC_SEQ_CST))
+
+#define STORE_RELAXED(p, v) (__atomic_store_n(p, v, __ATOMIC_RELAXED))
+#define STORE_RELEASE(p, v) (__atomic_store_n(p, v, __ATOMIC_RELEASE))
+#define STORE(p, v) (__atomic_store_n(p, v, __ATOMIC_SEQ_CST))
+
+#define FETCH_ADD_RELAXED(p, v) (__atomic_fetch_add(p, v, __ATOMIC_RELAXED))
+#define FETCH_SUB_RELAXED(p, v) (__atomic_fetch_sub(p, v, __ATOMIC_RELAXED))
+#define FETCH_ADD(p, v) (__atomic_fetch_add(p, v, __ATOMIC_SEQ_CST))
+#define FETCH_SUB(p, v) (__atomic_fetch_sub(p, v, __ATOMIC_SEQ_CST))
+
+#endif /* OUROBOROS_LIB_ATOMICS_H */
diff --git a/include/ouroboros/crc16.h b/include/ouroboros/crc16.h
new file mode 100644
index 00000000..df4d4f57
--- /dev/null
+++ b/include/ouroboros/crc16.h
@@ -0,0 +1,43 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * 16-bit Cyclic Redundancy Check (CCITT-FALSE variant)
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+/*
+ * Polynomial: ITU-T V.41 / CCITT-FALSE, CRC-16/IBM-3740.
+ * reveng catalog: https://reveng.sourceforge.io/crc-catalogue
+ *
+ * Intended for medium-size header check sequences (typ. <= 4 KiB).
+ * Hamming distance HD=4 up to 32751 message bits.
+ */
+
+#ifndef OUROBOROS_LIB_CRC16_H
+#define OUROBOROS_LIB_CRC16_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#define CRC16_HASH_LEN 2
+
+void crc16_ccitt_false(uint16_t * crc,
+ const void * buf,
+ size_t len);
+
+#endif /* OUROBOROS_LIB_CRC16_H */
diff --git a/include/ouroboros/crc64.h b/include/ouroboros/crc64.h
new file mode 100644
index 00000000..f6e407a0
--- /dev/null
+++ b/include/ouroboros/crc64.h
@@ -0,0 +1,44 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * 64-bit Cyclic Redundancy Check (NVMe variant)
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+/*
+ * Polynomial: NVM Express Base Spec, CRC-64/NVMe.
+ * reveng catalog: https://reveng.sourceforge.io/crc-catalogue
+ *
+ * Fold-by-N (PCLMUL/PMULL) algorithm:
+ * V. Gopal et al., "Fast CRC Computation for Generic Polynomials
+ * Using PCLMULQDQ", Intel white paper, 2009.
+ */
+
+#ifndef OUROBOROS_LIB_CRC64_H
+#define OUROBOROS_LIB_CRC64_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#define CRC64_HASH_LEN 8
+
+void crc64_nvme(uint64_t * crc,
+ const void * buf,
+ size_t len);
+
+#endif /* OUROBOROS_LIB_CRC64_H */
diff --git a/include/ouroboros/crc8.h b/include/ouroboros/crc8.h
new file mode 100644
index 00000000..97502a25
--- /dev/null
+++ b/include/ouroboros/crc8.h
@@ -0,0 +1,43 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * 8-bit Cyclic Redundancy Check (AUTOSAR variant)
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+/*
+ * Polynomial: AUTOSAR SWS_CRC, CRC-8/AUTOSAR.
+ * reveng catalog: https://reveng.sourceforge.io/crc-catalogue
+ *
+ * Intended for short header check sequences (typ. <= 32 bytes).
+ * Hamming distance HD=4 up to 119 message bits, HD=3 up to 247.
+ */
+
+#ifndef OUROBOROS_LIB_CRC8_H
+#define OUROBOROS_LIB_CRC8_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#define CRC8_HASH_LEN 1
+
+void crc8_autosar(uint8_t * crc,
+ const void * buf,
+ size_t len);
+
+#endif /* OUROBOROS_LIB_CRC8_H */
diff --git a/include/ouroboros/crypt.h b/include/ouroboros/crypt.h
index 806d39ab..9feaa610 100644
--- a/include/ouroboros/crypt.h
+++ b/include/ouroboros/crypt.h
@@ -28,19 +28,19 @@
#include <assert.h>
-#define IVSZ 16
+#define NONCESZ 16
#define SYMMKEYSZ 32
#define MAX_HASH_SIZE 64 /* SHA-512/BLAKE2b max */
#define KEX_ALGO_BUFSZ 32
#define KEX_CIPHER_BUFSZ 32
-#define MSGBUFSZ 2048
+#define CACERT_PATH_BUFSZ 256
/*
* On OSX the OpenSSL NIDs are automatically loaded with evp.h.
* Some have a different spelling. This header avoids the double definitions.
*/
- #define NID_undef 0
+#define NID_undef 0
/* Cipher NIDs (match OpenSSL values) */
#define NID_aes_128_gcm 895
@@ -51,7 +51,7 @@
#define NID_aes_256_ctr 906
#define NID_chacha20_poly1305 1018
- #if !defined (__APPLE__) || !defined ( HAVE_OPENSSL )
+#if !defined (__APPLE__) || !defined ( HAVE_OPENSSL )
/* KEX algorithm NIDs (match OpenSSL values) */
#define NID_X9_62_prime256v1 415
#define NID_secp384r1 715
@@ -95,16 +95,22 @@
#define X448MLKEM1024_PKSZ 1624 /* 56 + 1568 */
#define X448MLKEM1024_SKSZ 3224 /* 56 + 3168 */
+#define CRYPT_KEY_BUFSZ 4096 /* Safe buffer for key material */
+
#define KEM_MODE_SERVER_ENCAP 0 /* Server encapsulates (default) */
#define KEM_MODE_CLIENT_ENCAP 1 /* Client encapsulates */
#define IS_KEX_ALGO_SET(cfg) ((cfg)->x.nid != NID_undef)
#define IS_KEX_CIPHER_SET(cfg) ((cfg)->c.nid != NID_undef)
+/* Flow role: forks the per-direction keys so each end's TX = peer's RX. */
+#define CRYPT_ROLE_INIT 0 /* flow allocator / OAP client */
+#define CRYPT_ROLE_RESP 1 /* flow acceptor / OAP server */
struct crypt_sk {
int nid;
uint8_t * key;
- uint8_t rot_bit; /* Rotation bit to control epoch */
+ uint8_t epoch; /* installed batch epoch */
+ uint8_t role; /* CRYPT_ROLE_INIT / _RESP */
};
struct sec_config {
@@ -113,18 +119,26 @@ struct sec_config {
int nid;
int mode;
} x; /* key exchange */
+
struct {
const char * str;
int nid;
} k; /* kdf */
+
struct {
const char * str;
int nid;
} c; /* cipher */
+
struct {
const char * str;
int nid;
} d; /* digest */
+
+ struct {
+ bool req; /* require peer auth */
+ char cacert[CACERT_PATH_BUFSZ]; /* pinned CA, "" = any */
+ } a; /* authentication */
};
/* Helper macros to set sec_config fields consistently */
@@ -210,9 +224,21 @@ void auth_destroy_ctx(struct auth_ctx * ctx);
int auth_add_crt_to_store(struct auth_ctx * ctx,
void * crt);
+/* Untrusted intermediates: used to build a path, never as trust anchors */
+int auth_add_crt_to_chain(struct auth_ctx * ctx,
+ void * crt);
+
int auth_verify_crt(struct auth_ctx * ctx,
void * crt);
+/* As auth_verify_crt, pin must be in the verified chain (NULL: any) */
+int auth_verify_crt_pin(struct auth_ctx * ctx,
+ void * crt,
+ void * pin);
+
+/* False for PQC keys: their signature digest is intrinsic */
+bool crypt_pk_requires_md(const void * pk);
+
int auth_sign(void * pkp,
int md_nid,
buffer_t msg,
@@ -288,12 +314,16 @@ const char * md_nid_to_str(uint16_t nid);
uint16_t md_str_to_nid(const char * kdf);
-ssize_t md_digest(int md_nid,
- buffer_t in,
- uint8_t * out);
+ssize_t md_digest(int md_nid,
+ buffer_t in,
+ uint8_t * out);
ssize_t md_len(int md_nid);
+int crypt_hkdf_expand(buffer_t key,
+ buffer_t info,
+ buffer_t out);
+
int crypt_encrypt(struct crypt_ctx * ctx,
buffer_t in,
buffer_t * out);
@@ -302,10 +332,37 @@ int crypt_decrypt(struct crypt_ctx * ctx,
buffer_t in,
buffer_t * out);
-int crypt_get_ivsz(struct crypt_ctx * ctx);
+/* One-shot AEAD over an explicit key/nonce. out = ciphertext ‖ tag. */
+int crypt_oneshot_seal(int nid,
+ const uint8_t * key,
+ const uint8_t * nonce,
+ buffer_t aad,
+ buffer_t in,
+ buffer_t * out);
+
+int crypt_oneshot_open(int nid,
+ const uint8_t * key,
+ const uint8_t * nonce,
+ buffer_t aad,
+ buffer_t in,
+ buffer_t * out);
+
+int crypt_get_headsz(struct crypt_ctx * ctx);
int crypt_get_tagsz(struct crypt_ctx * ctx);
+int crypt_rekey(struct crypt_ctx * ctx,
+ struct crypt_sk * sk);
+
+/* Nodes remaining in the TX batch (re-key watermark). */
+int crypt_nodes_left(struct crypt_ctx * ctx);
+
+/* 1 once the peer has been observed on the current generation. */
+int crypt_peer_synced(struct crypt_ctx * ctx);
+
+/* Switch TX to the installed (new) batch (after peer synced/grace). */
+void crypt_tx_promote(struct crypt_ctx * ctx);
+
int crypt_load_crt_file(const char * path,
void ** crt);
@@ -341,6 +398,10 @@ int crypt_load_pubkey_raw_file(const char * path,
int crypt_load_privkey_raw_file(const char * path,
void ** key);
+int crypt_ct_cmp(const void * a,
+ const void * b,
+ size_t len);
+
int crypt_cmp_key(const void * key1,
const void * key2);
@@ -358,6 +419,8 @@ int crypt_check_crt_name(void * crt,
int crypt_get_crt_name(void * crt,
char * name);
+void crypt_cleanup(void);
+
/* Secure memory allocation for sensitive data (keys, secrets) */
int crypt_secure_malloc_init(size_t max);
diff --git a/include/ouroboros/errno.h b/include/ouroboros/errno.h
index 9d84df88..eedd978f 100644
--- a/include/ouroboros/errno.h
+++ b/include/ouroboros/errno.h
@@ -37,5 +37,6 @@
#ifndef EAUTH /* Exists on BSD */
#define EAUTH 1009 /* Authentication error */
#endif
+#define EREPLAY 1010 /* OAP replay detected */
#endif /* OUROBOROS_ERRNO_H */
diff --git a/include/ouroboros/fccntl.h b/include/ouroboros/fccntl.h
index d3baea8f..e91e91dd 100644
--- a/include/ouroboros/fccntl.h
+++ b/include/ouroboros/fccntl.h
@@ -50,6 +50,12 @@
#define FRCTFRESCNTL 00000002 /* Feedback from receiver */
#define FRCTFLINGER 00000004 /* Send unsent data */
+/* All user-visible bits (readable via FRCTGFLAGS). */
+#define FRCTFMASK (FRCTFRTX | FRCTFRESCNTL | FRCTFLINGER)
+
+/* Subset writable via FRCTSFLAGS; FRCTFRTX is fixed at flow_alloc. */
+#define FRCTFSETMASK (FRCTFRESCNTL | FRCTFLINGER)
+
/* Flow operations */
#define FLOWSRCVTIMEO 00000001 /* Set read timeout */
#define FLOWGRCVTIMEO 00000002 /* Get read timeout */
@@ -60,10 +66,17 @@
#define FLOWGFLAGS 00000007 /* Get flags for flow */
#define FLOWGRXQLEN 00000010 /* Get queue length on rx */
#define FLOWGTXQLEN 00000011 /* Get queue length on tx */
+#define FLOWGMTU 00000012 /* Get per-packet MTU */
/* FRCT operations */
#define FRCTSFLAGS 00001000 /* Set flags for FRCT */
#define FRCTGFLAGS 00002000 /* Get flags for FRCT */
+#define FRCTSMAXSDU 00003000 /* Set max recv SDU size */
+#define FRCTGMAXSDU 00004000 /* Get max recv SDU size */
+#define FRCTSRRINGSZ 00005000 /* Set stream rcv ring sz */
+#define FRCTGRRINGSZ 00006000 /* Get stream rcv ring sz */
+#define FRCTSRTOMIN 00007000 /* Set RTO floor (ns) */
+#define FRCTGRTOMIN 00010000 /* Get RTO floor (ns) */
__BEGIN_DECLS
diff --git a/include/ouroboros/flow.h b/include/ouroboros/flow.h
index fe4582e7..8b096410 100644
--- a/include/ouroboros/flow.h
+++ b/include/ouroboros/flow.h
@@ -25,6 +25,7 @@
#include <ouroboros/qos.h>
+#include <stdint.h>
#include <sys/types.h>
#define SYMMKEYSZ 32
@@ -50,6 +51,8 @@ struct flow_info {
time_t mpl;
+ uint32_t mtu; /* n-1 layer MTU in bytes, 0 = unknown */
+
struct qos_spec qs;
enum flow_state state;
diff --git a/include/ouroboros/fqueue.h b/include/ouroboros/fqueue.h
index 2546c79d..322da3ea 100644
--- a/include/ouroboros/fqueue.h
+++ b/include/ouroboros/fqueue.h
@@ -34,7 +34,8 @@ enum fqtype {
FLOW_UP = (1 << 2),
FLOW_ALLOC = (1 << 3),
FLOW_DEALLOC = (1 << 4),
- FLOW_PEER = (1 << 5)
+ FLOW_PEER = (1 << 5),
+ FLOW_UPD = (1 << 6)
};
struct flow_set;
diff --git a/include/ouroboros/hash.h b/include/ouroboros/hash.h
index 0838df97..c6609ffc 100644
--- a/include/ouroboros/hash.h
+++ b/include/ouroboros/hash.h
@@ -38,6 +38,9 @@ enum hash_algo {
HASH_SHA3_512 = DIR_HASH_SHA3_512,
HASH_CRC32,
HASH_MD5,
+ HASH_CRC64,
+ HASH_CRC8,
+ HASH_CRC16,
};
#define HASH_FMT32 "%02x%02x%02x%02x"
@@ -86,4 +89,7 @@ void str_hash(enum hash_algo algo,
void * dst,
const char * str);
+/* Non-cryptographic finalizer for hashing an integer key to a table index. */
+uint64_t hash_mix64(uint64_t key);
+
#endif /* OUROBOROS_LIB_HASH_H */
diff --git a/include/ouroboros/ipcp-dev.h b/include/ouroboros/ipcp-dev.h
index 93236271..d00d6f08 100644
--- a/include/ouroboros/ipcp-dev.h
+++ b/include/ouroboros/ipcp-dev.h
@@ -28,16 +28,23 @@
#include <ouroboros/ssm_pool.h>
#include <ouroboros/utils.h>
+#include <stdint.h>
+
int ipcp_create_r(const struct ipcp_info * info);
int ipcp_flow_req_arr(const buffer_t * dst,
qosspec_t qs,
time_t mpl,
+ uint32_t mtu,
const buffer_t * data);
+int ipcp_flow_update_arr(int flow_id,
+ const buffer_t * data);
+
int ipcp_flow_alloc_reply(int fd,
int response,
time_t mpl,
+ uint32_t mtu,
const buffer_t * data);
int ipcp_flow_read(int fd,
diff --git a/include/ouroboros/ipcp.h b/include/ouroboros/ipcp.h
index e29b080a..135b8fcb 100644
--- a/include/ouroboros/ipcp.h
+++ b/include/ouroboros/ipcp.h
@@ -124,9 +124,10 @@ enum pol_cong_avoid {
struct dt_config {
struct {
- uint8_t addr_size;
- uint8_t eid_size;
- uint8_t max_ttl;
+ uint8_t addr_size;
+ uint8_t eid_size;
+ uint8_t max_ttl;
+ uint16_t max_rtt; /* Declared max layer RTT (ms) */
};
struct routing_config routing; /* Routing policy */
};
@@ -135,6 +136,7 @@ static const struct dt_config default_dt_config = {
.addr_size = 4,
.eid_size = 8,
.max_ttl = 60,
+ .max_rtt = 200,
.routing = {
.pol = ROUTING_LINK_STATE,
.ls = {
diff --git a/include/ouroboros/irm.h b/include/ouroboros/irm.h
index d5e4f1ab..7cb71c21 100644
--- a/include/ouroboros/irm.h
+++ b/include/ouroboros/irm.h
@@ -53,13 +53,13 @@ int irm_bootstrap_ipcp(pid_t pid,
const struct ipcp_config * conf);
int irm_connect_ipcp(pid_t pid,
- const char * component,
const char * dst,
+ const char * component,
qosspec_t qs);
int irm_disconnect_ipcp(pid_t pid,
- const char * component,
- const char * dst);
+ const char * dst,
+ const char * component);
int irm_bind_program(const char * prog,
const char * name,
diff --git a/include/ouroboros/logs.h b/include/ouroboros/logs.h
index 58494531..1ae77673 100644
--- a/include/ouroboros/logs.h
+++ b/include/ouroboros/logs.h
@@ -29,6 +29,7 @@
#include <ouroboros/hash.h>
+#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <stdbool.h>
@@ -55,6 +56,8 @@ void log_fini(void);
#define __olog(CLR, LVL, SYSLVL, ...) \
do { \
+ int __cs; \
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &__cs); \
if (log_syslog) { \
syslog(SYSLVL, __VA_ARGS__); \
} else { \
@@ -64,10 +67,13 @@ void log_fini(void);
printf(CLR_RESET "\n"); \
fflush(stdout); \
} \
+ pthread_setcancelstate(__cs, NULL); \
} while (0)
#define __olog_id(CLR, LVL, SYSLVL, id, fmt, ...) \
do { \
+ int __cs; \
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &__cs); \
if (log_syslog) { \
syslog(SYSLVL, "[" HASH_FMT64 "] " fmt, \
HASH_VAL64(id), ## __VA_ARGS__); \
@@ -79,6 +85,7 @@ void log_fini(void);
printf(CLR_RESET "\n"); \
fflush(stdout); \
} \
+ pthread_setcancelstate(__cs, NULL); \
} while (0)
#ifndef OUROBOROS_DISABLE_LOGGING
diff --git a/include/ouroboros/name.h b/include/ouroboros/name.h
index a9393820..a3aac8c4 100644
--- a/include/ouroboros/name.h
+++ b/include/ouroboros/name.h
@@ -34,9 +34,9 @@ enum pol_balance {
};
struct name_sec_paths {
- char enc[NAME_PATH_SIZE + 1]; /* path to crypt for this name */
- char key[NAME_PATH_SIZE + 1]; /* path to key for this name */
- char crt[NAME_PATH_SIZE + 1]; /* path to crt for this name */
+ char sec[NAME_PATH_SIZE + 1]; /* path to sec.conf for this name */
+ char key[NAME_PATH_SIZE + 1]; /* path to key for this name */
+ char crt[NAME_PATH_SIZE + 1]; /* path to crt for this name */
};
struct name_info {
diff --git a/include/ouroboros/np1_flow.h b/include/ouroboros/np1_flow.h
index 6f341cfc..758b6db8 100644
--- a/include/ouroboros/np1_flow.h
+++ b/include/ouroboros/np1_flow.h
@@ -36,13 +36,17 @@ int np1_flow_resp(int flow_id,
int np1_flow_dealloc(int flow_id,
time_t timeo);
+int np1_flow_fd(int flow_id);
+
+int np1_flow_id(int fd);
+
static const qosspec_t qos_np1 = {
+ .service = SVC_RAW,
.delay = UINT32_MAX,
.bandwidth = 0,
.availability = 0,
.loss = UINT32_MAX,
.ber = UINT32_MAX,
- .in_order = 0,
.max_gap = UINT32_MAX,
.timeout = 0
};
diff --git a/include/ouroboros/pthread.h b/include/ouroboros/pthread.h
index cd500795..3ca79d10 100644
--- a/include/ouroboros/pthread.h
+++ b/include/ouroboros/pthread.h
@@ -24,6 +24,7 @@
#define OUROBOROS_LIB_PTHREAD_H
#include <pthread.h>
+#include <stdio.h>
static int __attribute__((unused)) __timedwait(pthread_cond_t * cond,
pthread_mutex_t * mtx,
@@ -48,4 +49,9 @@ static void __attribute__((unused)) __cleanup_mutex_unlock(void * mutex)
pthread_mutex_unlock((pthread_mutex_t *) mutex);
}
+static void __attribute__((unused)) __cleanup_fclose(void * fp)
+{
+ fclose((FILE *) fp);
+}
+
#endif /* OUROBOROS_LIB_PTHREAD_H */
diff --git a/include/ouroboros/qos.h b/include/ouroboros/qos.h
index 6b0bbc17..7980ad00 100644
--- a/include/ouroboros/qos.h
+++ b/include/ouroboros/qos.h
@@ -28,79 +28,88 @@
#define DEFAULT_PEER_TIMEOUT 120000
+/* qos_spec.service: framing / reliability class. */
+enum qos_service {
+ SVC_RAW = 0, /* No FRCT; best-effort raw messages */
+ SVC_MESSAGE = 1, /* FRCT, reliable ordered messages */
+ SVC_STREAM = 2, /* FRCT, reliable ordered byte stream */
+};
+
typedef struct qos_spec {
+ uint8_t service; /* enum qos_service; gates FRCT (>0). */
uint32_t delay; /* In ms. */
uint64_t bandwidth; /* In bits/s. */
uint8_t availability; /* Class of 9s. */
uint32_t loss; /* Packet loss. */
uint32_t ber; /* Bit error rate, errors per billion bits. */
- uint8_t in_order; /* In-order delivery, enables FRCT. */
uint32_t max_gap; /* In ms. */
uint32_t timeout; /* Peer timeout time, in ms, 0 = no timeout. */
} qosspec_t;
+/* "_safe" = integrity check (ber=0). "rt" = latency over reliability. */
+
static const qosspec_t qos_raw = {
+ .service = SVC_RAW,
.delay = UINT32_MAX,
.bandwidth = 0,
.availability = 0,
.loss = 1,
.ber = 1,
- .in_order = 0,
.max_gap = UINT32_MAX,
- .timeout = DEFAULT_PEER_TIMEOUT
+ .timeout = 0
};
-static const qosspec_t qos_raw_no_errors = {
+static const qosspec_t qos_raw_safe = {
+ .service = SVC_RAW,
.delay = UINT32_MAX,
.bandwidth = 0,
.availability = 0,
.loss = 1,
.ber = 0,
- .in_order = 0,
.max_gap = UINT32_MAX,
- .timeout = DEFAULT_PEER_TIMEOUT
+ .timeout = 0
};
-static const qosspec_t qos_best_effort = {
- .delay = UINT32_MAX,
- .bandwidth = 0,
- .availability = 0,
+static const qosspec_t qos_rt = {
+ .service = SVC_MESSAGE,
+ .delay = 100,
+ .bandwidth = UINT64_MAX,
+ .availability = 3,
.loss = 1,
- .ber = 0,
- .in_order = 1,
- .max_gap = UINT32_MAX,
+ .ber = 1,
+ .max_gap = 100,
.timeout = DEFAULT_PEER_TIMEOUT
};
-static const qosspec_t qos_video = {
+static const qosspec_t qos_rt_safe = {
+ .service = SVC_MESSAGE,
.delay = 100,
.bandwidth = UINT64_MAX,
.availability = 3,
.loss = 1,
.ber = 0,
- .in_order = 1,
.max_gap = 100,
.timeout = DEFAULT_PEER_TIMEOUT
};
-static const qosspec_t qos_voice = {
- .delay = 50,
- .bandwidth = 100000,
- .availability = 5,
- .loss = 1,
+static const qosspec_t qos_msg = {
+ .service = SVC_MESSAGE,
+ .delay = 1000,
+ .bandwidth = 0,
+ .availability = 0,
+ .loss = 0,
.ber = 0,
- .in_order = 1,
- .max_gap = 50,
+ .max_gap = 2000,
.timeout = DEFAULT_PEER_TIMEOUT
};
-static const qosspec_t qos_data = {
+static const qosspec_t qos_stream = {
+ .service = SVC_STREAM,
.delay = 1000,
.bandwidth = 0,
.availability = 0,
.loss = 0,
.ber = 0,
- .in_order = 1,
.max_gap = 2000,
.timeout = DEFAULT_PEER_TIMEOUT
};
diff --git a/include/ouroboros/rcu.h b/include/ouroboros/rcu.h
new file mode 100644
index 00000000..b4e7d27c
--- /dev/null
+++ b/include/ouroboros/rcu.h
@@ -0,0 +1,110 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * Read-mostly pointer publication (RCU, with a locked fallback)
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef OUROBOROS_LIB_RCU_H
+#define OUROBOROS_LIB_RCU_H
+
+/*
+ * Lock-free reads of published pointers via liburcu (urcu-bp) when
+ * available; a per-object rwlock fallback otherwise.
+ * Include config.h before this header so HAVE_LIBURCU is defined.
+ *
+ * Embed a struct rcu_guard in the object. A reader brackets its access
+ * with rcu_rdlock/rcu_rdunlock and reads published pointers via rcu_deref.
+ * A writer serialises with rcu_wrlock/rcu_wrunlock and publishes via
+ * rcu_assign; after unlock it reclaims a now-unreachable object with
+ * rcu_reclaim (waits out live readers) before freeing it. rcu_drain waits
+ * out all readers at teardown.
+ */
+
+#include <ouroboros/pthread.h>
+
+#ifdef HAVE_LIBURCU
+
+#include <urcu-bp.h>
+
+struct rcu_guard {
+ pthread_mutex_t w; /* serialises writers; readers use RCU */
+};
+
+#define rcu_guard_init(g) pthread_mutex_init(&(g)->w, NULL)
+#define rcu_guard_fini(g) pthread_mutex_destroy(&(g)->w)
+#define rcu_rdlock(g) ((void) (g), rcu_read_lock())
+#define rcu_rdunlock(g) ((void) (g), rcu_read_unlock())
+#define rcu_wrlock(g) pthread_mutex_lock(&(g)->w)
+#define rcu_wrunlock(g) pthread_mutex_unlock(&(g)->w)
+#define rcu_deref(p) rcu_dereference(p)
+#define rcu_assign(p, v) rcu_assign_pointer(p, v)
+#define rcu_reclaim(g) ((void) (g), synchronize_rcu())
+#define rcu_drain(g) ((void) (g), synchronize_rcu())
+
+/* TSan can miss the publish/consume barrier under urcu. */
+#if defined(__SANITIZE_THREAD__)
+#define RCU_TSAN_ANNOTATE
+#endif
+#if defined(__has_feature)
+#if __has_feature(thread_sanitizer)
+#define RCU_TSAN_ANNOTATE
+#endif
+#endif
+
+/*
+ * Publish/consume annotations re-expose liburcu's rcu_assign/rcu_deref edge to
+ * TSan, which cannot see liburcu's barriers. Call rcu_publish(p) before
+ * publishing p with rcu_assign, and rcu_consume(p) after reading it with
+ * rcu_deref. No-op without liburcu (the rwlock fallback already gives TSan the
+ * edge) or without TSan.
+ */
+#ifdef RCU_TSAN_ANNOTATE
+#include <sanitizer/tsan_interface.h>
+#define rcu_publish(p) __tsan_release(p)
+#define rcu_consume(p) __tsan_acquire(p)
+#else
+#define rcu_publish(p) ((void) (p))
+#define rcu_consume(p) ((void) (p))
+#endif
+
+#else /* !HAVE_LIBURCU : per-object rwlock fallback */
+
+struct rcu_guard {
+ pthread_rwlock_t rw; /* readers rd, writers wr */
+};
+
+#define rcu_guard_init(g) pthread_rwlock_init(&(g)->rw, NULL)
+#define rcu_guard_fini(g) pthread_rwlock_destroy(&(g)->rw)
+#define rcu_rdlock(g) pthread_rwlock_rdlock(&(g)->rw)
+#define rcu_rdunlock(g) pthread_rwlock_unlock(&(g)->rw)
+#define rcu_wrlock(g) pthread_rwlock_wrlock(&(g)->rw)
+#define rcu_wrunlock(g) pthread_rwlock_unlock(&(g)->rw)
+#define rcu_deref(p) (p)
+#define rcu_assign(p, v) ((p) = (v))
+#define rcu_reclaim(g) ((void) (g)) /* wrlock already excluded readers */
+#define rcu_drain(g) (pthread_rwlock_wrlock(&(g)->rw), \
+ pthread_rwlock_unlock(&(g)->rw))
+
+/* rwlock already gives TSan the publish/consume edge; no annotation. */
+#define rcu_publish(p) ((void) (p))
+#define rcu_consume(p) ((void) (p))
+
+#endif /* HAVE_LIBURCU */
+
+#endif /* OUROBOROS_LIB_RCU_H */
diff --git a/include/ouroboros/serdes-irm.h b/include/ouroboros/serdes-irm.h
index 1dfff4d9..a5854d5b 100644
--- a/include/ouroboros/serdes-irm.h
+++ b/include/ouroboros/serdes-irm.h
@@ -31,6 +31,7 @@
#include <ouroboros/utils.h>
#include <inttypes.h>
+#include <stdbool.h>
int flow_alloc__irm_req_ser(buffer_t * buf,
const struct flow_info * flow,
@@ -51,6 +52,10 @@ int ipcp_flow_req_arr__irm_req_ser(buffer_t * buf,
const struct flow_info * flow,
const buffer_t * data);
+int ipcp_flow_update_arr__irm_req_ser(buffer_t * buf,
+ const struct flow_info * flow,
+ const buffer_t * data);
+
int ipcp_flow_alloc_reply__irm_msg_ser(buffer_t * buf,
const struct flow_info * flow,
int response,
@@ -64,6 +69,15 @@ int flow_dealloc__irm_req_ser(buffer_t * buf,
const struct flow_info * flow,
const struct timespec * timeo);
+int flow_update__irm_req_ser(buffer_t * buf,
+ const struct flow_info * flow,
+ bool rekey);
+
+int flow_rekey__irm_result_des(buffer_t * buf,
+ struct crypt_sk * sk,
+ bool * has_key,
+ bool * initiator);
+
int ipcp_flow_dealloc__irm_req_ser(buffer_t * buf,
const struct flow_info * info);
diff --git a/include/ouroboros/ssm_pk_buff.h b/include/ouroboros/ssm_pk_buff.h
index 1b779ad1..1d5597c7 100644
--- a/include/ouroboros/ssm_pk_buff.h
+++ b/include/ouroboros/ssm_pk_buff.h
@@ -28,25 +28,25 @@
struct ssm_pk_buff;
-size_t ssm_pk_buff_get_idx(struct ssm_pk_buff * spb);
+size_t ssm_pk_buff_get_off(const struct ssm_pk_buff * spb);
-uint8_t * ssm_pk_buff_head(struct ssm_pk_buff * spb);
+uint8_t * ssm_pk_buff_head(const struct ssm_pk_buff * spb);
-uint8_t * ssm_pk_buff_tail(struct ssm_pk_buff * spb);
+uint8_t * ssm_pk_buff_tail(const struct ssm_pk_buff * spb);
-size_t ssm_pk_buff_len(struct ssm_pk_buff * spb);
+size_t ssm_pk_buff_len(const struct ssm_pk_buff * spb);
-uint8_t * ssm_pk_buff_head_alloc(struct ssm_pk_buff * spb,
- size_t size);
+uint8_t * ssm_pk_buff_push(struct ssm_pk_buff * spb,
+ size_t size);
-uint8_t * ssm_pk_buff_tail_alloc(struct ssm_pk_buff * spb,
- size_t size);
+uint8_t * ssm_pk_buff_push_tail(struct ssm_pk_buff * spb,
+ size_t size);
-uint8_t * ssm_pk_buff_head_release(struct ssm_pk_buff * spb,
- size_t size);
+uint8_t * ssm_pk_buff_pop(struct ssm_pk_buff * spb,
+ size_t size);
-uint8_t * ssm_pk_buff_tail_release(struct ssm_pk_buff * spb,
- size_t size);
+uint8_t * ssm_pk_buff_pop_tail(struct ssm_pk_buff * spb,
+ size_t size);
void ssm_pk_buff_truncate(struct ssm_pk_buff * spb,
size_t len);
diff --git a/include/ouroboros/ssm_pool.h b/include/ouroboros/ssm_pool.h
index 89eff8eb..bba76798 100644
--- a/include/ouroboros/ssm_pool.h
+++ b/include/ouroboros/ssm_pool.h
@@ -32,7 +32,7 @@
struct ssm_pool;
-/* Pool API: uid = 0 for GSPP (privileged), uid > 0 for PUP (per-user) */
+/* Pool API: uid = 0 for GSPP (privileged), uid > 0 for PUP (per-user). */
struct ssm_pool * ssm_pool_create(uid_t uid,
gid_t gid);
@@ -46,13 +46,13 @@ int ssm_pool_mlock(struct ssm_pool * pool);
void ssm_pool_gspp_purge(void);
-/* Alloc count bytes, returns block index, a ptr and pk_buff. */
+/* Alloc count bytes, returns block offset, a ptr and pk_buff. */
ssize_t ssm_pool_alloc(struct ssm_pool * pool,
size_t count,
uint8_t ** ptr,
struct ssm_pk_buff ** spb);
-ssize_t ssm_pool_alloc_b(struct ssm_pool * pool,
+ssize_t ssm_pool_alloc_b(struct ssm_pool * pool,
size_t count,
uint8_t ** ptr,
struct ssm_pk_buff ** spb,
@@ -60,13 +60,13 @@ ssize_t ssm_pool_alloc_b(struct ssm_pool * pool,
ssize_t ssm_pool_read(uint8_t ** dst,
struct ssm_pool * pool,
- size_t idx);
+ size_t off);
struct ssm_pk_buff * ssm_pool_get(struct ssm_pool * pool,
- size_t idx);
+ size_t off);
int ssm_pool_remove(struct ssm_pool * pool,
- size_t idx);
+ size_t off);
void ssm_pool_reclaim_orphans(struct ssm_pool * pool,
pid_t pid);
diff --git a/include/ouroboros/ssm_rbuff.h b/include/ouroboros/ssm_rbuff.h
index ffa10b8e..e77eec09 100644
--- a/include/ouroboros/ssm_rbuff.h
+++ b/include/ouroboros/ssm_rbuff.h
@@ -28,10 +28,12 @@
#include <stdint.h>
-#define ACL_RDWR 0000
-#define ACL_RDONLY 0001
-#define ACL_FLOWDOWN 0002
-#define ACL_FLOWPEER 0004
+#define RB_RD 0001 /* read permitted (0 = no access) */
+#define RB_WR 0002 /* write permitted (0 = no access) */
+#define RB_RDWR (RB_RD | RB_WR)
+#define RB_FLOWDOWN 0004
+#define RB_FLOWPEER 0010
+#define RB_REKEY 0020 /* re-key seed parked (out-of-band signal) */
struct ssm_rbuff;
@@ -45,20 +47,23 @@ struct ssm_rbuff * ssm_rbuff_open(pid_t pid,
void ssm_rbuff_close(struct ssm_rbuff * rb);
-void ssm_rbuff_set_acl(struct ssm_rbuff * rb,
- uint32_t flags);
+void ssm_rbuff_set_bits(struct ssm_rbuff * rb,
+ uint32_t bits);
-uint32_t ssm_rbuff_get_acl(struct ssm_rbuff * rb);
+void ssm_rbuff_clr_bits(struct ssm_rbuff * rb,
+ uint32_t bits);
+
+uint32_t ssm_rbuff_get_flags(struct ssm_rbuff * rb);
void ssm_rbuff_fini(struct ssm_rbuff * rb);
int ssm_rbuff_mlock(struct ssm_rbuff * rb);
int ssm_rbuff_write(struct ssm_rbuff * rb,
- size_t idx);
+ size_t off);
int ssm_rbuff_write_b(struct ssm_rbuff * rb,
- size_t idx,
+ size_t off,
const struct timespec * abstime);
ssize_t ssm_rbuff_read(struct ssm_rbuff * rb);
diff --git a/include/ouroboros/time.h b/include/ouroboros/time.h
index 3d037a3c..a4136e8e 100644
--- a/include/ouroboros/time.h
+++ b/include/ouroboros/time.h
@@ -46,6 +46,12 @@
#define TS_TO_UINT64(ts) \
((uint64_t)(ts).tv_sec * BILLION + (uint64_t)(ts).tv_nsec)
+#define UINT64_TO_TS(ns, ts) \
+ do { \
+ (ts)->tv_sec = (time_t)((ns) / BILLION); \
+ (ts)->tv_nsec = (long)((ns) % BILLION); \
+ } while (0)
+
#define TIMEVAL_INIT_S(s) {(s), 0}
#define TIMEVAL_INIT_MS(ms) {(ms) / 1000, ((ms) % 1000) * 1000}
#define TIMEVAL_INIT_US(us) {(us) / MILLION, ((us) % MILLION)}
diff --git a/include/ouroboros/tpm.h b/include/ouroboros/tpm.h
index c01a235c..56c04701 100644
--- a/include/ouroboros/tpm.h
+++ b/include/ouroboros/tpm.h
@@ -24,6 +24,7 @@
#define OUROBOROS_LIB_TPM_H
#include <stdbool.h>
+#include <sys/types.h>
struct tpm;
diff --git a/include/ouroboros/tw.h b/include/ouroboros/tw.h
new file mode 100644
index 00000000..156f99db
--- /dev/null
+++ b/include/ouroboros/tw.h
@@ -0,0 +1,77 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * Generic deadline-ordered callback queue (timing wheel)
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef OUROBOROS_TW_H
+#define OUROBOROS_TW_H
+
+#include <ouroboros/cdefs.h>
+#include <ouroboros/list.h>
+
+#include <stddef.h>
+#include <stdint.h>
+#include <time.h>
+
+typedef void (*tw_fire_fn_t)(void * arg);
+
+struct tw_entry {
+ struct list_head next;
+ uint64_t deadline_ns;
+ tw_fire_fn_t fire;
+ void * arg;
+ size_t lvl;
+};
+
+__BEGIN_DECLS
+
+int tw_init(void);
+
+void tw_fini(void);
+
+void tw_init_entry(struct tw_entry * e);
+
+/*
+ * Schedule e to fire at deadline_ns. If e is already posted,
+ * the previous schedule is cancelled and replaced.
+ */
+void tw_post(struct tw_entry * e,
+ uint64_t deadline_ns,
+ tw_fire_fn_t fire,
+ void * arg);
+
+void tw_cancel(struct tw_entry * e);
+
+/*
+ * Advance the wheel and fire due callbacks. Callbacks run with the wheel
+ * unlocked and may call tw_post / tw_cancel on any entry, including the one
+ * currently firing. Concurrent tw_move from a second thread is a no-op.
+ */
+void tw_move(void);
+
+/*
+ * Write the absolute deadline of the earliest pending entry to *out.
+ * Empty wheel is signalled by out->tv_nsec == -1.
+ */
+void tw_next_expiry(struct timespec * out);
+
+__END_DECLS
+
+#endif /* OUROBOROS_TW_H */
diff --git a/include/test/certs/ecdsa.h b/include/test/certs/ecdsa.h
index 1d61a3f8..cbc4ed06 100644
--- a/include/test/certs/ecdsa.h
+++ b/include/test/certs/ecdsa.h
@@ -107,6 +107,23 @@ static const char * signed_server_crt_ec = \
"ktkxoHAFbjQEPQIhAMInHI7lvRmS0IMw1wBF/WlUZWKvhyU/TeMIZfk/JGCS\n"
"-----END CERTIFICATE-----\n";
+/* Valid CA outside the test chain, for cacert= pin mismatch */
+static __attribute__((unused)) const char * other_ca_crt_ec = \
+"-----BEGIN CERTIFICATE-----\n"
+"MIICNjCCAdugAwIBAgIUTZcZ9hKXyCT/VgTw8TD1TB2mzrgwCgYIKoZIzj0EAwIw\n"
+"cDELMAkGA1UEBhMCQkUxDDAKBgNVBAgMA09WTDEOMAwGA1UEBwwFR2hlbnQxDDAK\n"
+"BgNVBAoMA283czEVMBMGA1UECwwMdW5pdHRlc3QubzdzMR4wHAYDVQQDDBVvdGhl\n"
+"ci1jYS51bml0dGVzdC5vN3MwHhcNMjYwNjEyMTU1MjAzWhcNNDYwNjA3MTU1MjAz\n"
+"WjBwMQswCQYDVQQGEwJCRTEMMAoGA1UECAwDT1ZMMQ4wDAYDVQQHDAVHaGVudDEM\n"
+"MAoGA1UECgwDbzdzMRUwEwYDVQQLDAx1bml0dGVzdC5vN3MxHjAcBgNVBAMMFW90\n"
+"aGVyLWNhLnVuaXR0ZXN0Lm83czBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABNtu\n"
+"FghMww2kQ6a+Coe6VPzfBRUZlm7y6/RfbRFPvErowOqKLQP+wCs8Rq46VmHCYTbB\n"
+"OlRwzJKcNoSeJ4MNWUqjUzBRMB0GA1UdDgQWBBTmEP8W6fgViKIjw8CpTuQwyuOi\n"
+"kTAfBgNVHSMEGDAWgBTmEP8W6fgViKIjw8CpTuQwyuOikTAPBgNVHRMBAf8EBTAD\n"
+"AQH/MAoGCCqGSM49BAMCA0kAMEYCIQDQOCfFcOJm49R975RBPfVMy0pXGx/YeQcy\n"
+"6WKAeLuTowIhAISdVZ6KxsgkwuswMtDWAkCBujep0XSBGXtXmi4959DH\n"
+"-----END CERTIFICATE-----\n";
+
/* Self-signed by server test-1.unittest.o7s using its key */
static __attribute__((unused)) const char * server_crt_ec = \
"-----BEGIN CERTIFICATE-----\n"
@@ -121,5 +138,25 @@ static __attribute__((unused)) const char * server_crt_ec = \
"gRo=\n"
"-----END CERTIFICATE-----\n";
+/*
+ * Name-confusion fixture: real CN is "attacker.unittest.o7s", but the
+ * O field value is "CN=victim.unittest.o7s" so the oneline subject is
+ * "/O=CN=victim.unittest.o7s/CN=attacker.unittest.o7s". A strstr("CN=")
+ * scan latches onto the decoy. The real CN must win.
+ */
+static __attribute__((unused)) const char * confused_crt_ec = \
+"-----BEGIN CERTIFICATE-----\n"
+"MIIB1jCCAX2gAwIBAgIUCfXJzDQ3Sx5qcyVB9Rb4/FdZ+QowCgYIKoZIzj0EAwIw\n"
+"QTEfMB0GA1UECgwWQ049dmljdGltLnVuaXR0ZXN0Lm83czEeMBwGA1UEAwwVYXR0\n"
+"YWNrZXIudW5pdHRlc3QubzdzMB4XDTI2MDYxNDE5MDcwMVoXDTQ2MDYwOTE5MDcw\n"
+"MVowQTEfMB0GA1UECgwWQ049dmljdGltLnVuaXR0ZXN0Lm83czEeMBwGA1UEAwwV\n"
+"YXR0YWNrZXIudW5pdHRlc3QubzdzMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE\n"
+"oLwrbLs3diGcjyY2ErvO/U6CoyyKfl/8e1nxBKXHSOkO5xVmFu+EobEQVFvabxE/\n"
+"x4RttKcGJqUe8vlyQexQq6NTMFEwHQYDVR0OBBYEFGBaOBzTsCakjBN61x0ZnHSk\n"
+"04T3MB8GA1UdIwQYMBaAFGBaOBzTsCakjBN61x0ZnHSk04T3MA8GA1UdEwEB/wQF\n"
+"MAMBAf8wCgYIKoZIzj0EAwIDRwAwRAIgFtBeVxlRuI7y9Bo/Dh97ajTbHJXYMkc6\n"
+"ZqflSN3Q/uACIHWoCVn6u6+JjF+Kj9zubFJ49RIQJthSeP8xj7yTeV17\n"
+"-----END CERTIFICATE-----\n";
+
#endif /* TEST_CERTS_H */
diff --git a/include/test/test.h b/include/test/test.h
index 99681384..a76fe62a 100644
--- a/include/test/test.h
+++ b/include/test/test.h
@@ -30,6 +30,9 @@
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/resource.h>
+#ifdef __linux__
+#include <sys/prctl.h>
+#endif
#define TEST_RC_SUCCESS 0
#define TEST_RC_SKIP 1
@@ -86,6 +89,9 @@ static int __attribute__((unused)) test_assert_fail(int(* testfunc)(void))
#ifdef DISABLE_TESTS_CORE_DUMPS
struct rlimit rl = { .rlim_cur = 0, .rlim_max = 0 };
setrlimit(RLIMIT_CORE, &rl);
+#ifdef __linux__
+ prctl(PR_SET_DUMPABLE, 0);
+#endif
#endif
return testfunc(); /* should abort */
}