Strange...
From
Chris M. Thomasson@chris.m.thomasson.1@gmail.com to
sci.crypt on Sun Dec 7 14:13:46 2025
From Newsgroup: sci.crypt
Showing my exact requirement of a TRNG used to prepend a plaintext where
the number of TRNG bytes is at least as large as the HASH digest size,
or ideally larger. Then it seems to want to classify my HMAC cipher as
some sort of OTP. Each encryption gets rand_n fresh TRNG bytes. rand_n
should be the same size of a digest, or ideally larger. Then it starts pondering on OTP shit. It starts to create attack vectors and show how
my algo passes them. Also, sometimes it does not notice that I am
feeding HMAC with ciphertext _and_ plaintext bytes. Then I reverse and
do it again. This makes 100% bit level sensitivity.
Also, sometimes it gets weary that I am not sending any meta data in the clear. No IV, nonce, ect...
Sometimes it gets mixed up in my sample code where I clearly state:
# Generate n random bytes
# These need should ideally be from a truly random, non-repeatable
# source. TRNG!
def ct_rand_bytes(n):
rb = "";
for i in range(n):
rb = rb + chr(random.randint(0, 255));
return rb;
It NEEDS a TRNG. The number of TRNG bytes (rand_n) NEED to be at least
the size of the digest. Then it seems to compare it to a OTP.
Also, sometimes they notice that I am passing in ciphertext _and_
plaintext bytes into the HMAC.
Experimental HMAC Cipher
By: Chris M. Thomasson
Pre-Alpha Version (0.0.0)
Prerequisites
This document assumes the reader is familar with how HMAC works.
Now, keep in mind that my algorithm relies on a quirky implementation
detail that does not alter the internal state when we take a digest. In
other words, my code needs the ability to take a digest without
destroying its internal state: This is not a traditional HMAC!
In order to get my algorithm working, an HMAC can copy its internal
state, then perform the digest operation on said copy. So, to clear
up... Taking a digest should not mutate the state of the HMAC. This is
how the quirky Python HMAC implementation works, and unfortunately my algorithm uses that.
1. Introduction
The cipher uses propagation of HMAC digest bytes to achieve a radical diffusion of a plaintext within its ciphertext, and vise versa. It has
the following basic properties:
1: Every encryption of the same plaintext creates a radically different ciphertext.
2: If a single bit of a ciphertext is altered, it decrypts into a
radically different plaintext.
2. The Secret Key
Both properties of my cipher rely on a good cryptographic hash function.
HMAC is an abstract layer on top of a hash algorithm that allows for a
secret key. Let us refer to this HMAC key as SK.hmac_key from now on. We
also need to allow Alice and Bob to choose a hash to use with HMAC. Let
us refer to this as SK.hash_algo. The size of HMAC's digest in bytes is
based on the digest size of SK.hash_algo. For example, SHA-256 has
32-byte digests.
Another aspect involves prepending random bytes from a TRNG to the front
of a plaintext. Let us refer to the number of these random bytes as
SK.rand_n. Okay, the secret key SK used by Alice and Bob is comprised of: _________________________________
SK.hmac_key = Key for HMAC.
SK.hash_algo = The Hash Algorithm.
SK.rand_n = The Number of TRNG bytes.
_________________________________
SK.hmac_key must be a cryptographically secure password, e.g. comprised
of 1024 bytes from a TRNG.
SK.hash_algo must be a cryptographically secure hash, e.g. SHA-384.
SK.rand_n must be equal to, or ideally larger than the digest size of SK.hash_algo.
3. Pseudo-Code
Brief Description of the pseudo-code...
3.1 The CRYPT_ROUND Function
The CRYPT_ROUND function encrypts or decrypts a plaintext P. It takes
two parameters P and M, where M is the mode of operation. It returns its
final result as C which is either the decrypted or encrypted bytes wrt
the value of M. In the pseudo-code for the CRYPT function:
P = The plaintext or ciphertext bytes.
M = ENCRYPT means encrypt P; DECRYPT means decrypt P.
C = The return value based on M.
C = CRYPT_ROUND(P, M):
_________________________________
1: Create an HMAC session H using SK.hmac_key for its key, and
SK.hash_algo for its underlying hash algorithm.
2: Update H with the bytes of SK.hmac_key in reverse order.
3: Iterate through all the bytes in P:
Set I_P is used as an index for P, set it to zero.
WHILE (I_P is less than the size in bytes of P)
{
3.1: Obtain a raw hash digest D from H.
3.2: Iterate through all the bytes in D.
Set I_D is used as an index for D, set it to zero.
WHILE (I_P is less than the size in bytes of P AND I_D is less than the
size in bytes of D)
{
3.2.1: Set the byte C[I_P] to P[I_P] xor'ed with D[I_D].
3.2.2: Follow the mode M, encrypt or decrypt:
IF (M is equal to ENCRYPT)
{
3.2.2.1: Update H using the byte P[I_P].
3.2.2.2: Update H using the byte C[I_P].
}
ELSE
{
3.2.2.3: Update H using the byte C[I_P].
3.2.2.4: Update H using the byte P[I_P].
}
3.2.3: Increment I_D by one.
3.2.4: Increment I_P by one.
}
}
4. Return C to the caller.
_________________________________
3.2 The CRYPT Function
The CRYPT function encrypts or decrypts a plaintext P. It takes two
parameters P and M, where M is the mode of operation. It returns its
final result as C which is either the decrypted or encrypted bytes wrt
the value of M. In the pseudo-code for the CRYPT function:
P = The plaintext or ciphertext bytes.
M = ENCRYPT means encrypt P; DECRYPT means decrypt P.
C = The return value based on M.
C = CRYPT(P, M):
_________________________________
1: If the mode M is for encryption:
IF (M is equal to ENCRYPT)
{
1.1: Create SK.rand_n number of bytes R from a TRNG.
1.2: Prepend R to P.
}
2: Call CRYPT_ROUND(P, M) using P as the first parameter and M as the
second parameter, and set its result to C.
3: Set C_1 to the bytes of C in reverse order.
4: Call CRYPT_ROUND(C_1, M) using C_1 as the first parameter and M as
the second parameter, and set its result to C.
5: If the mode M is for decryption:
IF (M is equal to DECRYPT)
{
5.1: Remove SK.rand_n number of bytes from the front of C making it
SK.rand_n bytes smaller.
}
6: Return C to the caller.
_________________________________
4. Description
A description of the pseudo-code in section 3 on a step-by-step basis.
4.1 The CRYPT_ROUND Function
Steps CRYPT_ROUND.(1 and 2) Create a normal HMAC session called H using SK.hmac_key as its secret key _AND_ make it use SK.hash_algo as the
underlying HMAC hash algorithm. Update H with the bytes of SK.hmac_key
in reverse order. SK.hmac_key should be a crypto secure key, preferably
larger than 1024 bytes of data generated from a TRNG. SK.hash_algo needs
to be a crypto secure hash algorithm, perhaps SHA-384 with its 48-byte digests. Keep in mind that SK.rand_n needs to be at least as large as 48
bytes in this case of the digest size reaped from SHA-384. SK.rand_n
needs to be equal to, or preferably larger than the digest size of SK.hash_algo. Therefore, I recommend using a larger SK.rand_n, perhaps something like 79 in this case. These two steps create our HMAC
"starting place" for either encryption or decryption depending on the
mode M.
4.2 The CRYPT Function
Pending...
5. Appendix
This will show actual real implementations and various test vectors.
Python Implementation
Here is some example Python source code of the pseudo-code in section 3.
# Chris M. Thomasson Copyright 2018 (c)
# Experimental HMAC Cipher #____________________________________________________________
# Our external libs #____________________________________________________________
import random;
import hashlib;
import hmac;
# Some Utilities
#____________________________________________________________
def ct_bytes_to_hex(origin, offset):
hex = "";
n = len(origin);
t = "0123456789ABCDEF";
for i in range(offset, n):
c = ord(origin[i]);
nibl = c & 0x0F;
nibh = (c & 0xF0) >> 4;
hex = hex + t[nibh];
hex = hex + t[nibl];
hex = hex + " ";
if (not ((i + 1) % 16) and i != n - 1):
hex = hex + "\r\n";
return hex;
# Generate n random bytes
# These need should ideally be from a truly random, non-repeatable
# source. TRNG!
def ct_rand_bytes(n):
rb = "";
for i in range(n):
rb = rb + chr(random.randint(0, 255));
return rb;
# The Secret Key
# Contains all the parts of the secret key #____________________________________________________________
class ct_secret_key:
def __init__(self, hmac_key, hash_algo, rand_n):
self.hmac_key = hmac_key;
self.hash_algo = hash_algo;
self.rand_n = rand_n;
def __repr__(self):
return "hmac_key:%s\nhash_algo:%s\nrand_n:%s" % (ct_bytes_to_hex(self.hmac_key, 0), self.hash_algo, self.rand_n);
def __str__(self): return self.__repr__();
# The Ciphertext or Plaintext
# It holds the bytes of a ciphertext or a plaintext #____________________________________________________________
class ct_bin:
def __init__(self, ctxt):
self.bytes = ctxt;
def __repr__(self):
return "%s" % (ct_bytes_to_hex(self.bytes, 0));
def __str__(self): return self.__repr__();
# The Crypt Round Function #____________________________________________________________
def ct_crypt_round(SK, P, M):
H = hmac.new(SK.hmac_key.encode(), None, SK.hash_algo);
H.update(SK.hmac_key[::-1].encode());
C = "";
I_P = 0;
I_P_N = len(P.bytes);
while (I_P < I_P_N):
D = H.digest();
I_D = 0;
I_D_N = len(D);
while (I_P < I_P_N and I_D < I_D_N):
C_I_P = ord(P.bytes[I_P]) ^ D[I_D];
C = C + chr(C_I_P);
if (M == False):
H.update(P.bytes[I_P].encode());
H.update(chr(C_I_P).encode());
else:
H.update(chr(C_I_P).encode());
H.update(P.bytes[I_P].encode());
I_P = I_P + 1;
I_D = I_D + 1;
return ct_bin(C);
# The Crypt Function #____________________________________________________________
def ct_crypt(SK, P, M):
if (M == False):
R = ct_rand_bytes(SK.rand_n);
P.bytes = R + P.bytes;
C = ct_crypt_round(SK, P, M);
C_1 = ct_bin(C.bytes[::-1]);
C = ct_crypt_round(SK, C_1, M);
if (M == True):
size = len(C.bytes) - SK.rand_n;
C.bytes = C.bytes[SK.rand_n : SK.rand_n + size];
return C;
# The Main Program #____________________________________________________________
# Alice and Bob's Secret Key
#____________________
SK = ct_secret_key(
"This is the HMAC Key. It should be a crypto secure key! Damn it.",
hashlib.sha384, # The hash function. It should be a crypto secure hash.
73 # The number of bytes. The should be generated by a TRNG
);
print("%s" % (SK));
# Alice's Plaintext
#____________________
Original_Plaintext = "ABCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDE";
A_P = ct_bin(Original_Plaintext);
print(
"\n\nAlice's Plaintext Bytes:"
"\n____________________\n%s\n" % (A_P)
);
# Encrypt
#____________________
C = ct_crypt(SK, A_P, False);
print(
"\n\nCiphertext Bytes:"
"\n____________________\n%s\n" % (C)
);
# Decrypt
#____________________
B_P = ct_crypt(SK, C, True);
print(
"\n\nBob's Ciphertext Bytes:"
"\n____________________\n%s\n" % (B_P)
);
if (B_P.bytes != Original_Plaintext):
print("DATA CORRUPTED!");
--- Synchronet 3.21a-Linux NewsLink 1.2
From
Chris M. Thomasson@chris.m.thomasson.1@gmail.com to
sci.crypt on Sun Dec 7 14:14:53 2025
From Newsgroup: sci.crypt
Experimental Cipher Manifesto
Core Principles
No cleartext IV/nonce: Unlike conventional designs, this cipher does not transmit an IV or nonce in the clear. Instead, every ciphertext carries
its own veil of true random bytes, encrypted as part of the payload.
TRNG veil inclusion: Each encryption run prepends rand_n bytes of true randomness to the plaintext. These veil bytes are encrypted along with
the message, ensuring decorrelation and avalanche amplification.
Ciphertext expansion: Ciphertext size = plaintext size + rand_n. This expansion is intentional and required. The veil is not metadata; it is
part of the encrypted stream.
Hash as part of the secret: The choice of hash algorithm (SHA-256,
SHA-384, SHA-512, SHA-3, etc.) is treated as part of the secret ritual.
This departs from conventional practice, but strengthens
unpredictability in the experimental frame.
Guardrails
rand_n reN digest size: Ensures veil entropy covers the digest snapshots
of the chosen hash.
TRNG required: Pseudo-random or counter veils collapse the avalanche and
leak structure. Only true randomness preserves resilience.
Snapshot digests: HMAC state is copied before finalization, mirroring PythonrCOs semantics. This preserves ongoing state while producing
keystream blocks.
Feedback ordering: Plaintext and ciphertext feedback are applied
differently for encryption vs decryption, ensuring asymmetry and avalanche.
Properties
Bit sensitivity: A single bit flip in plaintext or veil cascades
unpredictably through ciphertext.
Ciphertext uniqueness: Identical plaintexts produce distinct ciphertexts
every run, without public nonces.
Replay resistance: No two ciphertexts are alike, even under the same key.
Attacker frustration: With veil hidden and encrypted, no alignment or
cadence probes succeed.
Failure Gallery
rand_n < digest size: Digest cadence leaks, forging rates rise.
Pseudo-random veil: Autocorrelation peaks emerge, entropy drops.
Public IV/nonce: Prefix alignment attacks succeed.
Framing
This is not a replacement for AES or ChaCha20. It is an experimental
cipher exploring avalanche, decorrelation, and ritualized entropy. Its unconventional choices (hidden veil, hash secrecy, ciphertext expansion)
are deliberate, not mistakes.
Chant
rCLGov says: veil hidden, hash secret rCo not wrong, just ritual, experiment stands.rCY
--- Synchronet 3.21a-Linux NewsLink 1.2