tiny.reticulum.wire.link
Defined in wire.
API (33)
Actions
Public operations.
Proof.decode: Splits a link request proof payload of 96 or 99 bytes confirming acceptance into the signature, the key, and any signalling for an initiator, following Reticulum@1.5.0 RNS/Link.py:399-406.Proof.encode: Puts the signature at the start ofout, follows it with the encryption key and then the signalling when there is any, and gives back what it wrote, so the responder confirming the link writes the proof into its outgoing buffer, following Reticulum@1.5.0 RNS/Link.py:371.Proof.encodedLengthRequest.decode: Splits a link request payload of 64 or 67 bytes to open a session into the two keys and any signalling for a responder, following Reticulum@1.5.0 RNS/Link.py:187-189.Request.encode: Puts both keys at the start ofout, follows them with the signalling when there is any, and gives back what it wrote, so the initiator sending the request writes it into its outgoing buffer, following Reticulum@1.5.0 RNS/Link.py:312.Request.encodedLengthRequest.mode: Returns the cipher mode that the request signalled, and AES-256-CBC when it signalled none, so a responder learns which cipher the initiator asked for, following Reticulum@1.5.0 RNS/Link.py:172-176.Signalling.decode: Pulls the MTU out of the 21 lowest bits and the cipher out of the three highest for a caller reading the values an arriving request or proof carried, following Reticulum@1.5.0 RNS/Link.py:154-164,172-183.Signalling.encode: Returns the three bytes with the cipher in the three highest bits and the MTU in the 21 below them, most significant byte first, for a caller appending its signalling to the request or proof it is about to send, following Reticulum@1.5.0 RNS/Link.py:148-151.closes: Returns whether the decrypted plaintext is exactly the link id, the payload carried by the packet an end sends to close the link, so a caller decides whether that plaintext closes the link, following Reticulum@1.5.0 RNS/Link.py:674-678.decodeRtt: Returns the round trip time carried by a nine-byte plaintext whose first byte is 0xcb and whose remaining eight hold a double that is finite with its sign bit clear, so a responder reads what the initiator reported to bring the link up, the form Reticulum@1.5.0 RNS/vendor/umsgpack.py:768-769 accepts.encodeRtt: Returns the nine bytes that report a round trip time in seconds: the 0xcb marker and then the double's bits, most significant byte first, following Reticulum@1.5.0 RNS/vendor/umsgpack.py:325-333.linkId: Returns the 16-byte truncated hash that names an encrypted session between two endpoints (link), taken over the request packet's hashable part with everything past the 64 key bytes left off, following Reticulum@1.5.0 RNS/Link.py:335-342.proofMode: Reads the cipher mode out of the top three bits of byte 96 once a proof payload runs past 96 bytes, and gives AES-256-CBC for a payload of 96 bytes or fewer, so a caller reads a proof's cipher without splitting the whole payload first, following Reticulum@1.5.0 RNS/Link.py:179-183,396-398.signedProof: Builds inoutthe bytes a link request proof puts its signature over, and returns them, running the 16-byte link id naming the session, then responder encryption key, then the signing key of the destination's identity, then whatever signalling rode along, so a responder builds the message it signs and an initiator…
Types and contracts
Public types and contracts.
DecodeErrorEncodeErrorMode: The three-bit cipher mode a link runs under, for a caller reading the cipher out of three signalled bits.Proof: The three fields of a link request proof payload confirming acceptance: the signature made by the destination's identity, the responder's encryption key, and, once the payload runs long enough, the signalling, so an initiator reads these three fields out of the proof that came back, following Reticulum@1.5.0 RNS/Lin…Request: The three fields of a link request payload to open a session: the initiator's encryption key, its signing key, and, once the payload runs long enough, the signalling, so a responder reads these three fields out of the request that reached it, following Reticulum@1.5.0 RNS/Link.py:312.RttErrorSignalling: The link MTU and cipher a request or proof states, for a caller reading or stating what an encrypted session (link) will run under.
Values and defaults
Public values and defaults.
default_mode: AES-256-CBC, the cipher that a request or proof carrying no signalling runs under and the single cipher the reference turns on, for working out which cipher applies when a request or proof signalled nothing, following Reticulum@1.5.0 RNS/Link.py:133-134,176,183.default_signalling: The MTU 500 with AES-256-CBC that an initiator sends on an interface reporting no hardware MTU of its own, and that the responder confirms.keepalive_answer: 0xfe, the one-byte payload a responder returns once a keepalive request reaches it, so an initiator recognizes its keepalive was heard, following Reticulum@1.5.0 RNS/Link.py:1131-1133.keepalive_request: 0xff, the one-byte payload an initiator alone sends to keep a quiet link alive, which the responder recognizes, following Reticulum@1.5.0 RNS/Link.py:750,800.proof_bytes: Ninety-six bytes, a link request proof payload with no signalling: a 64-byte Ed25519 signature and then 32 bytes of the responder's encryption key, for sizing or checking the payload the receiving end (responder) sends back to accept an encrypted session (link), following Reticulum@1.5.0 RNS/Link.py:405-406.request_bytes: Sixty-four bytes, the payload of an opening packet (link request) with no signalling: two 32-byte keys back to back, the initiator's encryption key and then its signing key, for sizing or checking the payload an initiator sends to open an encrypted session (link), following Reticulum@1.5.0 RNS/Link.py:70,189.rtt_bytes: Nine bytes, what the opening end (initiator) sends to report its round trip time: a leading 0xcb, which is how MessagePack introduces a double, and then that double's eight bytes, most significant first, for sizing the plaintext that reports a round trip time over a new encrypted session (link), following Reticu…signalled_proof_bytes: Ninety-nine bytes, a link request proof payload with signalling, and the one length from which an initiator reads a confirmed MTU, so the opening end (initiator) can check whether the proof it received settles the link MTU, following Reticulum@1.5.0 RNS/Link.py:399-402.signalled_request_bytes: Sixty-seven bytes, a link request payload with signalling, which together with 64 makes the pair of lengths the receiving end (responder) accepts, for checking an arriving request against the second of the two lengths a responder takes, following Reticulum@1.5.0 RNS/Link.py:187.signalling_bytes: Three bytes, which an opening packet (link request) or a proof packet (link request proof) may carry after its fixed body to state the link MTU and the cipher, for working out how much longer a request or a proof gets once it states an MTU and a cipher, following Reticulum@1.5.0 RNS/Link.py:80,148-151,312,371.signed_proof_bytes_max: Eighty-three bytes, the widest signing input a link request proof ever has, which runs the 16-byte session identifier (link id), then responder encryption key, then the signing key of the destination's identity, then whatever signalling rode along, for sizing the buffer a caller builds a proof's signing message in…
Source
Source: lib/reticulum/src/wire/link.zig
zig
const std = @import("std");const flags = @import("flags.zig");const hash = @import("hash.zig");const header = @import("header.zig");/// Sixty-four bytes, the payload of an opening packet (*link request*) with no/// signalling: two 32-byte keys back to back, the initiator's encryption key/// and then its signing key, for sizing or checking the payload an initiator/// sends to open an encrypted session (*link*), following Reticulum@1.5.0/// RNS/Link.py:70,189.pub const request_bytes: u8 = 64;/// Three bytes, which an opening packet (*link request*) or a proof packet/// (*link request proof*) may carry after its fixed body to state the link MTU/// and the cipher, for working out how much longer a request or a proof gets/// once it states an MTU and a cipher, following Reticulum@1.5.0/// RNS/Link.py:80,148-151,312,371.pub const signalling_bytes: u8 = 3;/// Sixty-seven bytes, a link request payload with signalling, which together/// with 64 makes the pair of lengths the receiving end (*responder*) accepts,/// for checking an arriving request against the second of the two lengths a/// responder takes, following Reticulum@1.5.0 RNS/Link.py:187.pub const signalled_request_bytes: u8 = request_bytes + signalling_bytes;/// Ninety-six bytes, a link request proof payload with no signalling: a 64-byte/// Ed25519 signature and then 32 bytes of the responder's encryption key, for/// sizing or checking the payload the receiving end (*responder*) sends back to/// accept an encrypted session (*link*), following Reticulum@1.5.0/// RNS/Link.py:405-406.pub const proof_bytes: u8 = 96;/// Ninety-nine bytes, a link request proof payload with signalling, and the one/// length from which an initiator reads a confirmed MTU, so the opening end/// (*initiator*) can check whether the proof it received settles the link MTU,/// following Reticulum@1.5.0 RNS/Link.py:399-402.pub const signalled_proof_bytes: u8 = proof_bytes + signalling_bytes;/// Eighty-three bytes, the widest signing input a link request proof ever has,/// which runs the 16-byte session identifier (*link id*), then responder/// encryption key, then the signing key of the destination's identity, then/// whatever signalling rode along, for sizing the buffer a caller builds a/// proof's signing message in, following Reticulum@1.5.0 RNS/Link.py:368,412.pub const signed_proof_bytes_max: u8 = header.truncated_hash_bytes + 32 + 32 + signalling_bytes;/// Nine bytes, what the opening end (*initiator*) sends to report its round/// trip time: a leading 0xcb, which is how MessagePack introduces a double, and/// then that double's eight bytes, most significant first, for sizing the/// plaintext that reports a round trip time over a new encrypted session/// (*link*), following Reticulum@1.5.0 RNS/vendor/umsgpack.py:329.pub const rtt_bytes: u8 = 9;/// 0xff, the one-byte payload an initiator alone sends to keep a quiet link/// alive, which the responder recognizes, following Reticulum@1.5.0/// RNS/Link.py:750,800. An initiator that hears one back reports a link state/// mismatch.pub const keepalive_request: u8 = 0xff;/// 0xfe, the one-byte payload a responder returns once a keepalive request/// reaches it, so an initiator recognizes its keepalive was heard, following/// Reticulum@1.5.0 RNS/Link.py:1131-1133.pub const keepalive_answer: u8 = 0xfe;const float64_marker: u8 = 0xcb;/// The three-bit cipher mode a link runs under, for a caller reading the cipher/// out of three signalled bits. The reference names eight values and enables/// AES-256-CBC alone, following Reticulum@1.5.0 RNS/Link.py:125-133. No/// three-bit value is left without a name here, so decoding one always lands on/// a cipher.pub const Mode = enum(u3) { aes128_cbc = 0, aes256_cbc = 1, aes256_gcm = 2, otp_reserved = 3, pq_reserved_1 = 4, pq_reserved_2 = 5, pq_reserved_3 = 6, pq_reserved_4 = 7,};/// AES-256-CBC, the cipher that a request or proof carrying no signalling runs/// under and the single cipher the reference turns on, for working out which/// cipher applies when a request or proof signalled nothing, following/// Reticulum@1.5.0 RNS/Link.py:133-134,176,183.pub const default_mode: Mode = .aes256_cbc;/// The link MTU and cipher a request or proof states, for a caller reading or/// stating what an encrypted session (*link*) will run under. The three bytes/// make one 24-bit value, most significant byte first, whose three highest bits/// carry the cipher and whose remaining 21 carry the MTU, following/// Reticulum@1.5.0 RNS/Link.py:144-151.pub const Signalling = struct { mtu: u21, mode: Mode, /// Returns the three bytes with the cipher in the three highest bits and /// the MTU in the 21 below them, most significant byte first, for a caller /// appending its signalling to the request or proof it is about to send, /// following Reticulum@1.5.0 RNS/Link.py:148-151. pub fn encode(self: Signalling) [signalling_bytes]u8 { const value = (@as(u24, @backingInt(self.mode)) << 21) | self.mtu; var bytes: [signalling_bytes]u8 = undefined; std.mem.writeInt(u24, &bytes, value, .big); return bytes; } /// Pulls the MTU out of the 21 lowest bits and the cipher out of the three /// highest for a caller reading the values an arriving request or proof /// carried, following Reticulum@1.5.0 RNS/Link.py:154-164,172-183. Requests /// and proofs signal in the same layout, so one reader serves both. pub fn decode(bytes: [signalling_bytes]u8) Signalling { const value = std.mem.readInt(u24, &bytes, .big); return .{ .mtu = @truncate(value), .mode = @fromBackingInt(@as(u3, @truncate(value >> 21))), }; }};/// The MTU 500 with AES-256-CBC that an initiator sends on an interface/// reporting no hardware MTU of its own, and that the responder confirms. The/// reference signals MTU 500 when the interface toward the next hop reports no/// hardware MTU of its own, following Reticulum@1.5.0 RNS/Link.py:307-310.pub const default_signalling: Signalling = .{ .mtu = header.mtu, .mode = default_mode };pub const DecodeError = error{InvalidLength};pub const EncodeError = error{OutputTooSmall};pub const RttError = error{InvalidRtt};/// The three fields of a link request payload to open a session: the/// initiator's encryption key, its signing key, and, once the payload runs long/// enough, the signalling, so a responder reads these three fields out of the/// request that reached it, following Reticulum@1.5.0 RNS/Link.py:312.pub const Request = struct { encryption_public: [32]u8, signing_public: [32]u8, signalling: ?Signalling, /// Splits a link request payload of 64 or 67 bytes to open a session into /// the two keys and any signalling for a responder, following /// Reticulum@1.5.0 RNS/Link.py:187-189. The call returns /// `error.InvalidLength` for every other length. pub fn decode(payload: []const u8) DecodeError!Request { const signalling: ?Signalling = switch (payload.len) { request_bytes => null, signalled_request_bytes => Signalling.decode( payload[request_bytes..][0..signalling_bytes].*, ), else => return error.InvalidLength, }; return .{ .encryption_public = payload[0..32].*, .signing_public = payload[32..request_bytes].*, .signalling = signalling, }; } pub fn encodedLength(self: Request) u8 { return if (self.signalling == null) request_bytes else signalled_request_bytes; } /// Puts both keys at the start of `out`, follows them with the signalling /// when there is any, and gives back what it wrote, so the initiator /// sending the request writes it into its outgoing buffer, following /// Reticulum@1.5.0 RNS/Link.py:312. The call returns `error.OutputTooSmall` /// when `out` is shorter than the payload. pub fn encode(self: Request, out: []u8) EncodeError![]u8 { const length: usize = self.encodedLength(); if (out.len < length) return error.OutputTooSmall; out[0..32].* = self.encryption_public; out[32..request_bytes].* = self.signing_public; if (self.signalling) |value| out[request_bytes..][0..signalling_bytes].* = value.encode(); return out[0..length]; } /// Returns the cipher mode that the request signalled, and AES-256-CBC when /// it signalled none, so a responder learns which cipher the initiator /// asked for, following Reticulum@1.5.0 RNS/Link.py:172-176. pub fn mode(self: Request) Mode { const value = self.signalling orelse return default_mode; return value.mode; }};/// The three fields of a link request proof payload confirming acceptance: the/// signature made by the destination's identity, the responder's encryption/// key, and, once the payload runs long enough, the signalling, so an initiator/// reads these three fields out of the proof that came back, following/// Reticulum@1.5.0 RNS/Link.py:369-371.pub const Proof = struct { signature: [64]u8, encryption_public: [32]u8, signalling: ?Signalling, /// Splits a link request proof payload of 96 or 99 bytes confirming /// acceptance into the signature, the key, and any signalling for an /// initiator, following Reticulum@1.5.0 RNS/Link.py:399-406. The call /// returns `error.InvalidLength` for every other length. pub fn decode(payload: []const u8) DecodeError!Proof { const signalling: ?Signalling = switch (payload.len) { proof_bytes => null, signalled_proof_bytes => Signalling.decode( payload[proof_bytes..][0..signalling_bytes].*, ), else => return error.InvalidLength, }; return .{ .signature = payload[0..64].*, .encryption_public = payload[64..proof_bytes].*, .signalling = signalling, }; } pub fn encodedLength(self: Proof) u8 { return if (self.signalling == null) proof_bytes else signalled_proof_bytes; } /// Puts the signature at the start of `out`, follows it with the encryption /// key and then the signalling when there is any, and gives back what it /// wrote, so the responder confirming the link writes the proof into its /// outgoing buffer, following Reticulum@1.5.0 RNS/Link.py:371. The call /// returns `error.OutputTooSmall` when `out` is shorter than the payload. pub fn encode(self: Proof, out: []u8) EncodeError![]u8 { const length: usize = self.encodedLength(); if (out.len < length) return error.OutputTooSmall; out[0..64].* = self.signature; out[64..proof_bytes].* = self.encryption_public; if (self.signalling) |value| out[proof_bytes..][0..signalling_bytes].* = value.encode(); return out[0..length]; }};/// Reads the cipher mode out of the top three bits of byte 96 once a proof/// payload runs past 96 bytes, and gives AES-256-CBC for a payload of 96 bytes/// or fewer, so a caller reads a proof's cipher without splitting the whole/// payload first, following Reticulum@1.5.0 RNS/Link.py:179-183,396-398.pub fn proofMode(payload: []const u8) Mode { if (payload.len <= proof_bytes) return default_mode; return @fromBackingInt(@as(u3, @truncate(payload[proof_bytes] >> 5)));}/// Builds in `out` the bytes a link request proof puts its signature over, and/// returns them, running the 16-byte link id naming the session, then responder/// encryption key, then the signing key of the destination's identity, then/// whatever signalling rode along, so a responder builds the message it signs/// and an initiator builds the same message to check that signature, following/// Reticulum@1.5.0 RNS/Link.py:368,412. A proof with no signalling signs 80/// bytes, and one with signalling signs 83.pub fn signedProof( link_id: [header.truncated_hash_bytes]u8, encryption_public: [32]u8, signing_public: [32]u8, signalling: ?Signalling, out: *[signed_proof_bytes_max]u8,) []const u8 { out[0..16].* = link_id; out[16..48].* = encryption_public; out[48..80].* = signing_public; const value = signalling orelse return out[0..80]; out[80..signed_proof_bytes_max].* = value.encode(); return out[0..signed_proof_bytes_max];}/// Returns the 16-byte truncated hash that names an encrypted session between/// two endpoints (*link*), taken over the request packet's hashable part with/// everything past the 64 key bytes left off, following Reticulum@1.5.0/// RNS/Link.py:335-342. Trimming that way keeps the name the same with or/// without three signalling bytes, and with or without the sixteen bytes a/// carrying node inserts, so a relay and the two ends all arrive at one link/// id. The function returns `error.PacketTooShort` when `raw` is shorter than/// the header its flags byte claims.pub fn linkId(raw: []const u8) hash.HashError![header.truncated_hash_bytes]u8 { if (raw.len < 1) return error.PacketTooShort; const payload_start: usize = header.headerLength(flags.Flags.decode(raw[0]).header); if (raw.len < payload_start) return error.PacketTooShort; const excess = (raw.len - payload_start) -| request_bytes; return hash.truncated(raw[0 .. raw.len - excess]);}/// Returns the nine bytes that report a round trip time in seconds: the 0xcb/// marker and then the double's bits, most significant byte first, following/// Reticulum@1.5.0 RNS/vendor/umsgpack.py:325-333. The call asserts that the/// seconds it was handed are finite and that their sign bit is clear.pub fn encodeRtt(seconds: f64) [rtt_bytes]u8 { std.debug.assert(std.math.isFinite(seconds)); std.debug.assert(!std.math.signbit(seconds)); var bytes: [rtt_bytes]u8 = undefined; bytes[0] = float64_marker; std.mem.writeInt(u64, bytes[1..rtt_bytes], @bitCast(seconds), .big); return bytes;}/// Returns the round trip time carried by a nine-byte plaintext whose first/// byte is 0xcb and whose remaining eight hold a double that is finite with its/// sign bit clear, so a responder reads what the initiator reported to bring/// the link up, the form Reticulum@1.5.0 RNS/vendor/umsgpack.py:768-769/// accepts. The function returns `error.InvalidRtt` for every other plaintext,/// negative zero, negative values, the infinities, and NaN among them. The/// reference reads more forms than this: it unpacks any MessagePack value at/// Reticulum@1.5.0 RNS/Link.py:521 and hands it to `max` at Reticulum@1.5.0/// RNS/Link.py:522, so a 32-bit float under the 0xca marker, an integer, or any/// other floating value reaches it as well. The reference drops the link at/// Reticulum@1.5.0 RNS/Link.py:536-538 whenever either that unpacking or that/// comparison throws.pub fn decodeRtt(plaintext: []const u8) RttError!f64 { if (plaintext.len != rtt_bytes) return error.InvalidRtt; if (plaintext[0] != float64_marker) return error.InvalidRtt; const seconds: f64 = @bitCast(std.mem.readInt(u64, plaintext[1..rtt_bytes], .big)); if (!std.math.isFinite(seconds)) return error.InvalidRtt; if (std.math.signbit(seconds)) return error.InvalidRtt; return seconds;}/// Returns whether the decrypted plaintext is exactly the link id, the payload/// carried by the packet an end sends to close the link, so a caller decides/// whether that plaintext closes the link, following Reticulum@1.5.0/// RNS/Link.py:674-678. No other plaintext closes a link.pub fn closes(plaintext: []const u8, link_id: [header.truncated_hash_bytes]u8) bool { return std.mem.eql(u8, plaintext, &link_id);}fn patternedKey(salt: u8) [32]u8 { var bytes: [32]u8 = undefined; for (&bytes, 0..) |*byte, index| byte.* = @intCast((index * 37 + salt) & 0xff); return bytes;}fn requestFrame( payload: []const u8, transport_id: ?[16]u8, hops: u8, out: *[header.mtu]u8,) ![]u8 { return header.encode(.{ .ifac = 0, .header = if (transport_id == null) .one else .two, .context_flag = 0, .transport = if (transport_id == null) .broadcast else .transport, .destination_type = .single, .packet_type = .link_request, .hops = hops, .transport_id = transport_id, .destination = @splat(0x5d), .context = .none, .payload = payload, }, out);}test "Reticulum@1.5.0 RNS/Link.py:148-151 signals MTU 500 and AES-256-CBC as 2001f4" { const bytes = default_signalling.encode(); try std.testing.expectEqualSlices(u8, &.{ 0x20, 0x01, 0xf4 }, &bytes); try std.testing.expectEqual(default_signalling, Signalling.decode(bytes));}test "Reticulum@1.5.0 RNS/Link.py:144-183 signalling keeps every mode and 21 MTU bits" { const mtus = [_]u21{ 0, 1, header.mtu, std.math.maxInt(u21) }; for (std.enums.values(Mode)) |value| { for (mtus) |mtu| { const signalling = Signalling{ .mtu = mtu, .mode = value }; const bytes = signalling.encode(); try std.testing.expectEqual(@as(u8, @backingInt(value)), bytes[0] >> 5); try std.testing.expectEqual(signalling, Signalling.decode(bytes)); } }}test "Reticulum@1.5.0 RNS/Link.py:187 decodes only 64 and 67 request bytes" { var payload: [header.mtu]u8 = undefined; for (&payload, 0..) |*byte, index| byte.* = @intCast(index & 0xff); for (0..payload.len + 1) |length| { const decoded = Request.decode(payload[0..length]) catch |err| { try std.testing.expectEqual(error.InvalidLength, err); try std.testing.expect(length != request_bytes and length != signalled_request_bytes); continue; }; try std.testing.expect(length == request_bytes or length == signalled_request_bytes); try std.testing.expectEqual(length == signalled_request_bytes, decoded.signalling != null); var out: [signalled_request_bytes]u8 = undefined; try std.testing.expectEqualSlices(u8, payload[0..length], try decoded.encode(&out)); }}test "Reticulum@1.5.0 RNS/Link.py:172-176 uses the default request mode without signalling" { const unsignalled = Request{ .encryption_public = patternedKey(1), .signing_public = patternedKey(2), .signalling = null, }; try std.testing.expectEqual(default_mode, unsignalled.mode()); var signalled = unsignalled; signalled.signalling = .{ .mtu = header.mtu, .mode = .aes128_cbc }; try std.testing.expectEqual(Mode.aes128_cbc, signalled.mode()); var out: [signalled_request_bytes - 1]u8 = undefined; try std.testing.expectError(error.OutputTooSmall, signalled.encode(&out));}test "Reticulum@1.5.0 RNS/Link.py:399-406 decodes only 96 and 99 proof bytes" { var payload: [header.mtu]u8 = undefined; for (&payload, 0..) |*byte, index| byte.* = @intCast((index * 7) & 0xff); for (0..payload.len + 1) |length| { const decoded = Proof.decode(payload[0..length]) catch |err| { try std.testing.expectEqual(error.InvalidLength, err); try std.testing.expect(length != proof_bytes and length != signalled_proof_bytes); continue; }; try std.testing.expect(length == proof_bytes or length == signalled_proof_bytes); var out: [signalled_proof_bytes]u8 = undefined; try std.testing.expectEqualSlices(u8, payload[0..length], try decoded.encode(&out)); }}test "Reticulum@1.5.0 RNS/Link.py:179-183 reads a proof mode past byte 95" { var payload: [signalled_proof_bytes + 1]u8 = @splat(0); try std.testing.expectEqual(default_mode, proofMode(payload[0..proof_bytes])); payload[proof_bytes] = 0x40; try std.testing.expectEqual(Mode.aes256_gcm, proofMode(payload[0 .. proof_bytes + 1])); try std.testing.expectEqual(Mode.aes256_gcm, proofMode(&payload)); payload[proof_bytes] = 0xff; try std.testing.expectEqual(Mode.pq_reserved_4, proofMode(payload[0..signalled_proof_bytes]));}test "Reticulum@1.5.0 RNS/Link.py:368,412 signs signalling only when a proof carries it" { var out: [signed_proof_bytes_max]u8 = undefined; const link_id: [16]u8 = @splat(0x11); const unsignalled = signedProof(link_id, patternedKey(3), patternedKey(4), null, &out); try std.testing.expectEqual(@as(usize, 80), unsignalled.len); try std.testing.expectEqualSlices(u8, &link_id, unsignalled[0..16]); try std.testing.expectEqualSlices(u8, &patternedKey(3), unsignalled[16..48]); try std.testing.expectEqualSlices(u8, &patternedKey(4), unsignalled[48..80]); const signalled = signedProof( link_id, patternedKey(3), patternedKey(4), default_signalling, &out, ); try std.testing.expectEqual(@as(usize, signed_proof_bytes_max), signalled.len); try std.testing.expectEqualSlices(u8, &.{ 0x20, 0x01, 0xf4 }, signalled[80..]);}test "Reticulum@1.5.0 RNS/Link.py:335-342 link ids ignore signalling and transport headers" { const request = Request{ .encryption_public = patternedKey(5), .signing_public = patternedKey(6), .signalling = default_signalling, }; var payload: [signalled_request_bytes]u8 = undefined; const signalled = try request.encode(&payload); var frames: [4][header.mtu]u8 = undefined; const direct = try requestFrame(signalled, null, 0, &frames[0]); const stripped = try requestFrame(signalled[0..request_bytes], null, 1, &frames[1]); const inserted = try requestFrame(signalled, @splat(0x77), 2, &frames[2]); const expected = try hash.truncated(stripped); try std.testing.expectEqualSlices(u8, &expected, &try linkId(direct)); try std.testing.expectEqualSlices(u8, &expected, &try linkId(stripped)); try std.testing.expectEqualSlices(u8, &expected, &try linkId(inserted)); const shorter = try requestFrame(signalled[0 .. request_bytes - 1], null, 0, &frames[3]); try std.testing.expectEqualSlices(u8, &try hash.truncated(shorter), &try linkId(shorter)); try std.testing.expectError(error.PacketTooShort, linkId(&.{})); const partial_header = direct[0 .. header.header_one_bytes - 1]; try std.testing.expectError(error.PacketTooShort, linkId(partial_header));}test "Reticulum@1.5.0 RNS/vendor/umsgpack.py:329 encodes an RTT of 0.25 as cb3fd0000000000000" { const bytes = encodeRtt(0.25); const expected = [_]u8{ 0xcb, 0x3f, 0xd0, 0, 0, 0, 0, 0, 0 }; try std.testing.expectEqualSlices(u8, &expected, &bytes); try std.testing.expectEqual(@as(f64, 0.25), try decodeRtt(&bytes)); try std.testing.expectEqual(@as(f64, 0), try decodeRtt(&encodeRtt(0)));}test "RTT decoding rejects every first byte other than 0xcb" { var plaintext = encodeRtt(1.0); for (0..256) |byte| { plaintext[0] = @intCast(byte); if (byte == float64_marker) { try std.testing.expectEqual(@as(f64, 1.0), try decodeRtt(&plaintext)); } else { try std.testing.expectError(error.InvalidRtt, decodeRtt(&plaintext)); } }}test "RTT decoding rejects other lengths, non-finite values, and set sign bits" { var long: [rtt_bytes + 1]u8 = @splat(0); long[0] = float64_marker; for (0..long.len + 1) |length| { if (length == rtt_bytes) continue; try std.testing.expectError(error.InvalidRtt, decodeRtt(long[0..length])); } const rejected = [_]f64{ -0.0, -1.0, std.math.inf(f64), -std.math.inf(f64), std.math.nan(f64), }; for (rejected) |seconds| { var plaintext: [rtt_bytes]u8 = undefined; plaintext[0] = float64_marker; std.mem.writeInt(u64, plaintext[1..rtt_bytes], @bitCast(seconds), .big); try std.testing.expectError(error.InvalidRtt, decodeRtt(&plaintext)); } const largest = encodeRtt(std.math.floatMax(f64)); try std.testing.expectEqual(std.math.floatMax(f64), try decodeRtt(&largest));}test "Reticulum@1.5.0 RNS/Link.py:674-683 closes only on the link id plaintext" { const link_id: [16]u8 = @splat(0x3c); try std.testing.expect(closes(&link_id, link_id)); var changed = link_id; changed[15] ^= 1; try std.testing.expect(!closes(&changed, link_id)); try std.testing.expect(!closes(link_id[0..15], link_id)); try std.testing.expect(!closes(&.{}, link_id));}Source: lib/reticulum/src/wire/root.zig:52
zig
pub const link = @import("link.zig");Complete caller list for wire.link.decodeRtt
7 direct callers.
lib.reticulum.src.node.link.acceptRtt[function] — private source atlib/reticulum/src/node/link.zig:1048in nearest public ownertiny.reticulum.node.linklib.reticulum.src.properties.link.RttPlaintexts.property[function] — private source atlib/reticulum/src/properties/link.zig:84in nearest public ownerlib.reticulum.src.properties.linklib.reticulum.src.properties.link.RttRoundTrip.property[function] — private source atlib/reticulum/src/properties/link.zig:40in nearest public ownerlib.reticulum.src.properties.linklib.reticulum.src.wire.link.test_RTT_decoding_rejects_every_first_byte_other_than_0xcb[function] — test source atlib/reticulum/src/wire/link.zig:465in nearest public ownertiny.reticulum.wire.linklib.reticulum.src.wire.link.test_RTT_decoding_rejects_other_lengths,_non-finite_values,_and_set_sign_bits[function] — test source atlib/reticulum/src/wire/link.zig:477in nearest public ownertiny.reticulum.wire.linklib.reticulum.src.wire.link.test_Reticulum@1.5.0_RNS/vendor/umsgpack.py:329_encodes_an_RTT_of_0.25_as_cb3fd0000000000000[function] — test source atlib/reticulum/src/wire/link.zig:457in nearest public ownertiny.reticulum.wire.linklib.reticulum.src.wire.test.test_Reticulum@1.5.0_RNS/Link.py:419-437,516-522_link_RTT_corpus_is_a_whole-second_float64[function] — test source atlib/reticulum/src/wire/test.zig:362in nearest public ownerlib.reticulum.src.wire.test
Complete caller list for wire.link.encodeRtt
7 direct callers.
lib.reticulum.src.node.link.establishInitiator[function] — private source atlib/reticulum/src/node/link.zig:1014in nearest public ownertiny.reticulum.node.linklib.reticulum.src.properties.link.RttPlaintexts.property[function] — private source atlib/reticulum/src/properties/link.zig:84in nearest public ownerlib.reticulum.src.properties.linklib.reticulum.src.properties.link.RttRoundTrip.property[function] — private source atlib/reticulum/src/properties/link.zig:40in nearest public ownerlib.reticulum.src.properties.linklib.reticulum.src.wire.link.test_RTT_decoding_rejects_every_first_byte_other_than_0xcb[function] — test source atlib/reticulum/src/wire/link.zig:465in nearest public ownertiny.reticulum.wire.linklib.reticulum.src.wire.link.test_RTT_decoding_rejects_other_lengths,_non-finite_values,_and_set_sign_bits[function] — test source atlib/reticulum/src/wire/link.zig:477in nearest public ownertiny.reticulum.wire.linklib.reticulum.src.wire.link.test_Reticulum@1.5.0_RNS/vendor/umsgpack.py:329_encodes_an_RTT_of_0.25_as_cb3fd0000000000000[function] — test source atlib/reticulum/src/wire/link.zig:457in nearest public ownertiny.reticulum.wire.linklib.reticulum.src.wire.test.test_Reticulum@1.5.0_RNS/Link.py:419-437,516-522_link_RTT_corpus_is_a_whole-second_float64[function] — test source atlib/reticulum/src/wire/test.zig:362in nearest public ownerlib.reticulum.src.wire.test
Complete caller list for wire.link.linkId
7 direct callers.
lib.reticulum.src.node.link.answerRequest[function] — private source atlib/reticulum/src/node/link.zig:902in nearest public ownertiny.reticulum.node.linktiny.reticulum.node.link.open[function] atlib/reticulum/src/node/link.zig:470tiny.reticulum.node.transport.link.relay.request[function] atlib/reticulum/src/node/transport/link/relay.zig:169lib.reticulum.src.node.transport.test.test_Reticulum@1.5.0_RNS/Transport.py:1968-2077_corpus_link_relays_match_node_frames[function] — test source atlib/reticulum/src/node/transport/test.zig:932in nearest public ownerlib.reticulum.src.node.transport.testlib.reticulum.src.properties.link.LinkIds.property[function] — private source atlib/reticulum/src/properties/link.zig:176in nearest public ownerlib.reticulum.src.properties.linklib.reticulum.src.wire.link.test_Reticulum@1.5.0_RNS/Link.py:335-342_link_ids_ignore_signalling_and_transport_headers[function] — test source atlib/reticulum/src/wire/link.zig:434in nearest public ownertiny.reticulum.wire.linklib.reticulum.src.wire.test.test_Reticulum@1.5.0_RNS/Link.py:304-346_link_request_corpus_and_its_stripped_link_id[function] — test source atlib/reticulum/src/wire/test.zig:284in nearest public ownerlib.reticulum.src.wire.test
Audit
| Definitions | 34 |
|---|---|
| Public names | 34 |
| Members | 19 |
| Version | 26.7.0 |
| Revision | daab053ee433 |