Skip to documentation
SLOP

tiny.reticulum.crypto.token

Reference tiny.reticulum crypto token

Defined in crypto.

API (11)

Actions

Public operations.

Types and contracts

Public types and contracts.

Values and defaults

Public values and defaults.

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

Source

Called byCallstest sourcelib.reticulum.src.crypto.test.test_Reticulum@...py:61-114 differential corpustest sourcelib.reticulum.src.crypto.test.test_Reticulum@...py:77-114 authenticated short tokenprivate sourcelib.reticulum.src.destination.cipherdecryptGroupprivate sourcelib.reticulum.src.identity.cipherdecryptWithKeyprivate sourcelib.reticulum.src.node.linkacceptClose+4 morecrypto.pkcs7unpadcrypto.token.Tokenverifyprivate sourcelib.reticulum.src.crypto.tokenoverlapscrypto.token.Tokendecrypt
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallstest sourcelib.reticulum.src.crypto.test.test_Reticulum@...py:61-114 differential corpusdestination.cipherencryptidentity.cipherencryptprivate sourcelib.reticulum.src.node.linkencryptedFrameprivate sourcelib.reticulum.src.properties.cryptoexpectTokenRoundTripcrypto.hmacsigncrypto.pkcs7padcrypto.tokenencryptedLengthprivate sourcelib.reticulum.src.crypto.tokenoverlapscrypto.token.Tokenencrypt
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsNo direct callstest sourcelib.reticulum.src.crypto.test.test_Reticulum@...py:61-114 differential corpustest sourcelib.reticulum.src.crypto.test.test_Reticulum@...py:77-114 authenticated short tokentest sourcelib.reticulum.src.crypto.testtest: token u16 maximum and maximum p...private sourcelib.reticulum.src.destination.cipherdecryptGroupdestination.cipherencrypt+8 morecrypto.token.Tokeninit
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.reticulum.src.crypto.test.test_Reticulum@...py:61-114 differential corpustest sourcelib.reticulum.src.crypto.test.test_Reticulum@...py:77-114 authenticated short tokentest sourcelib.reticulum.src.crypto.testtest: token u16 maximum and maximum p...crypto.token.Tokendecryptprivate sourcelib.reticulum.src.properties.cryptoexpectTokenRoundTripcrypto.hmacverifycrypto.token.Tokenverify
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.reticulum.src.crypto.testtest: token u16 maximum and maximum p...crypto.token.Tokenencryptidentity.cipherencryptedLengthcrypto.pkcs7paddedLengthcrypto.tokenencryptedLength
Static calls · unresolved targets: 0 · external targets: 0.

Source: lib/reticulum/src/crypto/root.zig:49

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

Source: lib/reticulum/src/crypto/token.zig

zig
const std = @import("std");const crypto = @import("root.zig");pub const iv_length: u8 = crypto.cbc.block_length;pub const overhead: u16 = iv_length + crypto.hmac.tag_length;pub const max_plaintext_length: u16 = 65_471;pub const max_encrypted_length: u16 = 65_520;const FormatError = error{    InvalidKeyLength,    PlaintextTooLong,    InvalidToken,};pub const TokenError = crypto.cbc.CbcError || crypto.pkcs7.PadError ||    crypto.pkcs7.UnpadError || FormatError;const Mode = enum {    aes128,    aes256,};pub fn encryptedLength(plaintext_length: u17) TokenError!u16 {    if (plaintext_length > max_plaintext_length) return error.PlaintextTooLong;    const padded_length = try crypto.pkcs7.paddedLength(plaintext_length);    const result = @as(u32, padded_length) + overhead;    std.debug.assert(result <= max_encrypted_length);    return @intCast(result);}/// A key split in half, the first half signing and the second half encrypting/// (*token key*), for a caller holding a shared key to seal or open payloads,/// following Reticulum@1.5.0 RNS/Cryptography/Token.py:61-72. The value borrows/// the caller's key bytes, so they have to outlive it. A 32-byte key selects/// AES-128 and a 64-byte key selects AES-256, and any other length returns/// `error.InvalidKeyLength`.pub const Token = struct {    signing_key: []const u8,    encryption_key: []const u8,    mode: Mode,    pub fn init(key: []const u8) TokenError!Token {        return switch (key.len) {            32 => .{                .signing_key = key[0..16],                .encryption_key = key[16..32],                .mode = .aes128,            },            64 => .{                .signing_key = key[0..32],                .encryption_key = key[32..64],                .mode = .aes256,            },            else => error.InvalidKeyLength,        };    }    /// Writes the sealed form into `out` and returns it: the initialization    /// vector, the padded ciphertext, then the tag over everything before it,    /// for a caller to seal a payload under the shared key, following    /// Reticulum@1.5.0 RNS/Cryptography/Token.py:87-97. The caller supplies the    /// initialization vector and owns the source it drew it from. The call    /// returns `error.PlaintextTooLong` past 65,471 bytes,    /// `error.OutputTooSmall` when `out` is shorter than the sealed form, and    /// `error.OverlappingBuffers` when either key half overlaps the output.    pub fn encrypt(        self: Token,        iv: [iv_length]u8,        plaintext: []const u8,        out: []u8,    ) TokenError![]u8 {        if (plaintext.len > max_plaintext_length) return error.PlaintextTooLong;        const result_length: usize = try encryptedLength(@intCast(plaintext.len));        if (out.len < result_length) return error.OutputTooSmall;        const result = out[0..result_length];        if (overlaps(self.signing_key, result)) return error.OverlappingBuffers;        if (overlaps(self.encryption_key, result)) return error.OverlappingBuffers;        const ciphertext_length = result_length - overhead;        const ciphertext_out = result[iv_length..][0..ciphertext_length];        const padded = try crypto.pkcs7.pad(plaintext, ciphertext_out);        std.debug.assert(padded.len == ciphertext_length);        @memcpy(result[0..iv_length], &iv);        switch (self.mode) {            .aes128 => _ = try crypto.cbc.Aes128Cbc.encrypt(                self.encryption_key[0..16].*,                iv,                padded,                ciphertext_out,            ),            .aes256 => _ = try crypto.cbc.Aes256Cbc.encrypt(                self.encryption_key[0..32].*,                iv,                padded,                ciphertext_out,            ),        }        const signed_length = result_length - crypto.hmac.tag_length;        const tag = crypto.hmac.sign(self.signing_key, result[0..signed_length]);        @memcpy(result[signed_length..result_length], &tag);        return result;    }    /// Returns whether the trailing 32-byte authentication value (*tag*)    /// matches a tag over every byte before it, for a caller to check that a    /// sealed payload arrived as it was sent before decrypting anything,    /// following Reticulum@1.5.0 RNS/Cryptography/Token.py:77-85. Data of 32    /// bytes or fewer, and data past 65,520 bytes, give false.    pub fn verify(self: Token, data: []const u8) bool {        if (data.len <= crypto.hmac.tag_length) return false;        if (data.len > max_encrypted_length) return false;        const signed_length = data.len - crypto.hmac.tag_length;        const tag = data[signed_length..][0..crypto.hmac.tag_length].*;        return crypto.hmac.verify(self.signing_key, data[0..signed_length], tag);    }    /// Checks the 32-byte authentication value (*tag*), decrypts, cuts the    /// padding back off, and returns the plaintext in `out` for a caller to    /// open a sealed payload under the shared key, following Reticulum@1.5.0    /// RNS/Cryptography/Token.py:100-114. The call returns `error.InvalidToken`    /// when the tag fails or the sealed form is under 64 bytes,    /// `error.OutputTooSmall` when `out` is shorter than the plaintext, and    /// `error.OverlappingBuffers` when either key half overlaps the output. The    /// tag is checked before anything is decrypted.    pub fn decrypt(self: Token, data: []const u8, out: []u8) TokenError![]u8 {        if (!self.verify(data)) return error.InvalidToken;        const minimum_length = overhead + crypto.cbc.block_length;        if (data.len < minimum_length) return error.InvalidToken;        const ciphertext_length = data.len - overhead;        if (out.len < ciphertext_length) return error.OutputTooSmall;        const plaintext_out = out[0..ciphertext_length];        if (overlaps(self.signing_key, plaintext_out)) return error.OverlappingBuffers;        if (overlaps(self.encryption_key, plaintext_out)) return error.OverlappingBuffers;        const iv = data[0..iv_length].*;        const ciphertext = data[iv_length .. data.len - crypto.hmac.tag_length];        const padded = switch (self.mode) {            .aes128 => try crypto.cbc.Aes128Cbc.decrypt(                self.encryption_key[0..16].*,                iv,                ciphertext,                plaintext_out,            ),            .aes256 => try crypto.cbc.Aes256Cbc.decrypt(                self.encryption_key[0..32].*,                iv,                ciphertext,                plaintext_out,            ),        };        const plaintext = try crypto.pkcs7.unpad(padded);        return plaintext_out[0..plaintext.len];    }};fn overlaps(input: []const u8, output: []u8) bool {    if (input.len == 0) return false;    const input_start = @intFromPtr(input.ptr);    const output_start = @intFromPtr(output.ptr);    const input_end = input_start + input.len;    const output_end = output_start + output.len;    return input_start < output_end and output_start < input_end;}

Complete caller list for crypto.token.Token.decrypt

9 direct callers.

Complete caller list for crypto.token.Token.init

13 direct callers.

Audit

Definitions12
Public names12
Members3
Version26.7.0
Revisiondaab053ee433