Skip to documentation
SLOP

tiny.reticulum.destination.announce

Reference tiny.reticulum destination announce

Defined in destination.

API (12)

Actions

Public operations.

Types and contracts

Public types and contracts.

Values and defaults

Public values and defaults.

No direct callersNo direct callsdestinationannounce
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Called byCallsprivate sourcelib.reticulum.src.destination.testcheckAnnounceVectortest sourcelib.reticulum.src.destination.testtest: announce app-data limits accept...test sourcelib.reticulum.src.destination.testtest: announce output and payload lim...private sourcelib.reticulum.src.node.announcebuildFrameprivate sourcelib.reticulum.src.properties.announce.Rebuildpropertyprivate sourcelib.reticulum.src.destination.announceappMaximumprivate sourcelib.reticulum.src.destination.announceappendprivate sourcelib.reticulum.src.destination.announcesignatureOffsetprivate sourcelib.reticulum.src.destination.announcewritePrefixdestination.announcebuild
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsNo direct callsdestination.announcevalidatetest sourcelib.reticulum.src.destination.test.test_Retic...py:236-237 announce validation bounds...destination.announcepayloadBytesMax
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsprivate sourcelib.reticulum.src.destination.testcheckAnnounceVectortest sourcelib.reticulum.src.destination.test.test_Retic...py:236-237 announce validation bounds...test sourcelib.reticulum.src.destination.testtest: announce output and payload lim...private sourcelib.reticulum.src.node.inboundacceptAnnounceprivate sourcelib.reticulum.src.properties.announce.Rebuildpropertyprivate sourcelib.reticulum.src.properties.announce.Totalpropertyprivate sourcelib.reticulum.src.destination.announceannouncedDestinationdestination.announcepayloadBytesMaxprivate sourcelib.reticulum.src.destination.announcesignatureOffsetprivate sourcelib.reticulum.src.destination.announcesignedTranscriptdestination.announcevalidate
Static calls · unresolved targets: 0 · external targets: 2.

Source: lib/reticulum/src/destination/announce.zig

zig
const std = @import("std");const reticulum = @import("../root.zig");const identity = reticulum.identity;const wire = reticulum.wire;const public_bytes: usize = 64;const name_hash_bytes: usize = 10;const random_hash_bytes: usize = 10;const rotating_bytes: usize = 32;const signature_bytes: usize = 64;const destination_hash_bytes: usize = 16;const prefix_bytes: usize = public_bytes + name_hash_bytes + random_hash_bytes;const fixed_payload_bytes: u16 = @intCast(prefix_bytes + signature_bytes);/// 465 bytes, the most an announce payload holds, the room left behind a/// 35-byte header, following Reticulum@1.5.0 RNS/Reticulum.py:93,151. The same/// bound covers every announce this package builds and every announce a node/// forwards behind a 35-byte header.pub const payload_bytes_max: u16 = 465;/// 317 bytes, the application data an announce carries once its fixed 148 bytes/// are taken.pub const app_bytes_max: u16 = payload_bytes_max - fixed_payload_bytes;/// 285 bytes, the application data an announce carries once a 32-byte rotating/// key takes its place in the payload.pub const rotating_app_bytes_max: u16 = app_bytes_max - @as(u16, rotating_bytes);/// 481 bytes, the most payload a packet holds behind a 19-byte header,/// following Reticulum@1.5.0 RNS/Packet.py:236-237.pub const received_payload_bytes_max: u16 = wire.mtu - wire.header_one_bytes;/// 333 bytes, the application data an announce carries behind a 19-byte header.pub const received_app_bytes_max: u16 = received_payload_bytes_max - fixed_payload_bytes;const signed_bytes_max: usize = @as(usize, received_payload_bytes_max) - signature_bytes +    destination_hash_bytes;comptime {    std.debug.assert(payload_bytes_max == wire.mtu - wire.header_two_bytes);    std.debug.assert(received_payload_bytes_max == wire.mtu - wire.header_one_bytes);}/// Returns how much payload a packet holds behind the given header shape, which/// is 500 bytes less that header, following Reticulum@1.5.0/// RNS/Packet.py:236-237.pub fn payloadBytesMax(header: wire.HeaderType) u16 {    return wire.mtu - wire.headerLength(header);}pub const Fields = struct {    destination_hash: [destination_hash_bytes]u8,    name_hash: [name_hash_bytes]u8,    random_hash: [random_hash_bytes]u8,    rotating_public_key: ?[rotating_bytes]u8 = null,    app_data: []const u8 = &.{},};pub const BuildError = error{    AppDataTooLong,    OutputTooSmall,};pub const ValidateError = error{    PayloadTooShort,    PayloadTooLong,    InvalidSignature,    DestinationMismatch,};/// The fields of a checked announce: the public key, the name hash, the ten/// random bytes, the rotating public key when the announce carries one, the/// signature, the application data, and the context flag. Every slice points/// into the payload the caller passed in, so it stays good as long as that/// payload does.pub const Announce = struct {    public_key: identity.BorrowedPublic,    name_hash: []const u8,    random_hash: []const u8,    rotating_public_key: ?[]const u8,    signature: []const u8,    app_data: []const u8,    context_flag: u1,};fn append(output: []u8, cursor: *usize, bytes: []const u8) void {    std.debug.assert(cursor.* <= output.len);    std.debug.assert(bytes.len <= output.len - cursor.*);    @memcpy(output[cursor.*..][0..bytes.len], bytes);    cursor.* += bytes.len;}fn signatureOffset(has_rotating: bool) usize {    return prefix_bytes + if (has_rotating) rotating_bytes else 0;}fn appMaximum(has_rotating: bool) usize {    return if (has_rotating) rotating_app_bytes_max else app_bytes_max;}fn writePrefix(fields: Fields, public: identity.KeyBytes, output: []u8) usize {    var cursor: usize = 0;    append(output, &cursor, &public);    append(output, &cursor, &fields.name_hash);    append(output, &cursor, &fields.random_hash);    if (fields.rotating_public_key) |rotating| append(output, &cursor, &rotating);    return cursor;}/// Writes the signed announce payload into `output` and returns it: the 64/// public bytes, the name hash, the ten random bytes, the rotating public key/// when the caller passes one, the signature, and the application data,/// following Reticulum@1.5.0 RNS/Destination.py:244-304. What the signature/// covers starts with the destination hash, then the payload up to the/// signature, then the application data. The call returns/// `error.AppDataTooLong` past 317 bytes, or past 285 when the announce carries/// a rotating key, and `error.OutputTooSmall` when `output` is shorter than the/// payload. The output buffer has to lie apart from the private key storage and/// from the application data.pub fn build(    fields: Fields,    private: *const identity.Private,    output: []u8,) BuildError![]u8 {    const has_rotating = fields.rotating_public_key != null;    if (fields.app_data.len > appMaximum(has_rotating)) return error.AppDataTooLong;    const signature_offset = signatureOffset(has_rotating);    const payload_bytes = signature_offset + signature_bytes + fields.app_data.len;    if (output.len < payload_bytes) return error.OutputTooSmall;    const public = private.publicBytes();    const prefix_end = writePrefix(fields, public, output);    std.debug.assert(prefix_end == signature_offset);    var signed: [signed_bytes_max]u8 = undefined;    var signed_end: usize = 0;    append(&signed, &signed_end, &fields.destination_hash);    append(&signed, &signed_end, output[0..prefix_end]);    append(&signed, &signed_end, fields.app_data);    const signature = private.sign(signed[0..signed_end]);    @memcpy(output[signature_offset..][0..signature_bytes], &signature);    @memcpy(output[signature_offset + signature_bytes ..][0..fields.app_data.len], fields.app_data);    return output[0..payload_bytes];}fn announcedDestination(announce: Announce) [destination_hash_bytes]u8 {    const identity_hash = announce.public_key.hash();    var hasher = reticulum.hash.Hasher.init();    hasher.update(announce.name_hash);    hasher.update(&identity_hash);    return hasher.finalTruncated();}fn signedTranscript(    destination_hash: [destination_hash_bytes]u8,    payload: []const u8,    signature_offset: usize,    app_data: []const u8,    output: *[signed_bytes_max]u8,) []const u8 {    var cursor: usize = 0;    append(output, &cursor, &destination_hash);    append(output, &cursor, payload[0..signature_offset]);    append(output, &cursor, app_data);    return output[0..cursor];}/// Checks an announce payload and returns its fields, following Reticulum@1.5.0/// RNS/Identity.py:511-565. A context flag of one says the payload carries a/// 32-byte rotating key between the random bytes and the signature. The/// signature is checked against the public key in the payload, over the/// destination hash followed by the payload up to the signature and then the/// application data. The address is recomputed from the announce's own name/// hash and the hash of its public key, and it has to equal the address the/// packet was sent to. The call returns `error.PayloadTooShort` below the fixed/// bytes, `error.PayloadTooLong` past what the header shape allows,/// `error.InvalidSignature`, and `error.DestinationMismatch`.pub fn validate(    destination_hash: [destination_hash_bytes]u8,    header: wire.HeaderType,    context_flag: u1,    payload: []const u8,) ValidateError!Announce {    const has_rotating = context_flag == 1;    const signature_offset = signatureOffset(has_rotating);    const fixed_bytes = signature_offset + signature_bytes;    if (payload.len < fixed_bytes) return error.PayloadTooShort;    if (payload.len > payloadBytesMax(header)) return error.PayloadTooLong;    std.debug.assert(payload.len - signature_bytes + destination_hash_bytes <= signed_bytes_max);    const public_pointer: *const identity.KeyBytes = @ptrCast(payload.ptr);    const app_data = payload[fixed_bytes..];    const announce = Announce{        .public_key = identity.BorrowedPublic.fromBytes(public_pointer),        .name_hash = payload[public_bytes..][0..name_hash_bytes],        .random_hash = payload[public_bytes + name_hash_bytes ..][0..random_hash_bytes],        .rotating_public_key = if (has_rotating)            payload[prefix_bytes..][0..rotating_bytes]        else            null,        .signature = payload[signature_offset..][0..signature_bytes],        .app_data = app_data,        .context_flag = context_flag,    };    var signed: [signed_bytes_max]u8 = undefined;    const transcript = signedTranscript(        destination_hash,        payload,        signature_offset,        app_data,        &signed,    );    if (!announce.public_key.validate(announce.signature[0..signature_bytes].*, transcript)) {        return error.InvalidSignature;    }    if (!std.mem.eql(u8, &announcedDestination(announce), &destination_hash)) {        return error.DestinationMismatch;    }    return announce;}

Source: lib/reticulum/src/destination/root.zig:57

zig
pub const announce = @import("announce.zig");

Audit

Definitions13
Public names13
Members18
Version26.7.0
Revisiondaab053ee433