tiny.reticulum.crypto.token
Defined in crypto.
API (11)
Actions
Public operations.
Token.decrypt: Checks the 32-byte authentication value (tag), decrypts, cuts the padding back off, and returns the plaintext inoutfor a caller to open a sealed payload under the shared key, following Reticulum@1.5.0 RNS/Cryptography/Token.py:100-114.Token.encrypt: Writes the sealed form intooutand 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.Token.initToken.verify: 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.encryptedLength
Types and contracts
Public types and contracts.
Token: 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.TokenError
Values and defaults
Public values and defaults.
Source
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.
lib.reticulum.src.crypto.test.test_Reticulum@1.5.0_RNS/Cryptography/Token.py:61-114_differential_corpus[function] — test source atlib/reticulum/src/crypto/test.zig:72in nearest public ownerlib.reticulum.src.crypto.testlib.reticulum.src.crypto.test.test_Reticulum@1.5.0_RNS/Cryptography/Token.py:77-114_authenticated_short_token[function] — test source atlib/reticulum/src/crypto/test.zig:315in nearest public ownerlib.reticulum.src.crypto.testlib.reticulum.src.destination.cipher.decryptGroup[function] — private source atlib/reticulum/src/destination/cipher.zig:111in nearest public ownertiny.reticulum.destination.cipherlib.reticulum.src.identity.cipher.decryptWithKey[function] — private source atlib/reticulum/src/identity/cipher.zig:88in nearest public ownertiny.reticulum.identity.cipherlib.reticulum.src.node.link.acceptClose[function] — private source atlib/reticulum/src/node/link.zig:759in nearest public ownertiny.reticulum.node.linklib.reticulum.src.node.link.acceptRtt[function] — private source atlib/reticulum/src/node/link.zig:1048in nearest public ownertiny.reticulum.node.linklib.reticulum.src.node.link.deliver[function] — private source atlib/reticulum/src/node/link.zig:711in nearest public ownertiny.reticulum.node.linklib.reticulum.src.properties.crypto.TotalDecoders.property[function] — private source atlib/reticulum/src/properties/crypto.zig:94in nearest public ownerlib.reticulum.src.properties.cryptolib.reticulum.src.properties.crypto.expectTokenRoundTrip[function] — private source atlib/reticulum/src/properties/crypto.zig:20in nearest public ownerlib.reticulum.src.properties.crypto
Complete caller list for crypto.token.Token.init
13 direct callers.
lib.reticulum.src.crypto.test.test_Reticulum@1.5.0_RNS/Cryptography/Token.py:61-114_differential_corpus[function] — test source atlib/reticulum/src/crypto/test.zig:72in nearest public ownerlib.reticulum.src.crypto.testlib.reticulum.src.crypto.test.test_Reticulum@1.5.0_RNS/Cryptography/Token.py:77-114_authenticated_short_token[function] — test source atlib/reticulum/src/crypto/test.zig:315in nearest public ownerlib.reticulum.src.crypto.testlib.reticulum.src.crypto.test.test_token_u16_maximum_and_maximum_plus_one_admission[function] — test source atlib/reticulum/src/crypto/test.zig:299in nearest public ownerlib.reticulum.src.crypto.testlib.reticulum.src.destination.cipher.decryptGroup[function] — private source atlib/reticulum/src/destination/cipher.zig:111in nearest public ownertiny.reticulum.destination.ciphertiny.reticulum.destination.cipher.encrypt[function] atlib/reticulum/src/destination/cipher.zig:84lib.reticulum.src.identity.cipher.decryptWithKey[function] — private source atlib/reticulum/src/identity/cipher.zig:88in nearest public ownertiny.reticulum.identity.ciphertiny.reticulum.identity.cipher.encrypt[function] atlib/reticulum/src/identity/cipher.zig:46lib.reticulum.src.node.link.acceptClose[function] — private source atlib/reticulum/src/node/link.zig:759in nearest public ownertiny.reticulum.node.linklib.reticulum.src.node.link.acceptRtt[function] — private source atlib/reticulum/src/node/link.zig:1048in nearest public ownertiny.reticulum.node.linklib.reticulum.src.node.link.deliver[function] — private source atlib/reticulum/src/node/link.zig:711in nearest public ownertiny.reticulum.node.linklib.reticulum.src.node.link.encryptedFrame[function] — private source atlib/reticulum/src/node/link.zig:257in nearest public ownertiny.reticulum.node.linklib.reticulum.src.properties.crypto.TotalDecoders.property[function] — private source atlib/reticulum/src/properties/crypto.zig:94in nearest public ownerlib.reticulum.src.properties.cryptolib.reticulum.src.properties.crypto.expectTokenRoundTrip[function] — private source atlib/reticulum/src/properties/crypto.zig:20in nearest public ownerlib.reticulum.src.properties.crypto
Audit
| Definitions | 12 |
|---|---|
| Public names | 12 |
| Members | 3 |
| Version | 26.7.0 |
| Revision | daab053ee433 |