Creating a TallyBox Wallet
This tutorial guides tech-savvy users through the process of creating a secure TallyBox wallet using the secp256r1 elliptic curve. You'll generate a key pair, derive a unique wallet address, verify its integrity, and provision it for use in the TallyBox ecosystem, ensuring compatibility with TallyBox's cryptographic standards. The focus is on the algorithms and math behind each step, making it easy to implement in any programming language. Pseudo-code is provided for complex steps to aid implementation without libraries.
Step 1: Generate a Private-Public Key Pair
Use the secp256r1 elliptic curve (also known as P-256) to generate a key pair. This curve is defined by the equation y² = x³ + ax + b over a finite field, where a and b are specific constants, and the field size is a 256-bit prime (P). The process involves:
- Define the curve parameters: P (prime), a, b, G (base point), and n (order).
- Select a random 256-bit integer as the private key (d), within the range [1, n-1].
- Compute the public key (Q) as Q = d * G, where * denotes elliptic curve point multiplication.
- The public key Q is a point (X, Y) on the curve, where X and Y are 256-bit integers.
Secp256r1 Curve Parameters:
                
P = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF  // Prime field
a = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC  // Curve coefficient
b = 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B  // Curve coefficient
Gx = 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296  // Base point X
Gy = 0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5  // Base point Y
n = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551  // Order of the curve
                
                
            Example Process:
                Private Key: 7a8d26a42f45ecc68bc9d9ad2de9e4868429de2b185ab7e023f93845c58c4fa1
                Public Key (Uncompressed):
                X: 252df02599b4560df18cb5e7486769303c0ec0dcb0f64a5a980e41feba7d593f
                Y: 7020405e0700461be964eaf159e3e20bd9aee2fda2f93b0c6ef03ee63876db47
References:
                SEC 2: Recommended Elliptic Curve Domain Parameters (secp256r1)
                Elliptic Curve Cryptography (Wikipedia)
            
Step 2: Compress and Encode the Public Key
Compress the public key to reduce its size, then encode it in Base58. The process involves:
- Take the X-coordinate of the public key (a 256-bit integer).
- Determine the parity of the Y-coordinate: if Y is odd, suffix = '1'; if Y is even, suffix = '2'. Parity is computed as Y mod 2.
- Form the compressed key as X + '*' + suffix (as a string).
- Encode the X-coordinate (as a byte array) in Base58, then append the suffix. Base58 encoding converts a byte array to a string using a 58-character alphabet, avoiding ambiguous characters like '0', 'O', 'I', and 'l'.
Pseudo-Code: Compress Public Key
                
FUNCTION compress_public_key(X, Y):
    parity = Y % 2
    IF parity == 1 THEN
        suffix = '1'
    ELSE
        suffix = '2'
    END IF
    compressed_key = X + '*' + suffix
    RETURN compressed_key
END FUNCTION
                
                
            Pseudo-Code: Base58 Encode
                
FUNCTION base58_encode(bytes):
    alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
    value = bytes_to_integer(bytes)  // Convert byte array to big integer (unsigned)
    result = ''
    WHILE value > 0:
        remainder = value % 58
        result = alphabet[remainder] + result
        value = value / 58  // Integer division
    END WHILE
    // Handle leading zeros in the byte array
    FOR each byte in bytes:
        IF byte == 0 THEN
            result = '1' + result
        ELSE
            BREAK
        END IF
    END FOR
    RETURN result
END FUNCTION
                
                
            Example Process:
                Compressed Key: 252df02599b4560df18cb5e7486769303c0ec0dcb0f64a5a980e41feba7d593f*1
                Base58 Compressed Public Key: 3W8iLTuAjbf9d1ih4RCi7Aat6keKxs72SNynZ1A269MY*1
References:
                Base58 Encoding (Wikipedia)
                Compressed Public Key Explanation (Bitcoin Stack Exchange)
            
Step 3: Derive the TallyBox Wallet Address
Derive a unique wallet address from the Base58-encoded public key. The process involves:
- Compute the SHA-256 hash of the Base58-encoded public key (treat the string as bytes using UTF-8 encoding).
- Ensure the SHA-256 hash is converted to a big integer as an unsigned value across all platforms (e.g., use unsigned conversion in C#, Python, Java, JavaScript).
- Encode the raw wallet string (as bytes) in Base58 (see pseudo-code in Step 2).
- Compute the MD5 hash of the Base58-encoded string (as bytes).
- Take the first 11 characters of the MD5 hash and prefix it with 'boxB' to form the checksum.
- Combine the checksum and the Base58-encoded string to form the final wallet address: checksum + Base58-encoded string.
Example Process:
                Base58 Compressed Public Key: 3W8iLTuAjbf9d1ih4RCi7Aat6keKxs72SNynZ1A269MY*1
                SHA-256 Hash: 5e3df94169f3d82b9b67d93acbc288a42c9812ced307297f78ed3058d338b1e4
                Raw Wallet (Bigint): 42626905736173508565770948203518737897276237144762802425237770121056689369572
                Base58 Encoded: 7Lt8jmf3GNWCJc2K5bU7aAS5gqSbDYnY5UVshnJVHEJP
                MD5 Hash: a01d317e6c52656bb188a55736dc5aab
                Checksum: boxBa01d317e6c5
                Final Wallet Address: boxBa01d317e6c57Lt8jmf3GNWCJc2K5bU7aAS5gqSbDYnY5UVshnJVHEJP
References:
                SHA-256 Algorithm (Wikipedia)
                MD5 Algorithm (Wikipedia)
                Base58 Encoding (Wikipedia)
            
Step 4: Verify the Key Pair Integrity
Verify the key pair to ensure it is valid for TallyBox transactions. The process involves:
- Sign the message "TallyBox, a tool for curious minds.." using the private key with the ECDSA (Elliptic Curve Digital Signature Algorithm) on the secp256r1 curve, following RFC 6979 for deterministic signature generation. This produces a signature (r, s).
- Decompress the compressed public key: given X and the suffix ('1' or '2'), compute Y using the curve equation y² = x³ + ax + b mod p, where p is the field prime. Solve for Y (two possible values) and select the one matching the parity (odd for '1', even for '2').
- Verify the signature using the decompressed public key (X, Y) with ECDSA. If the signature is valid, the key pair is trustworthy.
- If verification fails, discard the key pair and restart from Step 1.
Example Process:
				Message Signed: "TallyBox, a tool for curious minds.."
				Signature (Base64): MEYCIQD6BLlP29AzfKK/SPyuMdVYrWzn/wQElsdkVF+ewvBgDQIhANy4lgejq9JChhQM/9PWyxfLKJYxRz4SWcoBak468i5s
				Base58 Compressed Public Key: 3W8iLTuAjbf9d1ih4RCi7Aat6keKxs72SNynZ1A269MY*1
				Decompressed Public Key (X*Y): 252df02599b4560df18cb5e7486769303c0ec0dcb0f64a5a980e41feba7d593f*7020405e0700461be964eaf159e3e20bd9aee2fda2f93b0c6ef03ee63876db47
				Signature Verification Result: Valid
References:
				ECDSA Algorithm (Wikipedia)
				SEC 2: Recommended Elliptic Curve Domain Parameters (secp256r1)
				RFC 6979: Deterministic Usage of DSA and ECDSA
			
Step 5: Provision the TallyBox Wallet
Provision the wallet by securely storing its data. The process involves:
- Select a wallet name and a strong local password.
- Form a key string as wallet_name + '~' + password + '~' + wallet_address.
- Compute the SHA-256 hash of the key string (as bytes) to generate a 64-character hex secret key.
- Encrypt the private key (as a hex string) using a special AES-256-CBC algorithm with custom padding, described below. Encode the ciphertext in Base64.
- Decrypt the encrypted private key to verify correctness: use the same secret key and decrypt the ciphertext. Compare the result with the original private key.
- Create an XML file containing the wallet name, wallet address, Base58-encoded compressed public key, encrypted private key (Base64), and a tech info URL.
- Store the XML file and private key offline securely.
Special AES-256-CBC with Custom Padding
TallyBox uses a unique AES-256-CBC encryption algorithm with custom padding to secure the private key. The algorithm derives the key, IV, and salt from a 64-character hex secret key (SHA-256 of wallet_name~password~wallet_address). Random padding is added around the plaintext for enhanced security. The configuration is:
- Secret Key: A 64-character hex string (32 bytes), split into:
                            - Password: First 32 hex chars (16 bytes, ASCII-encoded).
- IV: Next 16 hex chars (8 bytes, ASCII-encoded, 16 bytes as bytes).
- Salt: Last 16 hex chars (8 bytes, ASCII-encoded).
 
- Key Derivation: PBKDF2 with SHA-256, 3 iterations, 32-byte output, using password and salt.
- Custom Padding: Random string (0–99 chars, a-zA-Z) + '|' + plaintext (64-char hex private key) + '|' + random string (0–99 chars, a-zA-Z), followed by PKCS#7 padding.
- Output: Base64-encoded ciphertext (no IV prepended).
Pseudo-Code: AES-256-CBC Encrypt with Custom Padding
                    
FUNCTION aes256_cbc_encrypt_custom(data, secret):
    IF length(secret) < 64 OR NOT is_hex(secret) THEN
        THROW "Secret must be a 64-char hex string"
    END IF
    password = secret[0:32]  // First 32 hex chars (ASCII)
    iv = secret[32:48]       // Next 16 hex chars (ASCII)
    salt = secret[48:64]     // Last 16 hex chars (ASCII)
    key = PBKDF2_HMAC_SHA256(password, salt, iterations=3, output_bytes=32)
    left_padding_size = random_integer(0, 99)
    left_padding = random_string(left_padding_size, 'a-zA-Z')
    right_padding_size = random_integer(0, 99)
    right_padding = random_string(right_padding_size, 'a-zA-Z')
    padded_data = left_padding + '|' + data + '|' + right_padding
    padding_length = 16 - (length(padded_data) % 16)
    padded_data = padded_data + (padding_length as byte) * padding_length  // PKCS#7
    cipher = initialize_aes256_cbc_encrypt(key, iv)
    ciphertext = cipher.encrypt(padded_data)
    base64_result = base64_encode(ciphertext)
    RETURN base64_result
END FUNCTION
                    
                    
                Pseudo-Code: AES-256-CBC Decrypt with Custom Padding
                    
FUNCTION aes256_cbc_decrypt_custom(base64_ciphertext, secret):
    IF length(secret) < 64 OR NOT is_hex(secret) THEN
        THROW "Secret must be a 64-char hex string"
    END IF
    ciphertext = base64_decode(base64_ciphertext.replace('\n', ''))  // Strip newlines
    password = secret[0:32]  // First 32 hex chars (ASCII)
    iv = secret[32:48]       // Next 16 hex chars (ASCII)
    salt = secret[48:64]     // Last 16 hex chars (ASCII)
    key = PBKDF2_HMAC_SHA256(password, salt, iterations=3, output_bytes=32)
    cipher = initialize_aes256_cbc_decrypt(key, iv)
    padded_data = cipher.decrypt(ciphertext)
    padding_length = padded_data[-1]
    IF padding_length > 16 OR padding_length == 0 THEN
        THROW "Invalid PKCS#7 padding"
    END IF
    padded_data = padded_data[:-padding_length]  // Remove PKCS#7 padding
    padded_text = padded_data.decode('utf-8')
    parts = padded_text.split('|')
    IF length(parts) != 3 THEN
        THROW "Invalid padding format: expected 'left|data|right'"
    END IF
    plaintext = parts[1]
    IF NOT is_hex(plaintext) OR length(plaintext) != 64 THEN
        THROW "Decrypted private key must be a 64-char hex string"
    END IF
    RETURN plaintext
END FUNCTION
                    
                    
                Example Process:
                Original Private Key (hex): 7a8d26a42f45ecc68bc9d9ad2de9e4868429de2b185ab7e023f93845c58c4fa1
                Key Components (pre-SHA-256): shahin~11~boxBa01d317e6c57Lt8jmf3GNWCJc2K5bU7aAS5gqSbDYnY5UVshnJVHEJP
                Local Key (SHA-256): 7e9b4699f7d3eea878f51a9d4e238922116cccc558b15f2db257d92a9779becc
                Encrypted Private Key (Base64): Lu8xEpnACOtVrmZxRt7I2m7Zc5pZI8sxIGhXV6FqwN6N2VG8yt4jof5c0Xo5slF+VyOLORhls2auG27Xmjd20Z/9YiBZ2E5ePFsQV7ZkeqKFZa+k5IrOHGjDcyJtiJt22zAzOaSvyTFFGRHwDGI1FCJ5BppBvYkoGhro9MxHp3MVS7eW2N3z2NEQV2Gxm+IErv0ExkxfesyXqw/kTwk2qHBuTUtKRgkOY8RCAPPkNglOjfo4ct+YCdkzoIS/Ww6xatWmAUA6bhtxK2q3vZly1iteNAa+Uz0Pyn166f7Y90cCGJPINc5x7WGcolqPeWf5
                Decrypted Private Key (hex): 7a8d26a42f45ecc68bc9d9ad2de9e4868429de2b185ab7e023f93845c58c4fa1
                Verification Result: Verified
Example Output:
                
References:
                AES Encryption (Wikipedia)
                SHA-256 Algorithm (Wikipedia)
                XML Specification (W3C)
            
Acknowledgments
Special thanks to Grok, for its invaluable assistance in creating this TallyBox wallet creation tutorial.