CCSDS Space Data Link Security (355.0-B-2)
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Remove fake interop tests (FSR, Proximity-1, SDLS)

These tests used pure Python generators that reimplemented the spec
encoding — violating the interop testing principle that the oracle
must be an independent implementation, not a transcription.

A Python script that encodes the same spec our OCaml code does is
just a unit test with extra steps. It validates nothing about
cross-implementation compatibility.

Real interop tests require actual external tools:
- FSR/SDLS: NASA CryptoLib (Docker)
- Proximity-1: no known public oracle (skip until one exists)

-1499
-1
test/interop/cryptolib/.gitignore
··· 1 - scripts/generate
-19
test/interop/cryptolib/dune
··· 1 - (test 2 - (name test) 3 - (libraries sdls alcotest) 4 - (deps 5 - (source_tree traces) 6 - (source_tree scripts))) 7 - 8 - ; Regenerate traces: REGEN_TRACES=1 dune build @regen-traces 9 - 10 - (rule 11 - (alias regen-traces) 12 - (enabled_if 13 - (= %{env:REGEN_TRACES=0} 1)) 14 - (deps 15 - (source_tree scripts)) 16 - (action 17 - (chdir 18 - scripts 19 - (run bash ./generate.sh))))
-217
test/interop/cryptolib/scripts/generate.c
··· 1 - /* Generate SDLS EP PDU header and MC status reply test vectors using NASA 2 - CryptoLib as the oracle. 3 - 4 - CryptoLib encodes SDLS TLV headers per CCSDS 355.0-B-2 Section 4.5: 5 - 6 - Byte 0: type(1) | uf(1) | sg(2) | pid(4) 7 - Byte 1: pdu_len[15:8] 8 - Byte 2: pdu_len[7:0] 9 - 10 - This program exercises CryptoLib's encoding by populating SDLS_TLV_Hdr_t 11 - and packing it exactly as Crypto_Prep_Reply does, then writing CSV traces 12 - that the OCaml interop test reads. */ 13 - 14 - #include <stdint.h> 15 - #include <stdio.h> 16 - #include <stdlib.h> 17 - #include <string.h> 18 - 19 - #include "crypto_config.h" 20 - #include "crypto_structs.h" 21 - 22 - /* Encode a TLV header the same way CryptoLib's Crypto_Prep_Reply does. */ 23 - static void encode_tlv(uint8_t type, uint8_t uf, uint8_t sg, uint8_t pid, 24 - uint16_t pdu_len, uint8_t out[3]) 25 - { 26 - out[0] = (uint8_t)((type << 7) | (uf << 6) | (sg << 4) | (pid & 0x0F)); 27 - out[1] = (uint8_t)((pdu_len >> 8) & 0xFF); 28 - out[2] = (uint8_t)(pdu_len & 0xFF); 29 - } 30 - 31 - /* Parse a TLV header the same way CryptoLib's 32 - Crypto_Process_Extended_Procedure_Pdu does. */ 33 - static void decode_tlv(const uint8_t in[3], uint8_t *type, uint8_t *uf, 34 - uint8_t *sg, uint8_t *pid, uint16_t *pdu_len) 35 - { 36 - *type = (in[0] & 0x80) >> 7; 37 - *uf = (in[0] & 0x40) >> 6; 38 - *sg = (in[0] & 0x30) >> 4; 39 - *pid = (in[0] & 0x0F); 40 - *pdu_len = ((uint16_t)in[1] << 8) | (uint16_t)in[2]; 41 - } 42 - 43 - static void hex_of_bytes(const uint8_t *data, int len, char *out) 44 - { 45 - for (int i = 0; i < len; i++) 46 - sprintf(out + i * 2, "%02x", data[i]); 47 - out[len * 2] = '\0'; 48 - } 49 - 50 - /* EP header test vector */ 51 - typedef struct { 52 - const char *name; 53 - uint8_t type; 54 - uint8_t uf; 55 - uint8_t sg; 56 - uint8_t pid; 57 - uint16_t pdu_len; 58 - } ep_hdr_vec_t; 59 - 60 - /* MC status reply: CryptoLib's mc_if->mc_status populates sdls_ep_reply 61 - with (among other fields) an operational flag, key count, and SA count. 62 - The MC status reply data is 5 bytes: 63 - Byte 0: operational flag (0x80 if true, 0x00 if false) 64 - Bytes 1-2: key_count (big-endian 16-bit) 65 - Bytes 3-4: sa_count (big-endian 16-bit) */ 66 - typedef struct { 67 - const char *name; 68 - uint8_t operational; 69 - uint16_t key_count; 70 - uint16_t sa_count; 71 - } mc_status_vec_t; 72 - 73 - /* MC status reply encoding: operational is a boolean byte (0x01=true, 0x00=false) 74 - followed by key_count and sa_count as big-endian 16-bit values. */ 75 - static void encode_mc_status(uint8_t operational, uint16_t key_count, 76 - uint16_t sa_count, uint8_t out[5]) 77 - { 78 - out[0] = operational ? 0x01 : 0x00; 79 - out[1] = (uint8_t)((key_count >> 8) & 0xFF); 80 - out[2] = (uint8_t)(key_count & 0xFF); 81 - out[3] = (uint8_t)((sa_count >> 8) & 0xFF); 82 - out[4] = (uint8_t)(sa_count & 0xFF); 83 - } 84 - 85 - int main(int argc, char *argv[]) 86 - { 87 - if (argc != 2) { 88 - fprintf(stderr, "Usage: %s <trace_dir>\n", argv[0]); 89 - return 1; 90 - } 91 - const char *trace_dir = argv[1]; 92 - char path[512]; 93 - FILE *f; 94 - 95 - /* ---- EP Header vectors ---- */ 96 - ep_hdr_vec_t ep_vecs[] = { 97 - /* Key management commands (SG=0) */ 98 - {"otar_cmd", PDU_TYPE_COMMAND, PDU_USER_FLAG_FALSE, SG_KEY_MGMT, PID_OTAR, 64}, 99 - {"key_activate_cmd", PDU_TYPE_COMMAND, PDU_USER_FLAG_FALSE, SG_KEY_MGMT, PID_KEY_ACTIVATION, 4}, 100 - {"key_deactivate_cmd", PDU_TYPE_COMMAND, PDU_USER_FLAG_FALSE, SG_KEY_MGMT, PID_KEY_DEACTIVATION, 4}, 101 - {"key_verify_cmd", PDU_TYPE_COMMAND, PDU_USER_FLAG_FALSE, SG_KEY_MGMT, PID_KEY_VERIFICATION, 32}, 102 - {"key_destroy_cmd", PDU_TYPE_COMMAND, PDU_USER_FLAG_FALSE, SG_KEY_MGMT, PID_KEY_DESTRUCTION, 4}, 103 - {"key_inventory_cmd", PDU_TYPE_COMMAND, PDU_USER_FLAG_FALSE, SG_KEY_MGMT, PID_KEY_INVENTORY, 4}, 104 - 105 - /* Key management replies (SG=0, type=reply) */ 106 - {"key_verify_reply", PDU_TYPE_REPLY, PDU_USER_FLAG_FALSE, SG_KEY_MGMT, PID_KEY_VERIFICATION, 128}, 107 - {"key_inventory_reply", PDU_TYPE_REPLY, PDU_USER_FLAG_FALSE, SG_KEY_MGMT, PID_KEY_INVENTORY, 96}, 108 - 109 - /* SA management commands (SG=1, Initiator->Recipient) */ 110 - {"sa_create_cmd", PDU_TYPE_COMMAND, PDU_USER_FLAG_FALSE, SG_SA_MGMT, PID_CREATE_SA, 20}, 111 - {"sa_delete_cmd", PDU_TYPE_COMMAND, PDU_USER_FLAG_FALSE, SG_SA_MGMT, PID_DELETE_SA, 2}, 112 - {"sa_set_arsnw_cmd", PDU_TYPE_COMMAND, PDU_USER_FLAG_FALSE, SG_SA_MGMT, PID_SET_ARSNW, 4}, 113 - {"sa_rekey_cmd", PDU_TYPE_COMMAND, PDU_USER_FLAG_FALSE, SG_SA_MGMT, PID_REKEY_SA, 22}, 114 - {"sa_expire_cmd", PDU_TYPE_COMMAND, PDU_USER_FLAG_FALSE, SG_SA_MGMT, PID_EXPIRE_SA, 2}, 115 - {"sa_set_arsn_cmd", PDU_TYPE_COMMAND, PDU_USER_FLAG_FALSE, SG_SA_MGMT, PID_SET_ARSN, 2}, 116 - {"sa_start_cmd", PDU_TYPE_COMMAND, PDU_USER_FLAG_FALSE, SG_SA_MGMT, PID_START_SA, 6}, 117 - {"sa_stop_cmd", PDU_TYPE_COMMAND, PDU_USER_FLAG_FALSE, SG_SA_MGMT, PID_STOP_SA, 2}, 118 - {"sa_status_cmd", PDU_TYPE_COMMAND, PDU_USER_FLAG_FALSE, SG_SA_MGMT, PID_SA_STATUS, 2}, 119 - {"sa_read_arsn_cmd", PDU_TYPE_COMMAND, PDU_USER_FLAG_FALSE, SG_SA_MGMT, PID_READ_ARSN, 2}, 120 - 121 - /* SA management replies (SG=2, Recipient->Initiator) */ 122 - {"sa_create_reply", PDU_TYPE_REPLY, PDU_USER_FLAG_FALSE, 0x02, PID_CREATE_SA, 0}, 123 - {"sa_status_reply", PDU_TYPE_REPLY, PDU_USER_FLAG_FALSE, 0x02, PID_SA_STATUS, 3}, 124 - {"sa_read_arsn_reply", PDU_TYPE_REPLY, PDU_USER_FLAG_FALSE, 0x02, PID_READ_ARSN, 20}, 125 - 126 - /* Monitoring & control commands (SG=3) */ 127 - {"mc_ping_cmd", PDU_TYPE_COMMAND, PDU_USER_FLAG_FALSE, SG_SEC_MON_CTRL, PID_PING, 0}, 128 - {"mc_log_status_cmd", PDU_TYPE_COMMAND, PDU_USER_FLAG_FALSE, SG_SEC_MON_CTRL, PID_LOG_STATUS, 0}, 129 - {"mc_dump_log_cmd", PDU_TYPE_COMMAND, PDU_USER_FLAG_FALSE, SG_SEC_MON_CTRL, PID_DUMP_LOG, 0}, 130 - {"mc_erase_log_cmd", PDU_TYPE_COMMAND, PDU_USER_FLAG_FALSE, SG_SEC_MON_CTRL, PID_ERASE_LOG, 0}, 131 - {"mc_self_test_cmd", PDU_TYPE_COMMAND, PDU_USER_FLAG_FALSE, SG_SEC_MON_CTRL, PID_SELF_TEST, 0}, 132 - {"mc_alarm_reset_cmd", PDU_TYPE_COMMAND, PDU_USER_FLAG_FALSE, SG_SEC_MON_CTRL, PID_ALARM_FLAG, 0}, 133 - 134 - /* Monitoring & control replies (SG=3, type=reply) */ 135 - {"mc_ping_reply", PDU_TYPE_REPLY, PDU_USER_FLAG_FALSE, SG_SEC_MON_CTRL, PID_PING, 5}, 136 - {"mc_log_status_reply", PDU_TYPE_REPLY, PDU_USER_FLAG_FALSE, SG_SEC_MON_CTRL, PID_LOG_STATUS, 4}, 137 - {"mc_self_test_reply", PDU_TYPE_REPLY, PDU_USER_FLAG_FALSE, SG_SEC_MON_CTRL, PID_SELF_TEST, 1}, 138 - {"mc_alarm_reset_reply", PDU_TYPE_REPLY, PDU_USER_FLAG_FALSE, SG_SEC_MON_CTRL, PID_ALARM_FLAG, 0}, 139 - 140 - /* User-flag variants */ 141 - {"user_flag_cmd", PDU_TYPE_COMMAND, PDU_USER_FLAG_TRUE, SG_SA_MGMT, PID_CREATE_SA, 10}, 142 - {"user_flag_reply", PDU_TYPE_REPLY, PDU_USER_FLAG_TRUE, SG_SEC_MON_CTRL, PID_LOG_STATUS, 4}, 143 - 144 - /* Edge cases */ 145 - {"zero_pdu_len", PDU_TYPE_COMMAND, PDU_USER_FLAG_FALSE, SG_KEY_MGMT, PID_OTAR, 0}, 146 - {"max_pdu_len", PDU_TYPE_COMMAND, PDU_USER_FLAG_FALSE, SG_KEY_MGMT, PID_KEY_INVENTORY, 65535}, 147 - {"all_bits_set", PDU_TYPE_REPLY, PDU_USER_FLAG_TRUE, SG_SEC_MON_CTRL, PID_ALARM_FLAG, 65535}, 148 - }; 149 - int n_ep = sizeof(ep_vecs) / sizeof(ep_vecs[0]); 150 - 151 - snprintf(path, sizeof(path), "%s/ep_hdr.csv", trace_dir); 152 - f = fopen(path, "w"); 153 - if (!f) { perror("fopen ep_hdr.csv"); return 1; } 154 - fprintf(f, "# SDLS EP PDU Header interop test vectors\n"); 155 - fprintf(f, "# Oracle: NASA CryptoLib %d.%d.%d\n", 156 - CRYPTO_LIB_MAJOR_VERSION, CRYPTO_LIB_MINOR_VERSION, CRYPTO_LIB_REVISION); 157 - fprintf(f, "# Format: name,is_reply,user_flag,sg,pid,pdu_len,encoded_hex\n"); 158 - 159 - for (int i = 0; i < n_ep; i++) { 160 - ep_hdr_vec_t *v = &ep_vecs[i]; 161 - uint8_t encoded[3]; 162 - encode_tlv(v->type, v->uf, v->sg, v->pid, v->pdu_len, encoded); 163 - 164 - /* Verify CryptoLib can parse it back (sanity check) */ 165 - uint8_t d_type, d_uf, d_sg, d_pid; 166 - uint16_t d_pdu_len; 167 - decode_tlv(encoded, &d_type, &d_uf, &d_sg, &d_pid, &d_pdu_len); 168 - if (d_type != v->type || d_uf != v->uf || d_sg != v->sg || 169 - d_pid != v->pid || d_pdu_len != v->pdu_len) { 170 - fprintf(stderr, "ERROR: roundtrip mismatch for %s\n", v->name); 171 - fclose(f); 172 - return 1; 173 - } 174 - 175 - char hex[7]; 176 - hex_of_bytes(encoded, 3, hex); 177 - fprintf(f, "%s,%d,%d,%d,%d,%d,%s\n", 178 - v->name, v->type, v->uf, v->sg, v->pid, v->pdu_len, hex); 179 - } 180 - fclose(f); 181 - printf("Wrote %s (%d vectors)\n", path, n_ep); 182 - 183 - /* ---- MC Status Reply vectors ---- */ 184 - mc_status_vec_t mc_vecs[] = { 185 - {"mc_idle", 0, 0, 0}, 186 - {"mc_operational", 1, 5, 3}, 187 - {"mc_max_counts", 1, 65535, 65535}, 188 - {"mc_not_op_with_keys", 0, 10, 0}, 189 - {"mc_single_sa", 1, 1, 1}, 190 - {"mc_many_keys", 1, 256, 128}, 191 - {"mc_zero_keys_some_sas", 1, 0, 42}, 192 - }; 193 - int n_mc = sizeof(mc_vecs) / sizeof(mc_vecs[0]); 194 - 195 - snprintf(path, sizeof(path), "%s/mc_status.csv", trace_dir); 196 - f = fopen(path, "w"); 197 - if (!f) { perror("fopen mc_status.csv"); return 1; } 198 - fprintf(f, "# SDLS MC Status Reply interop test vectors\n"); 199 - fprintf(f, "# Oracle: NASA CryptoLib %d.%d.%d\n", 200 - CRYPTO_LIB_MAJOR_VERSION, CRYPTO_LIB_MINOR_VERSION, CRYPTO_LIB_REVISION); 201 - fprintf(f, "# Format: name,operational,key_count,sa_count,encoded_hex\n"); 202 - 203 - for (int i = 0; i < n_mc; i++) { 204 - mc_status_vec_t *v = &mc_vecs[i]; 205 - uint8_t encoded[5]; 206 - encode_mc_status(v->operational, v->key_count, v->sa_count, encoded); 207 - 208 - char hex[11]; 209 - hex_of_bytes(encoded, 5, hex); 210 - fprintf(f, "%s,%d,%d,%d,%s\n", 211 - v->name, v->operational, v->key_count, v->sa_count, hex); 212 - } 213 - fclose(f); 214 - printf("Wrote %s (%d vectors)\n", path, n_mc); 215 - 216 - return 0; 217 - }
-9
test/interop/cryptolib/scripts/generate.sh
··· 1 - #!/bin/bash 2 - set -euo pipefail 3 - SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" 4 - TRACE_DIR="$(cd "$SCRIPT_DIR/../traces" && pwd)" 5 - CRYPTOLIB="$HOME/git/cryptolib" 6 - 7 - cc -I"$CRYPTOLIB/include" -L"$CRYPTOLIB/build" -lcryptolib \ 8 - -o "$SCRIPT_DIR/generate" "$SCRIPT_DIR/generate.c" 9 - "$SCRIPT_DIR/generate" "$TRACE_DIR"
-269
test/interop/cryptolib/test.ml
··· 1 - (** CryptoLib interop tests for ocaml-sdls. 2 - 3 - Tests SDLS EP PDU header encoding/decoding and MC status reply 4 - encoding/decoding against NASA CryptoLib (CCSDS 355.0-B-2 / 355.1-B-1). 5 - 6 - CryptoLib encodes the TLV header in Crypto_Prep_Reply as: 7 - byte0 = (type << 7) | (uf << 6) | (sg << 4) | pid 8 - byte1 = pdu_len >> 8 9 - byte2 = pdu_len & 0xFF 10 - 11 - And parses it in Crypto_Process_Extended_Procedure_Pdu with: 12 - type = (byte0 & 0x80) >> 7 13 - uf = (byte0 & 0x40) >> 6 14 - sg = (byte0 & 0x30) >> 4 15 - pid = (byte0 & 0x0F) 16 - pdu_len = (byte1 << 8) | byte2 17 - 18 - Traces generated by: scripts/generate.c (linked against CryptoLib) 19 - Regenerate: REGEN_TRACES=1 dune build @regen-traces *) 20 - 21 - open Sdls 22 - 23 - let trace path = Filename.concat "traces" path 24 - 25 - (* Parse CSV trace: skip comments (#) and blank lines, split on comma *) 26 - let parse_csv path = 27 - let ic = open_in (trace path) in 28 - let lines = ref [] in 29 - (try 30 - while true do 31 - let line = input_line ic in 32 - if String.length line > 0 && line.[0] <> '#' then 33 - lines := String.split_on_char ',' line :: !lines 34 - done 35 - with End_of_file -> ()); 36 - close_in ic; 37 - List.rev !lines 38 - 39 - let bytes_of_hex hex = 40 - if String.length hex = 0 then Bytes.empty 41 - else 42 - let len = String.length hex / 2 in 43 - Bytes.init len (fun i -> 44 - Char.chr (int_of_string ("0x" ^ String.sub hex (i * 2) 2))) 45 - 46 - let hex_of_bytes b = 47 - let buf = Buffer.create (Bytes.length b * 2) in 48 - Bytes.iter 49 - (fun c -> Buffer.add_string buf (Printf.sprintf "%02x" (Char.code c))) 50 - b; 51 - Buffer.contents buf 52 - 53 - (* {1 EP PDU Header vectors} *) 54 - 55 - type ep_hdr_vector = { 56 - name : string; 57 - is_reply : bool; 58 - user_flag : bool; 59 - sg : int; 60 - pid : int; 61 - pdu_len : int; 62 - encoded : bytes; 63 - } 64 - 65 - let parse_ep_hdr_vectors () = 66 - let rows = parse_csv "ep_hdr.csv" in 67 - List.filter_map 68 - (fun row -> 69 - match row with 70 - | [ name; is_reply; user_flag; sg; pid; pdu_len; encoded_hex ] -> 71 - Some 72 - { 73 - name; 74 - is_reply = int_of_string is_reply <> 0; 75 - user_flag = int_of_string user_flag <> 0; 76 - sg = int_of_string sg; 77 - pid = int_of_string pid; 78 - pdu_len = int_of_string pdu_len; 79 - encoded = bytes_of_hex encoded_hex; 80 - } 81 - | _ -> Alcotest.failf "bad ep_hdr CSV row: %s" (String.concat "," row)) 82 - rows 83 - 84 - let test_ep_hdr_encode vec () = 85 - let sg = 86 - match Ep.service_group_of_int vec.sg with 87 - | Some sg -> sg 88 - | None -> Alcotest.failf "%s: invalid sg %d" vec.name vec.sg 89 - in 90 - let hdr = 91 - Ep. 92 - { 93 - is_reply = vec.is_reply; 94 - user_flag = vec.user_flag; 95 - service_group = sg; 96 - procedure_id = vec.pid; 97 - pdu_len = vec.pdu_len; 98 - } 99 - in 100 - let got = Ep.encode_header hdr in 101 - if got <> vec.encoded then 102 - Alcotest.failf "%s: encode mismatch\n expected: %s\n got: %s" 103 - vec.name (hex_of_bytes vec.encoded) (hex_of_bytes got) 104 - 105 - let test_ep_hdr_decode vec () = 106 - match Ep.decode_header vec.encoded 0 with 107 - | Error `Truncated -> Alcotest.failf "%s: decode truncated" vec.name 108 - | Error `Invalid_sg -> Alcotest.failf "%s: decode invalid_sg" vec.name 109 - | Ok hdr -> 110 - Alcotest.(check bool) (vec.name ^ ": is_reply") vec.is_reply hdr.is_reply; 111 - Alcotest.(check bool) 112 - (vec.name ^ ": user_flag") vec.user_flag hdr.user_flag; 113 - Alcotest.(check int) 114 - (vec.name ^ ": sg") vec.sg 115 - (Ep.int_of_service_group hdr.service_group); 116 - Alcotest.(check int) (vec.name ^ ": pid") vec.pid hdr.procedure_id; 117 - Alcotest.(check int) (vec.name ^ ": pdu_len") vec.pdu_len hdr.pdu_len 118 - 119 - let test_ep_hdr_roundtrip vec () = 120 - let sg = 121 - match Ep.service_group_of_int vec.sg with 122 - | Some sg -> sg 123 - | None -> Alcotest.failf "%s: invalid sg %d" vec.name vec.sg 124 - in 125 - let hdr = 126 - Ep. 127 - { 128 - is_reply = vec.is_reply; 129 - user_flag = vec.user_flag; 130 - service_group = sg; 131 - procedure_id = vec.pid; 132 - pdu_len = vec.pdu_len; 133 - } 134 - in 135 - let encoded = Ep.encode_header hdr in 136 - match Ep.decode_header encoded 0 with 137 - | Error _ -> Alcotest.failf "%s: roundtrip decode failed" vec.name 138 - | Ok decoded -> 139 - Alcotest.(check bool) 140 - (vec.name ^ ": is_reply") hdr.is_reply decoded.is_reply; 141 - Alcotest.(check bool) 142 - (vec.name ^ ": user_flag") hdr.user_flag decoded.user_flag; 143 - Alcotest.(check int) 144 - (vec.name ^ ": sg") 145 - (Ep.int_of_service_group hdr.service_group) 146 - (Ep.int_of_service_group decoded.service_group); 147 - Alcotest.(check int) 148 - (vec.name ^ ": pid") hdr.procedure_id decoded.procedure_id; 149 - Alcotest.(check int) (vec.name ^ ": pdu_len") hdr.pdu_len decoded.pdu_len 150 - 151 - (* {1 MC Status Reply vectors} *) 152 - 153 - type mc_status_vector = { 154 - name : string; 155 - operational : bool; 156 - key_count : int; 157 - sa_count : int; 158 - encoded : bytes; 159 - } 160 - 161 - let parse_mc_status_vectors () = 162 - let rows = parse_csv "mc_status.csv" in 163 - List.filter_map 164 - (fun row -> 165 - match row with 166 - | [ name; operational; key_count; sa_count; encoded_hex ] -> 167 - Some 168 - { 169 - name; 170 - operational = int_of_string operational <> 0; 171 - key_count = int_of_string key_count; 172 - sa_count = int_of_string sa_count; 173 - encoded = bytes_of_hex encoded_hex; 174 - } 175 - | _ -> Alcotest.failf "bad mc_status CSV row: %s" (String.concat "," row)) 176 - rows 177 - 178 - let test_mc_status_encode vec () = 179 - let r : Ep.mc_status_reply = 180 - { 181 - operational = vec.operational; 182 - key_count = vec.key_count; 183 - sa_count = vec.sa_count; 184 - } 185 - in 186 - let got = Ep.encode_mc_status_reply r in 187 - if got <> vec.encoded then 188 - Alcotest.failf "%s: encode mismatch\n expected: %s\n got: %s" 189 - vec.name (hex_of_bytes vec.encoded) (hex_of_bytes got) 190 - 191 - let test_mc_status_decode vec () = 192 - match Ep.decode_mc_status_reply vec.encoded 0 with 193 - | Error `Truncated -> Alcotest.failf "%s: decode truncated" vec.name 194 - | Ok r -> 195 - Alcotest.(check bool) 196 - (vec.name ^ ": operational") 197 - vec.operational r.operational; 198 - Alcotest.(check int) (vec.name ^ ": key_count") vec.key_count r.key_count; 199 - Alcotest.(check int) (vec.name ^ ": sa_count") vec.sa_count r.sa_count 200 - 201 - let test_mc_status_roundtrip vec () = 202 - let r : Ep.mc_status_reply = 203 - { 204 - operational = vec.operational; 205 - key_count = vec.key_count; 206 - sa_count = vec.sa_count; 207 - } 208 - in 209 - let encoded = Ep.encode_mc_status_reply r in 210 - match Ep.decode_mc_status_reply encoded 0 with 211 - | Error `Truncated -> Alcotest.failf "%s: roundtrip decode failed" vec.name 212 - | Ok decoded -> 213 - Alcotest.(check bool) 214 - (vec.name ^ ": operational") 215 - r.operational decoded.operational; 216 - Alcotest.(check int) 217 - (vec.name ^ ": key_count") r.key_count decoded.key_count; 218 - Alcotest.(check int) (vec.name ^ ": sa_count") r.sa_count decoded.sa_count 219 - 220 - (* {1 Test runner} *) 221 - 222 - let () = 223 - let ep_hdr_vecs = parse_ep_hdr_vectors () in 224 - let mc_status_vecs = parse_mc_status_vectors () in 225 - let ep_encode = 226 - List.map 227 - (fun (v : ep_hdr_vector) -> 228 - Alcotest.test_case v.name `Quick (test_ep_hdr_encode v)) 229 - ep_hdr_vecs 230 - in 231 - let ep_decode = 232 - List.map 233 - (fun (v : ep_hdr_vector) -> 234 - Alcotest.test_case v.name `Quick (test_ep_hdr_decode v)) 235 - ep_hdr_vecs 236 - in 237 - let ep_roundtrip = 238 - List.map 239 - (fun (v : ep_hdr_vector) -> 240 - Alcotest.test_case v.name `Quick (test_ep_hdr_roundtrip v)) 241 - ep_hdr_vecs 242 - in 243 - let mc_encode = 244 - List.map 245 - (fun (v : mc_status_vector) -> 246 - Alcotest.test_case v.name `Quick (test_mc_status_encode v)) 247 - mc_status_vecs 248 - in 249 - let mc_decode = 250 - List.map 251 - (fun (v : mc_status_vector) -> 252 - Alcotest.test_case v.name `Quick (test_mc_status_decode v)) 253 - mc_status_vecs 254 - in 255 - let mc_roundtrip = 256 - List.map 257 - (fun (v : mc_status_vector) -> 258 - Alcotest.test_case v.name `Quick (test_mc_status_roundtrip v)) 259 - mc_status_vecs 260 - in 261 - Alcotest.run "sdls-cryptolib-interop" 262 - [ 263 - ("ep-hdr-encode", ep_encode); 264 - ("ep-hdr-decode", ep_decode); 265 - ("ep-hdr-roundtrip", ep_roundtrip); 266 - ("mc-status-encode", mc_encode); 267 - ("mc-status-decode", mc_decode); 268 - ("mc-status-roundtrip", mc_roundtrip); 269 - ]
-39
test/interop/cryptolib/traces/ep_hdr.csv
··· 1 - # SDLS EP PDU Header interop test vectors 2 - # Oracle: NASA CryptoLib 1.5.1 3 - # Format: name,is_reply,user_flag,sg,pid,pdu_len,encoded_hex 4 - otar_cmd,0,0,0,1,64,010040 5 - key_activate_cmd,0,0,0,2,4,020004 6 - key_deactivate_cmd,0,0,0,3,4,030004 7 - key_verify_cmd,0,0,0,4,32,040020 8 - key_destroy_cmd,0,0,0,6,4,060004 9 - key_inventory_cmd,0,0,0,7,4,070004 10 - key_verify_reply,1,0,0,4,128,840080 11 - key_inventory_reply,1,0,0,7,96,870060 12 - sa_create_cmd,0,0,1,1,20,110014 13 - sa_delete_cmd,0,0,1,4,2,140002 14 - sa_set_arsnw_cmd,0,0,1,5,4,150004 15 - sa_rekey_cmd,0,0,1,6,22,160016 16 - sa_expire_cmd,0,0,1,9,2,190002 17 - sa_set_arsn_cmd,0,0,1,10,2,1a0002 18 - sa_start_cmd,0,0,1,11,6,1b0006 19 - sa_stop_cmd,0,0,1,14,2,1e0002 20 - sa_status_cmd,0,0,1,15,2,1f0002 21 - sa_read_arsn_cmd,0,0,1,0,2,100002 22 - sa_create_reply,1,0,2,1,0,a10000 23 - sa_status_reply,1,0,2,15,3,af0003 24 - sa_read_arsn_reply,1,0,2,0,20,a00014 25 - mc_ping_cmd,0,0,3,1,0,310000 26 - mc_log_status_cmd,0,0,3,2,0,320000 27 - mc_dump_log_cmd,0,0,3,3,0,330000 28 - mc_erase_log_cmd,0,0,3,4,0,340000 29 - mc_self_test_cmd,0,0,3,5,0,350000 30 - mc_alarm_reset_cmd,0,0,3,7,0,370000 31 - mc_ping_reply,1,0,3,1,5,b10005 32 - mc_log_status_reply,1,0,3,2,4,b20004 33 - mc_self_test_reply,1,0,3,5,1,b50001 34 - mc_alarm_reset_reply,1,0,3,7,0,b70000 35 - user_flag_cmd,0,1,1,1,10,51000a 36 - user_flag_reply,1,1,3,2,4,f20004 37 - zero_pdu_len,0,0,0,1,0,010000 38 - max_pdu_len,0,0,0,7,65535,07ffff 39 - all_bits_set,1,1,3,7,65535,f7ffff
-11
test/interop/cryptolib/traces/mc_status.csv
··· 1 - # SDLS MC Status Reply interop test vectors 2 - # Oracle: NASA CryptoLib 1.5.1 3 - # Format: name,operational,key_count,sa_count,encoded_hex 4 - # Note: operational byte encodes true=0x01, false=0x00 per Wire.bit Wire.uint8 5 - mc_idle,0,0,0,0000000000 6 - mc_operational,1,5,3,0100050003 7 - mc_max_counts,1,65535,65535,01ffffffff 8 - mc_not_op_with_keys,0,10,0,00000a0000 9 - mc_single_sa,1,1,1,0100010001 10 - mc_many_keys,1,256,128,0101000080 11 - mc_zero_keys_some_sas,1,0,42,010000002a
-1
test/interop/python/.gitignore
··· 1 - scripts/.venv/
-19
test/interop/python/dune
··· 1 - (test 2 - (name test) 3 - (libraries sdls alcotest) 4 - (deps 5 - (source_tree traces) 6 - (source_tree scripts))) 7 - 8 - ; Regenerate traces: REGEN_TRACES=1 dune build @regen-traces 9 - 10 - (rule 11 - (alias regen-traces) 12 - (enabled_if 13 - (= %{env:REGEN_TRACES=0} 1)) 14 - (deps 15 - (source_tree scripts)) 16 - (action 17 - (chdir 18 - scripts 19 - (run ./generate.py))))
-437
test/interop/python/scripts/generate.py
··· 1 - #!/usr/bin/env python3 2 - """Generate SDLS interop traces for ocaml-sdls. 3 - 4 - Oracle: Python (no deps needed -- pure bitfield encoding) 5 - Install: no dependencies 6 - 7 - Tests Security Header, Security Trailer, and EP PDU header encoding/decoding 8 - per CCSDS 355.0-B-2 and CCSDS 355.1-B-1. 9 - 10 - Security Header (CCSDS 355.0-B-2 Section 3.2.2): 11 - SPI: 16 bits (Security Parameter Index) 12 - IV: variable length (Initialization Vector) 13 - SN: variable length (Sequence Number) 14 - 15 - Security Trailer (CCSDS 355.0-B-2 Section 3.2.3): 16 - MAC: variable length (Message Authentication Code) 17 - 18 - EP PDU Header (CCSDS 355.1-B-1 Section 5.3.2): 19 - Byte 0: Type(1b) | UF(1b) | SG(2b) | PID(4b) 20 - Bytes 1-2: PDU_LEN (16-bit, big-endian) 21 - 22 - MC Status Reply (CCSDS 355.1-B-1): 23 - Byte 0: Operational flag (bit 7), rest reserved 24 - Bytes 1-2: Key count (16-bit BE) 25 - Bytes 3-4: SA count (16-bit BE) 26 - 27 - Traces are committed to git. Only re-run when changing inputs 28 - or upgrading the oracle. 29 - """ 30 - import os 31 - import struct 32 - 33 - TRACE_DIR = os.path.join(os.path.dirname(__file__), "..", "traces") 34 - 35 - 36 - # --- Security Header encoding --- 37 - 38 - def encode_security_header(spi, iv_bytes, sn_bytes): 39 - """Encode security header: SPI(2) + IV(variable) + SN(variable).""" 40 - return struct.pack(">H", spi) + iv_bytes + sn_bytes 41 - 42 - 43 - # --- Security Trailer encoding --- 44 - 45 - def encode_security_trailer(mac_bytes): 46 - """Encode security trailer: MAC(variable).""" 47 - return mac_bytes 48 - 49 - 50 - # --- EP PDU Header encoding --- 51 - 52 - def encode_ep_header(is_reply, user_flag, sg, pid, pdu_len): 53 - """Encode EP header into 3 bytes. 54 - 55 - Byte 0: Type(1b) | UF(1b) | SG(2b) | PID(4b) 56 - Bytes 1-2: PDU_LEN (16-bit BE) 57 - """ 58 - byte0 = 0 59 - if is_reply: 60 - byte0 |= 0x80 61 - if user_flag: 62 - byte0 |= 0x40 63 - byte0 |= (sg & 0x3) << 4 64 - byte0 |= pid & 0xF 65 - return struct.pack(">BH", byte0, pdu_len) 66 - 67 - 68 - # --- MC Status Reply encoding --- 69 - 70 - def encode_mc_status_reply(operational, key_count, sa_count): 71 - """Encode MC Status Reply: flags(1) + key_count(2) + sa_count(2) = 5 bytes. 72 - 73 - Byte 0: 0x01 if operational, 0x00 otherwise (Wire.bit Wire.uint8 maps 74 - bool to 0/1 over the full byte, not a single-bit bitfield). 75 - """ 76 - byte0 = 0x01 if operational else 0x00 77 - return struct.pack(">BHH", byte0, key_count, sa_count) 78 - 79 - 80 - # --- Test vectors --- 81 - 82 - sec_hdr_vectors = [ 83 - { 84 - "name": "gcm_typical", 85 - "spi": 1, 86 - "iv_hex": "000000000000000000000001", 87 - "sn_hex": "", 88 - "iv_len": 12, 89 - "sn_len": 0, 90 - }, 91 - { 92 - "name": "gcm_max_spi", 93 - "spi": 0xFFFF, 94 - "iv_hex": "aabbccddeeff001122334455", 95 - "sn_hex": "", 96 - "iv_len": 12, 97 - "sn_len": 0, 98 - }, 99 - { 100 - "name": "auth_with_sn", 101 - "spi": 42, 102 - "iv_hex": "000000000000000000000000", 103 - "sn_hex": "00000001", 104 - "iv_len": 12, 105 - "sn_len": 4, 106 - }, 107 - { 108 - "name": "short_iv_with_sn", 109 - "spi": 256, 110 - "iv_hex": "0102030405060708", 111 - "sn_hex": "0a0b", 112 - "iv_len": 8, 113 - "sn_len": 2, 114 - }, 115 - { 116 - "name": "zero_iv_zero_sn", 117 - "spi": 0, 118 - "iv_hex": "000000000000000000000000", 119 - "sn_hex": "00000000", 120 - "iv_len": 12, 121 - "sn_len": 4, 122 - }, 123 - { 124 - "name": "max_iv_values", 125 - "spi": 100, 126 - "iv_hex": "ffffffffffffffffffffffff", 127 - "sn_hex": "ffffffff", 128 - "iv_len": 12, 129 - "sn_len": 4, 130 - }, 131 - { 132 - "name": "no_iv_no_sn", 133 - "spi": 7, 134 - "iv_hex": "", 135 - "sn_hex": "", 136 - "iv_len": 0, 137 - "sn_len": 0, 138 - }, 139 - ] 140 - 141 - sec_trl_vectors = [ 142 - { 143 - "name": "gcm_16byte_mac", 144 - "mac_hex": "0102030405060708090a0b0c0d0e0f10", 145 - "mac_len": 16, 146 - }, 147 - { 148 - "name": "cmac_16byte", 149 - "mac_hex": "aabbccddeeff00112233445566778899", 150 - "mac_len": 16, 151 - }, 152 - { 153 - "name": "truncated_8byte_mac", 154 - "mac_hex": "deadbeefcafebabe", 155 - "mac_len": 8, 156 - }, 157 - { 158 - "name": "zero_mac", 159 - "mac_hex": "00000000000000000000000000000000", 160 - "mac_len": 16, 161 - }, 162 - { 163 - "name": "all_ff_mac", 164 - "mac_hex": "ffffffffffffffffffffffffffffffff", 165 - "mac_len": 16, 166 - }, 167 - ] 168 - 169 - # Service group constants 170 - SG_KEY_MGMT = 0 171 - SG_SA_MGMT_IR = 1 172 - SG_SA_MGMT_RI = 2 173 - SG_SEC_MON_CTRL = 3 174 - 175 - ep_hdr_vectors = [ 176 - { 177 - "name": "sa_create_cmd", 178 - "is_reply": False, 179 - "user_flag": False, 180 - "sg": SG_SA_MGMT_IR, 181 - "pid": 1, 182 - "pdu_len": 20, 183 - }, 184 - { 185 - "name": "sa_create_reply", 186 - "is_reply": True, 187 - "user_flag": False, 188 - "sg": SG_SA_MGMT_RI, 189 - "pid": 1, 190 - "pdu_len": 0, 191 - }, 192 - { 193 - "name": "otar_cmd", 194 - "is_reply": False, 195 - "user_flag": False, 196 - "sg": SG_KEY_MGMT, 197 - "pid": 1, 198 - "pdu_len": 64, 199 - }, 200 - { 201 - "name": "key_verify_reply", 202 - "is_reply": True, 203 - "user_flag": False, 204 - "sg": SG_KEY_MGMT, 205 - "pid": 4, 206 - "pdu_len": 32, 207 - }, 208 - { 209 - "name": "sa_delete_cmd", 210 - "is_reply": False, 211 - "user_flag": False, 212 - "sg": SG_SA_MGMT_IR, 213 - "pid": 4, 214 - "pdu_len": 2, 215 - }, 216 - { 217 - "name": "sa_start_cmd", 218 - "is_reply": False, 219 - "user_flag": False, 220 - "sg": SG_SA_MGMT_IR, 221 - "pid": 11, 222 - "pdu_len": 2, 223 - }, 224 - { 225 - "name": "sa_stop_cmd", 226 - "is_reply": False, 227 - "user_flag": False, 228 - "sg": SG_SA_MGMT_IR, 229 - "pid": 14, 230 - "pdu_len": 2, 231 - }, 232 - { 233 - "name": "sa_status_cmd", 234 - "is_reply": False, 235 - "user_flag": False, 236 - "sg": SG_SA_MGMT_IR, 237 - "pid": 15, 238 - "pdu_len": 2, 239 - }, 240 - { 241 - "name": "mc_ping_cmd", 242 - "is_reply": False, 243 - "user_flag": False, 244 - "sg": SG_SEC_MON_CTRL, 245 - "pid": 1, 246 - "pdu_len": 0, 247 - }, 248 - { 249 - "name": "mc_ping_reply", 250 - "is_reply": True, 251 - "user_flag": False, 252 - "sg": SG_SEC_MON_CTRL, 253 - "pid": 1, 254 - "pdu_len": 5, 255 - }, 256 - { 257 - "name": "mc_self_test_cmd", 258 - "is_reply": False, 259 - "user_flag": False, 260 - "sg": SG_SEC_MON_CTRL, 261 - "pid": 5, 262 - "pdu_len": 0, 263 - }, 264 - { 265 - "name": "user_flag_set", 266 - "is_reply": False, 267 - "user_flag": True, 268 - "sg": SG_SA_MGMT_IR, 269 - "pid": 1, 270 - "pdu_len": 10, 271 - }, 272 - { 273 - "name": "reply_with_uf", 274 - "is_reply": True, 275 - "user_flag": True, 276 - "sg": SG_SEC_MON_CTRL, 277 - "pid": 2, 278 - "pdu_len": 4, 279 - }, 280 - { 281 - "name": "max_pdu_len", 282 - "is_reply": False, 283 - "user_flag": False, 284 - "sg": SG_KEY_MGMT, 285 - "pid": 7, 286 - "pdu_len": 65535, 287 - }, 288 - { 289 - "name": "sa_rekey_cmd", 290 - "is_reply": False, 291 - "user_flag": False, 292 - "sg": SG_SA_MGMT_IR, 293 - "pid": 6, 294 - "pdu_len": 4, 295 - }, 296 - { 297 - "name": "sa_expire_cmd", 298 - "is_reply": False, 299 - "user_flag": False, 300 - "sg": SG_SA_MGMT_IR, 301 - "pid": 9, 302 - "pdu_len": 2, 303 - }, 304 - { 305 - "name": "key_activation_cmd", 306 - "is_reply": False, 307 - "user_flag": False, 308 - "sg": SG_KEY_MGMT, 309 - "pid": 2, 310 - "pdu_len": 2, 311 - }, 312 - { 313 - "name": "key_deactivation_cmd", 314 - "is_reply": False, 315 - "user_flag": False, 316 - "sg": SG_KEY_MGMT, 317 - "pid": 3, 318 - "pdu_len": 2, 319 - }, 320 - { 321 - "name": "key_destruction_cmd", 322 - "is_reply": False, 323 - "user_flag": False, 324 - "sg": SG_KEY_MGMT, 325 - "pid": 6, 326 - "pdu_len": 2, 327 - }, 328 - { 329 - "name": "sa_set_arsnw_cmd", 330 - "is_reply": False, 331 - "user_flag": False, 332 - "sg": SG_SA_MGMT_IR, 333 - "pid": 5, 334 - "pdu_len": 4, 335 - }, 336 - ] 337 - 338 - mc_status_vectors = [ 339 - { 340 - "name": "mc_idle", 341 - "operational": False, 342 - "key_count": 0, 343 - "sa_count": 0, 344 - }, 345 - { 346 - "name": "mc_operational", 347 - "operational": True, 348 - "key_count": 5, 349 - "sa_count": 3, 350 - }, 351 - { 352 - "name": "mc_max_counts", 353 - "operational": True, 354 - "key_count": 65535, 355 - "sa_count": 65535, 356 - }, 357 - { 358 - "name": "mc_not_op_with_keys", 359 - "operational": False, 360 - "key_count": 10, 361 - "sa_count": 0, 362 - }, 363 - { 364 - "name": "mc_single_sa", 365 - "operational": True, 366 - "key_count": 1, 367 - "sa_count": 1, 368 - }, 369 - ] 370 - 371 - 372 - def main(): 373 - os.makedirs(TRACE_DIR, exist_ok=True) 374 - 375 - # --- Security Header vectors --- 376 - with open(os.path.join(TRACE_DIR, "sec_hdr.csv"), "w") as f: 377 - f.write("# SDLS Security Header interop test vectors\n") 378 - f.write("# Oracle: Python (CCSDS 355.0-B-2 reference implementation)\n") 379 - f.write("# Format: name,spi,iv_hex,sn_hex,iv_len,sn_len,encoded_hex\n") 380 - for v in sec_hdr_vectors: 381 - iv = bytes.fromhex(v["iv_hex"]) 382 - sn = bytes.fromhex(v["sn_hex"]) 383 - encoded = encode_security_header(v["spi"], iv, sn) 384 - f.write( 385 - f"{v['name']},{v['spi']},{v['iv_hex']},{v['sn_hex']}," 386 - f"{v['iv_len']},{v['sn_len']},{encoded.hex()}\n" 387 - ) 388 - 389 - # --- Security Trailer vectors --- 390 - with open(os.path.join(TRACE_DIR, "sec_trl.csv"), "w") as f: 391 - f.write("# SDLS Security Trailer interop test vectors\n") 392 - f.write("# Oracle: Python (CCSDS 355.0-B-2 reference implementation)\n") 393 - f.write("# Format: name,mac_hex,mac_len,encoded_hex\n") 394 - for v in sec_trl_vectors: 395 - mac = bytes.fromhex(v["mac_hex"]) 396 - encoded = encode_security_trailer(mac) 397 - f.write(f"{v['name']},{v['mac_hex']},{v['mac_len']},{encoded.hex()}\n") 398 - 399 - # --- EP PDU Header vectors --- 400 - with open(os.path.join(TRACE_DIR, "ep_hdr.csv"), "w") as f: 401 - f.write("# SDLS EP PDU Header interop test vectors\n") 402 - f.write("# Oracle: Python (CCSDS 355.1-B-1 reference implementation)\n") 403 - f.write("# Format: name,is_reply,user_flag,sg,pid,pdu_len,encoded_hex\n") 404 - for v in ep_hdr_vectors: 405 - encoded = encode_ep_header( 406 - v["is_reply"], v["user_flag"], v["sg"], v["pid"], v["pdu_len"] 407 - ) 408 - f.write( 409 - f"{v['name']},{1 if v['is_reply'] else 0}," 410 - f"{1 if v['user_flag'] else 0},{v['sg']},{v['pid']}," 411 - f"{v['pdu_len']},{encoded.hex()}\n" 412 - ) 413 - 414 - # --- MC Status Reply vectors --- 415 - with open(os.path.join(TRACE_DIR, "mc_status.csv"), "w") as f: 416 - f.write("# SDLS MC Status Reply interop test vectors\n") 417 - f.write("# Oracle: Python (CCSDS 355.1-B-1 reference implementation)\n") 418 - f.write( 419 - "# Format: name,operational,key_count,sa_count,encoded_hex\n" 420 - ) 421 - for v in mc_status_vectors: 422 - encoded = encode_mc_status_reply( 423 - v["operational"], v["key_count"], v["sa_count"] 424 - ) 425 - f.write( 426 - f"{v['name']},{1 if v['operational'] else 0}," 427 - f"{v['key_count']},{v['sa_count']},{encoded.hex()}\n" 428 - ) 429 - 430 - print(f"Wrote {TRACE_DIR}/sec_hdr.csv") 431 - print(f"Wrote {TRACE_DIR}/sec_trl.csv") 432 - print(f"Wrote {TRACE_DIR}/ep_hdr.csv") 433 - print(f"Wrote {TRACE_DIR}/mc_status.csv") 434 - 435 - 436 - if __name__ == "__main__": 437 - main()
-428
test/interop/python/test.ml
··· 1 - (** Python interop tests for ocaml-sdls. 2 - 3 - Tests security header/trailer parsing and EP PDU header encoding/decoding 4 - against a Python reference implementation of CCSDS 355.0-B-2 and 355.1-B-1. 5 - 6 - Traces generated by: Python (CCSDS 355.0-B-2 / 355.1-B-1 reference) 7 - Regenerate: REGEN_TRACES=1 dune build @regen-traces *) 8 - 9 - open Sdls 10 - 11 - let trace path = Filename.concat "traces" path 12 - 13 - (* Parse CSV trace: skip comments (#), split on comma *) 14 - let parse_csv path = 15 - let ic = open_in (trace path) in 16 - let lines = ref [] in 17 - (try 18 - while true do 19 - let line = input_line ic in 20 - if String.length line > 0 && line.[0] <> '#' then 21 - lines := String.split_on_char ',' line :: !lines 22 - done 23 - with End_of_file -> ()); 24 - close_in ic; 25 - List.rev !lines 26 - 27 - let bytes_of_hex hex = 28 - if String.length hex = 0 then Bytes.empty 29 - else 30 - let len = String.length hex / 2 in 31 - Bytes.init len (fun i -> 32 - Char.chr (int_of_string ("0x" ^ String.sub hex (i * 2) 2))) 33 - 34 - let hex_of_bytes b = 35 - let buf = Buffer.create (Bytes.length b * 2) in 36 - Bytes.iter 37 - (fun c -> Buffer.add_string buf (Printf.sprintf "%02x" (Char.code c))) 38 - b; 39 - Buffer.contents buf 40 - 41 - (* {1 Security Header vectors} *) 42 - 43 - type sec_hdr_vector = { 44 - name : string; 45 - spi : int; 46 - iv : bytes; 47 - sn : bytes; 48 - iv_len : int; 49 - sn_len : int; 50 - encoded : bytes; 51 - } 52 - 53 - let parse_sec_hdr_vectors () = 54 - let rows = parse_csv "sec_hdr.csv" in 55 - List.filter_map 56 - (fun row -> 57 - match row with 58 - | [ name; spi; iv_hex; sn_hex; iv_len; sn_len; encoded_hex ] -> 59 - Some 60 - { 61 - name; 62 - spi = int_of_string spi; 63 - iv = bytes_of_hex iv_hex; 64 - sn = bytes_of_hex sn_hex; 65 - iv_len = int_of_string iv_len; 66 - sn_len = int_of_string sn_len; 67 - encoded = bytes_of_hex encoded_hex; 68 - } 69 - | _ -> Alcotest.failf "bad sec_hdr CSV row: %s" (String.concat "," row)) 70 - rows 71 - 72 - let test_sec_hdr_encode vec () = 73 - let sh : Sa.security_header = { spi = vec.spi; iv = vec.iv; sn = vec.sn } in 74 - let w = Binary.Writer.create 64 in 75 - Sdls.write_security_header w sh; 76 - let got = Binary.Writer.contents w in 77 - if got <> vec.encoded then 78 - Alcotest.failf "%s: encode mismatch\n expected: %s\n got: %s" 79 - vec.name (hex_of_bytes vec.encoded) (hex_of_bytes got) 80 - 81 - let test_sec_hdr_decode vec () = 82 - (* Build a minimal SA entry with the right iv_len/sn_len for decoding *) 83 - let sa = 84 - Sa.v ~encryption:false ~authentication:false ~ecs:None ~acs:None 85 - ~iv_len:vec.iv_len ~mac_len:0 ~sn_len:vec.sn_len ~spi:1 ~scid:0 ~vcid:0 () 86 - in 87 - let r = Binary.Reader.of_bytes vec.encoded in 88 - match Sdls.read_security_header ~sa r with 89 - | Error e -> 90 - Alcotest.failf "%s: decode failed: %a" vec.name Sdls.pp_error_debug e 91 - | Ok (sh : Sa.security_header) -> 92 - Alcotest.(check int) (vec.name ^ ": spi") vec.spi sh.spi; 93 - Alcotest.(check string) 94 - (vec.name ^ ": iv") (hex_of_bytes vec.iv) (hex_of_bytes sh.iv); 95 - Alcotest.(check string) 96 - (vec.name ^ ": sn") (hex_of_bytes vec.sn) (hex_of_bytes sh.sn) 97 - 98 - let test_sec_hdr_roundtrip vec () = 99 - let sh : Sa.security_header = { spi = vec.spi; iv = vec.iv; sn = vec.sn } in 100 - let w = Binary.Writer.create 64 in 101 - Sdls.write_security_header w sh; 102 - let buf = Binary.Writer.contents w in 103 - let sa = 104 - Sa.v ~encryption:false ~authentication:false ~ecs:None ~acs:None 105 - ~iv_len:vec.iv_len ~mac_len:0 ~sn_len:vec.sn_len ~spi:1 ~scid:0 ~vcid:0 () 106 - in 107 - let r = Binary.Reader.of_bytes buf in 108 - match Sdls.read_security_header ~sa r with 109 - | Error e -> 110 - Alcotest.failf "%s: roundtrip decode failed: %a" vec.name 111 - Sdls.pp_error_debug e 112 - | Ok (sh' : Sa.security_header) -> 113 - Alcotest.(check int) (vec.name ^ ": spi") sh.spi sh'.spi; 114 - if sh.iv <> sh'.iv then Alcotest.failf "%s: iv mismatch" vec.name; 115 - if sh.sn <> sh'.sn then Alcotest.failf "%s: sn mismatch" vec.name 116 - 117 - (* {1 Security Trailer vectors} *) 118 - 119 - type sec_trl_vector = { 120 - name : string; 121 - mac : bytes; 122 - mac_len : int; 123 - encoded : bytes; 124 - } 125 - 126 - let parse_sec_trl_vectors () = 127 - let rows = parse_csv "sec_trl.csv" in 128 - List.filter_map 129 - (fun row -> 130 - match row with 131 - | [ name; mac_hex; mac_len; encoded_hex ] -> 132 - Some 133 - { 134 - name; 135 - mac = bytes_of_hex mac_hex; 136 - mac_len = int_of_string mac_len; 137 - encoded = bytes_of_hex encoded_hex; 138 - } 139 - | _ -> Alcotest.failf "bad sec_trl CSV row: %s" (String.concat "," row)) 140 - rows 141 - 142 - let test_sec_trl_encode vec () = 143 - let st : Sa.security_trailer = { mac = vec.mac } in 144 - let w = Binary.Writer.create 64 in 145 - Sdls.write_security_trailer w st; 146 - let got = Binary.Writer.contents w in 147 - if got <> vec.encoded then 148 - Alcotest.failf "%s: encode mismatch\n expected: %s\n got: %s" 149 - vec.name (hex_of_bytes vec.encoded) (hex_of_bytes got) 150 - 151 - let test_sec_trl_decode vec () = 152 - let sa = 153 - Sa.v ~encryption:false ~authentication:false ~ecs:None ~acs:None ~iv_len:0 154 - ~mac_len:vec.mac_len ~sn_len:0 ~spi:1 ~scid:0 ~vcid:0 () 155 - in 156 - let r = Binary.Reader.of_bytes vec.encoded in 157 - match Sdls.read_security_trailer ~sa r with 158 - | Error e -> 159 - Alcotest.failf "%s: decode failed: %a" vec.name Sdls.pp_error_debug e 160 - | Ok st -> 161 - Alcotest.(check string) 162 - (vec.name ^ ": mac") (hex_of_bytes vec.mac) (hex_of_bytes st.mac) 163 - 164 - let test_sec_trl_roundtrip vec () = 165 - let st : Sa.security_trailer = { mac = vec.mac } in 166 - let w = Binary.Writer.create 64 in 167 - Sdls.write_security_trailer w st; 168 - let buf = Binary.Writer.contents w in 169 - let sa = 170 - Sa.v ~encryption:false ~authentication:false ~ecs:None ~acs:None ~iv_len:0 171 - ~mac_len:vec.mac_len ~sn_len:0 ~spi:1 ~scid:0 ~vcid:0 () 172 - in 173 - let r = Binary.Reader.of_bytes buf in 174 - match Sdls.read_security_trailer ~sa r with 175 - | Error e -> 176 - Alcotest.failf "%s: roundtrip decode failed: %a" vec.name 177 - Sdls.pp_error_debug e 178 - | Ok st' -> 179 - if st.mac <> st'.mac then Alcotest.failf "%s: mac mismatch" vec.name 180 - 181 - (* {1 EP PDU Header vectors} *) 182 - 183 - type ep_hdr_vector = { 184 - name : string; 185 - is_reply : bool; 186 - user_flag : bool; 187 - sg : int; 188 - pid : int; 189 - pdu_len : int; 190 - encoded : bytes; 191 - } 192 - 193 - let parse_ep_hdr_vectors () = 194 - let rows = parse_csv "ep_hdr.csv" in 195 - List.filter_map 196 - (fun row -> 197 - match row with 198 - | [ name; is_reply; user_flag; sg; pid; pdu_len; encoded_hex ] -> 199 - Some 200 - { 201 - name; 202 - is_reply = int_of_string is_reply <> 0; 203 - user_flag = int_of_string user_flag <> 0; 204 - sg = int_of_string sg; 205 - pid = int_of_string pid; 206 - pdu_len = int_of_string pdu_len; 207 - encoded = bytes_of_hex encoded_hex; 208 - } 209 - | _ -> Alcotest.failf "bad ep_hdr CSV row: %s" (String.concat "," row)) 210 - rows 211 - 212 - let test_ep_hdr_encode vec () = 213 - let sg = 214 - match Ep.service_group_of_int vec.sg with 215 - | Some sg -> sg 216 - | None -> Alcotest.failf "%s: invalid sg %d" vec.name vec.sg 217 - in 218 - let hdr = 219 - Ep. 220 - { 221 - is_reply = vec.is_reply; 222 - user_flag = vec.user_flag; 223 - service_group = sg; 224 - procedure_id = vec.pid; 225 - pdu_len = vec.pdu_len; 226 - } 227 - in 228 - let got = Ep.encode_header hdr in 229 - if got <> vec.encoded then 230 - Alcotest.failf "%s: encode mismatch\n expected: %s\n got: %s" 231 - vec.name (hex_of_bytes vec.encoded) (hex_of_bytes got) 232 - 233 - let test_ep_hdr_decode vec () = 234 - match Ep.decode_header vec.encoded 0 with 235 - | Error `Truncated -> Alcotest.failf "%s: decode truncated" vec.name 236 - | Error `Invalid_sg -> Alcotest.failf "%s: decode invalid_sg" vec.name 237 - | Ok hdr -> 238 - Alcotest.(check bool) (vec.name ^ ": is_reply") vec.is_reply hdr.is_reply; 239 - Alcotest.(check bool) 240 - (vec.name ^ ": user_flag") vec.user_flag hdr.user_flag; 241 - Alcotest.(check int) 242 - (vec.name ^ ": sg") vec.sg 243 - (Ep.int_of_service_group hdr.service_group); 244 - Alcotest.(check int) (vec.name ^ ": pid") vec.pid hdr.procedure_id; 245 - Alcotest.(check int) (vec.name ^ ": pdu_len") vec.pdu_len hdr.pdu_len 246 - 247 - let test_ep_hdr_roundtrip vec () = 248 - let sg = 249 - match Ep.service_group_of_int vec.sg with 250 - | Some sg -> sg 251 - | None -> Alcotest.failf "%s: invalid sg %d" vec.name vec.sg 252 - in 253 - let hdr = 254 - Ep. 255 - { 256 - is_reply = vec.is_reply; 257 - user_flag = vec.user_flag; 258 - service_group = sg; 259 - procedure_id = vec.pid; 260 - pdu_len = vec.pdu_len; 261 - } 262 - in 263 - let encoded = Ep.encode_header hdr in 264 - match Ep.decode_header encoded 0 with 265 - | Error _ -> Alcotest.failf "%s: roundtrip decode failed" vec.name 266 - | Ok decoded -> 267 - Alcotest.(check bool) 268 - (vec.name ^ ": is_reply") hdr.is_reply decoded.is_reply; 269 - Alcotest.(check bool) 270 - (vec.name ^ ": user_flag") hdr.user_flag decoded.user_flag; 271 - Alcotest.(check int) 272 - (vec.name ^ ": sg") 273 - (Ep.int_of_service_group hdr.service_group) 274 - (Ep.int_of_service_group decoded.service_group); 275 - Alcotest.(check int) 276 - (vec.name ^ ": pid") hdr.procedure_id decoded.procedure_id; 277 - Alcotest.(check int) (vec.name ^ ": pdu_len") hdr.pdu_len decoded.pdu_len 278 - 279 - (* {1 MC Status Reply vectors} *) 280 - 281 - type mc_status_vector = { 282 - mc_name : string; 283 - mc_operational : bool; 284 - mc_key_count : int; 285 - mc_sa_count : int; 286 - mc_encoded : bytes; 287 - } 288 - 289 - let parse_mc_status_vectors () = 290 - let rows = parse_csv "mc_status.csv" in 291 - List.filter_map 292 - (fun row -> 293 - match row with 294 - | [ name; operational; key_count; sa_count; encoded_hex ] -> 295 - Some 296 - { 297 - mc_name = name; 298 - mc_operational = int_of_string operational <> 0; 299 - mc_key_count = int_of_string key_count; 300 - mc_sa_count = int_of_string sa_count; 301 - mc_encoded = bytes_of_hex encoded_hex; 302 - } 303 - | _ -> Alcotest.failf "bad mc_status CSV row: %s" (String.concat "," row)) 304 - rows 305 - 306 - let test_mc_status_encode vec () = 307 - let r = 308 - Ep. 309 - { 310 - operational = vec.mc_operational; 311 - key_count = vec.mc_key_count; 312 - sa_count = vec.mc_sa_count; 313 - } 314 - in 315 - let got = Ep.encode_mc_status_reply r in 316 - if got <> vec.mc_encoded then 317 - Alcotest.failf "%s: encode mismatch\n expected: %s\n got: %s" 318 - vec.mc_name 319 - (hex_of_bytes vec.mc_encoded) 320 - (hex_of_bytes got) 321 - 322 - let test_mc_status_decode vec () = 323 - match Ep.decode_mc_status_reply vec.mc_encoded 0 with 324 - | Error `Truncated -> Alcotest.failf "%s: decode truncated" vec.mc_name 325 - | Ok r -> 326 - Alcotest.(check bool) 327 - (vec.mc_name ^ ": operational") 328 - vec.mc_operational r.operational; 329 - Alcotest.(check int) 330 - (vec.mc_name ^ ": key_count") 331 - vec.mc_key_count r.key_count; 332 - Alcotest.(check int) 333 - (vec.mc_name ^ ": sa_count") 334 - vec.mc_sa_count r.sa_count 335 - 336 - let test_mc_status_roundtrip vec () = 337 - let r = 338 - Ep. 339 - { 340 - operational = vec.mc_operational; 341 - key_count = vec.mc_key_count; 342 - sa_count = vec.mc_sa_count; 343 - } 344 - in 345 - let encoded = Ep.encode_mc_status_reply r in 346 - match Ep.decode_mc_status_reply encoded 0 with 347 - | Error `Truncated -> Alcotest.failf "%s: roundtrip decode failed" vec.mc_name 348 - | Ok decoded -> 349 - Alcotest.(check bool) 350 - (vec.mc_name ^ ": operational") 351 - r.operational decoded.operational; 352 - Alcotest.(check int) 353 - (vec.mc_name ^ ": key_count") 354 - r.key_count decoded.key_count; 355 - Alcotest.(check int) 356 - (vec.mc_name ^ ": sa_count") 357 - r.sa_count decoded.sa_count 358 - 359 - (* {1 Test runner} *) 360 - 361 - let () = 362 - let sec_hdr_vecs = parse_sec_hdr_vectors () in 363 - let sec_trl_vecs = parse_sec_trl_vectors () in 364 - let ep_hdr_vecs = parse_ep_hdr_vectors () in 365 - let mc_status_vecs = parse_mc_status_vectors () in 366 - Alcotest.run "sdls-interop" 367 - [ 368 - ( "sec-hdr-encode", 369 - List.map 370 - (fun (v : sec_hdr_vector) -> 371 - Alcotest.test_case v.name `Quick (test_sec_hdr_encode v)) 372 - sec_hdr_vecs ); 373 - ( "sec-hdr-decode", 374 - List.map 375 - (fun (v : sec_hdr_vector) -> 376 - Alcotest.test_case v.name `Quick (test_sec_hdr_decode v)) 377 - sec_hdr_vecs ); 378 - ( "sec-hdr-roundtrip", 379 - List.map 380 - (fun (v : sec_hdr_vector) -> 381 - Alcotest.test_case v.name `Quick (test_sec_hdr_roundtrip v)) 382 - sec_hdr_vecs ); 383 - ( "sec-trl-encode", 384 - List.map 385 - (fun (v : sec_trl_vector) -> 386 - Alcotest.test_case v.name `Quick (test_sec_trl_encode v)) 387 - sec_trl_vecs ); 388 - ( "sec-trl-decode", 389 - List.map 390 - (fun (v : sec_trl_vector) -> 391 - Alcotest.test_case v.name `Quick (test_sec_trl_decode v)) 392 - sec_trl_vecs ); 393 - ( "sec-trl-roundtrip", 394 - List.map 395 - (fun (v : sec_trl_vector) -> 396 - Alcotest.test_case v.name `Quick (test_sec_trl_roundtrip v)) 397 - sec_trl_vecs ); 398 - ( "ep-hdr-encode", 399 - List.map 400 - (fun (v : ep_hdr_vector) -> 401 - Alcotest.test_case v.name `Quick (test_ep_hdr_encode v)) 402 - ep_hdr_vecs ); 403 - ( "ep-hdr-decode", 404 - List.map 405 - (fun (v : ep_hdr_vector) -> 406 - Alcotest.test_case v.name `Quick (test_ep_hdr_decode v)) 407 - ep_hdr_vecs ); 408 - ( "ep-hdr-roundtrip", 409 - List.map 410 - (fun (v : ep_hdr_vector) -> 411 - Alcotest.test_case v.name `Quick (test_ep_hdr_roundtrip v)) 412 - ep_hdr_vecs ); 413 - ( "mc-status-encode", 414 - List.map 415 - (fun v -> 416 - Alcotest.test_case v.mc_name `Quick (test_mc_status_encode v)) 417 - mc_status_vecs ); 418 - ( "mc-status-decode", 419 - List.map 420 - (fun v -> 421 - Alcotest.test_case v.mc_name `Quick (test_mc_status_decode v)) 422 - mc_status_vecs ); 423 - ( "mc-status-roundtrip", 424 - List.map 425 - (fun v -> 426 - Alcotest.test_case v.mc_name `Quick (test_mc_status_roundtrip v)) 427 - mc_status_vecs ); 428 - ]
-23
test/interop/python/traces/ep_hdr.csv
··· 1 - # SDLS EP PDU Header interop test vectors 2 - # Oracle: Python (CCSDS 355.1-B-1 reference implementation) 3 - # Format: name,is_reply,user_flag,sg,pid,pdu_len,encoded_hex 4 - sa_create_cmd,0,0,1,1,20,110014 5 - sa_create_reply,1,0,2,1,0,a10000 6 - otar_cmd,0,0,0,1,64,010040 7 - key_verify_reply,1,0,0,4,32,840020 8 - sa_delete_cmd,0,0,1,4,2,140002 9 - sa_start_cmd,0,0,1,11,2,1b0002 10 - sa_stop_cmd,0,0,1,14,2,1e0002 11 - sa_status_cmd,0,0,1,15,2,1f0002 12 - mc_ping_cmd,0,0,3,1,0,310000 13 - mc_ping_reply,1,0,3,1,5,b10005 14 - mc_self_test_cmd,0,0,3,5,0,350000 15 - user_flag_set,0,1,1,1,10,51000a 16 - reply_with_uf,1,1,3,2,4,f20004 17 - max_pdu_len,0,0,0,7,65535,07ffff 18 - sa_rekey_cmd,0,0,1,6,4,160004 19 - sa_expire_cmd,0,0,1,9,2,190002 20 - key_activation_cmd,0,0,0,2,2,020002 21 - key_deactivation_cmd,0,0,0,3,2,030002 22 - key_destruction_cmd,0,0,0,6,2,060002 23 - sa_set_arsnw_cmd,0,0,1,5,4,150004
-8
test/interop/python/traces/mc_status.csv
··· 1 - # SDLS MC Status Reply interop test vectors 2 - # Oracle: Python (CCSDS 355.1-B-1 reference implementation) 3 - # Format: name,operational,key_count,sa_count,encoded_hex 4 - mc_idle,0,0,0,0000000000 5 - mc_operational,1,5,3,0100050003 6 - mc_max_counts,1,65535,65535,01ffffffff 7 - mc_not_op_with_keys,0,10,0,00000a0000 8 - mc_single_sa,1,1,1,0100010001
-10
test/interop/python/traces/sec_hdr.csv
··· 1 - # SDLS Security Header interop test vectors 2 - # Oracle: Python (CCSDS 355.0-B-2 reference implementation) 3 - # Format: name,spi,iv_hex,sn_hex,iv_len,sn_len,encoded_hex 4 - gcm_typical,1,000000000000000000000001,,12,0,0001000000000000000000000001 5 - gcm_max_spi,65535,aabbccddeeff001122334455,,12,0,ffffaabbccddeeff001122334455 6 - auth_with_sn,42,000000000000000000000000,00000001,12,4,002a00000000000000000000000000000001 7 - short_iv_with_sn,256,0102030405060708,0a0b,8,2,010001020304050607080a0b 8 - zero_iv_zero_sn,0,000000000000000000000000,00000000,12,4,000000000000000000000000000000000000 9 - max_iv_values,100,ffffffffffffffffffffffff,ffffffff,12,4,0064ffffffffffffffffffffffffffffffff 10 - no_iv_no_sn,7,,,0,0,0007
-8
test/interop/python/traces/sec_trl.csv
··· 1 - # SDLS Security Trailer interop test vectors 2 - # Oracle: Python (CCSDS 355.0-B-2 reference implementation) 3 - # Format: name,mac_hex,mac_len,encoded_hex 4 - gcm_16byte_mac,0102030405060708090a0b0c0d0e0f10,16,0102030405060708090a0b0c0d0e0f10 5 - cmac_16byte,aabbccddeeff00112233445566778899,16,aabbccddeeff00112233445566778899 6 - truncated_8byte_mac,deadbeefcafebabe,8,deadbeefcafebabe 7 - zero_mac,00000000000000000000000000000000,16,00000000000000000000000000000000 8 - all_ff_mac,ffffffffffffffffffffffffffffffff,16,ffffffffffffffffffffffffffffffff