• 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