ENCRYPT, as its name suggests, provides multiple encryption algorithms for the calculator.

Authors
Definitions
CSRNG (Cryptographically-Secure Random Number Generator)
Many random number generators, including the rand() implementation provided by the toolchain are only statistically random, but not unpredictable. That suffices for many applications but not for cryptography. Otherwise-secure cryptography can be defeated if the primative that generates keys and salts is predictable. To that end, the developers of this library put significant effort into constructing a generator that satifies the constraints for cryptographic security to the best extent possible on the hardware. Those constraints are as follows:
  • Statistically-random (passes all statistical randomness tests)
  • Unpredictable (ref: next-bit test)
  • Resistant to state-compromise; state compromise yields nothing of value

Encryption
A reversible transformation of data designed to render it difficult for an unauthorized party to read. A cipher is an algorithm that performs encryption and decryption.

Symmetric Encryption
An encryption system in which the same key works for both encryption and decryption. Symmetric encryption tends to be very fast and uses much smaller keys.

Asymmetric Encryption
An encryption system in which one key is used for encryption and a seperate key is used for decryption and there is some mathematical relationship between the two keys that allows them to reverse each other. Asymmetric encryption tends to be slow and uses much larger keys.

Advanced Encryption Standard (AES)
A fast symmetric encryption system that can encrypt arbitrary lengths of data in blocks of 128 bits (16 bytes). AES has three main variants, each of which take a key of different length:
  • AES-128: Uses a 128-bit (16 byte) key, performs 10 rounds of encryption
  • AES-192: Uses a 192-bit (24 byte) key, performs 12 rounds of encryption
  • AES-256: Uses a 256-bit (32 byte) key, performs 14 rounds of encryption
  • Rounds means number of times the algorithm repeats transformations on the data.
AES is one of the most secure encryption systems in use today. AES-256 is the most secure variant of the algorithm.

RSA
RSA is a form of public key cryptography. In this construction, both parties need a public key and a private key. Typically the public key is used to encrypt outbound messages and the private key is used to decrypt inbound messages. In a public key system anyone can encrypt messages for a specific host since the public key is sent in the clear (hence the term "public"). However, only the intended recipient can decrypt those messages as the private key is not shared. This is possible because the public and private keys are inverses of each other such that:
// RSA encryption and decryption
encrypted = message ^ public_exponent % public modulus
message = encrypted ^ private_exponent % private modulus

The cryptographic strength of RSA comes from the difficulty of factoring huge prime numbers. In recent times better hardware and faster algorithms have made solving this problem easier. 1024-bit RSA has been broken for some time and most cryptographers suspect 2048-bit RSA only has a few years of viability left.
Asymmetric encryption is very slow. Using even RSA-1024 on the TI-84+ CE will take several seconds. For this reason, you usually do not use RSA for sustained encrypted communication. Use RSA to share a symmetric key, and then use AES for future messages.

Elliptic Curve Diffie-Hellman (ECDH)
Diffie-Hellman is a key negotiation protocol. It is another form of public key cryptography in which two parties agree on a shared secret to use for symmetric encryption by exchanging a public key that is the product of a private key and some scalar. Standard Diffie-Hellman uses the same general schema as RSA involving primes (p) and a primitive root modulo p (G). The properties of these keys are such that the following is true:
// The Diffie-Hellman algorithm
G ^ Pa % p = Ua    // P = private, U = public, a = alice
G ^ Pb % p = Ub    // P = private, U = public, b = bob
// Alice and Bob exchange public keys (Ua <==> Ub)
Ub ^ Pa % p = secret
Ua ^ Pb % p = secret
// Note that both parties end up with a common shared secret.

For the same reasons as with RSA this requires extremely large values for p and G and it also suffers from the same weaknesses that render it increasingly insecure as hardware and algorithms get better at factoring the values. Enter elliptic curve cryptography. In the case of elliptic curve Diffie-Hellman, G is a base point of maximal order on the elliptic curve and the public key is the result of multiplying that point by the private key over a finite field. The revised algorithm can be expressed like so:
// The Elliptic Curve Diffie-Hellman algorithm
Pa * G = Ua    // P = private, U = public, a = alice
Pb * G = Ub    // P = private, U = public, b = bob
// Alice and Bob exchange public keys (Ua <==> Ub)
Ub * Pa = secret
Ua * Pb = secret
// Note that both parties end up with a common shared secret.

The behavior of an elliptic curve over a finite field lends itself to the creation of keys that are more complicated to crack and it so follows that the necessary key lengths are much smaller. The curve implemented by this library, SECT233k1, has a degree of 233 which also defines the maximum length of the private key. Just 233 bits for elliptic curve Diffie-Hellman. Versus several thousand for standard Diffie-Hellman and RSA. Quite the difference.
Macros
#define CRYPTX_AES128_KEYLEN  16

Defines the byte length of an AES-128 key.

#define CRYPTX_AES192_KEYLEN  24

Defines the byte length of an AES-192 key.

#define CRYPTX_AES256_KEYLEN  24

Defines the byte length of an AES-256 key.

#define CRYPTX_AES_BLOCK_SIZE  16

Defines the block size of the AES cipher.

#define CRYPTX_AES_IV_SIZE  CRYPTX_AES_BLOCK_SIZE

Defines the length of the AES initialization vector.

CRYPTX_AES_CIPHERTEXT_LEN(plaintext_len) \
  ((((plaintext_len)%CRYPTX_AES_BLOCK_SIZE)==0) ? \
    (len) + CRYPTX_AES_BLOCK_SIZE : (((len)>>4) + 1)<<4)

Defines a macro to return the necessary length for a padded AES plaintext.

#define CRYPTX_AES_CBC_FLAGS(padding_mode) \
  ((padding_mode)<<2) | AES_MODE_CBC

Defines a macro to enable AES CBC cipher mode and pass relevant configuration options.
See cryptx_aes_padding_modes.

#define CRYPTX_AES_CTR_FLAGS(nonce_len, counter_len) \
  ((0x0f & (counter_len))<<8) | ((0x0f & (nonce_len))<<4) | AES_MODE_CTR

Defines a macro to enable AES CTR cipher mode and pass relevant configuration options.
Pass 0 for nonce_len and counter_len to set default options.

CRYPTX_RSA_MODULUS_MAX  256

Defines the maximum byte length of an RSA public modulus supported by this library.

#define CRYPTX_ECDH_PRIVKEY_SIZE  30

Defines the byte length of an ECDH private key supported by this library.

#define CRYPTX_ECDH_PUBKEY_SIZE  (CRYPTX_ECDH_PRIVKEY_SIZE<<1)

Defines the byte length of an ECDH public key supported by this library.

Enumerations
typedef enum cryptx_csrng_sampling_modes {
  SAMPLING_THOROUGH = 0,
  SAMPLING_FAST = 1
} cryptx_csrng_sampling_mode;

Defines sampling modes for cryptx_csrand_init().
SAMPLING_THOROUGH: 1024 tests per bit
SAMPLING_FAST: 512 tests per bit

enum cryptx_aes_cipher_modes {
  AES_MODE_CBC,
  AES_MODE_CTR
};

Defines supported AES cipher modes.

enum cryptx_aes_padding_schemes {
  PAD_PKCS7,
  PAD_DEFAULT = PAD_PKCS7,
  PAD_ISO2
;};

Defines supported padding schemes for AES CBC mode

typedef enum {
  AES_OK,
  AES_INVALID_ARG,
  AES_INVALID_MSG,
  AES_INVALID_CIPHERMODE,
  AES_INVALID_PADDINGMODE,
  AES_INVALID_CIPHERTEXT,
  AES_INVALID_OPERATION
} aes_error_t;

Defines possible responses codes from calls to the AES API.

typedef enum {
  RSA_OK,
  RSA_INVALID_ARG,
  RSA_INVALID_MSG,
  RSA_INVALID_MODULUS,
  RSA_ENCODING_ERROR
} rsa_error_t;

Defines possible response codes from calls to the RSA API

typedef enum {
  ECDH_OK,
  ECDH_INVALID_ARG,
  ECDH_PRIVKEY_INVALID,
  ECDH_RPUBKEY_INVALID
} ecdh_error_t;

Defines possible response codes from calls to the ECDH API

Structs
struct cryptx_aes_ctx {...};

Defines state data for an AES context.

struct cryptx_ecdh_ctx {...};

Defines state data for an ECDH context.

Functions
bool cryptx_csrand_init(cryptx_csrng_sampling_mode mode);

Initializes the (HW)RNG.
mode: A flag specifying the sampling mode. See cryptx_csrng_sampling_modes.
output: true on success, false on failure. SAMPLING_THOROUGH ensures a more entropic source, but takes longer (~4s).
SAMPLING_FAST takes less time (~2s) but may not select the most entropic bit.

uint32_t cryptx_csrand_get(void);

Returns a securely random 32-bit (4 byte) integer.
output: A 32-bit random integer

bool cryptx_csrand_fill(void* buffer, size_t size);

Fills a buffer with securely random bytes.
buffer: Pointer to a buffer to fill with random bytes.
size: Length of the buffer.
output: true on success, false on failure.

aes_error_t cryptx_aes_init(
   struct cryptx_aes_ctx* context,
   const void* key, size_t keylen,
   const void* iv, uint24_t flags);

Initializes a stateful and one-directional AES context.
context: Pointer to an AES context.
key: Pointer to the key to use with the AES context
keylen: Length of the key, in bytes
iv: Pointer to a 16-byte initialization vector (salt)
flags: A series of cipher options bitwise-ORd together.
 See CRYPTX_AES_CBC_FLAGS() and CRYPTX_AES_CTR_FLAGS()
output: An aes_error_t indicating the status of the AES operation.

aes_error_t cryptx_aes_encrypt(
   struct cryptx_aes_ctx* context,
   const void* plaintext, size_t len,
   void* ciphertext);

Encrypts a stream of data and updates the AES context such that (1) it will return AES_INVALID_OPERATION if used with decryption, and (2) you can pass the next stream of data to aes_encrypt() using the same context.
context: Pointer to an initialized AES context.
plaintext: Pointer to stream of data to encrypt.
len: Length of data to encrypt.
ciphertext: Pointer to buffer to write encrypted data to.
output: An aes_error_t indicating the status of the AES operation.

aes_error_t cryptx_aes_decrypt(
   struct cryptx_aes_ctx* context,
   const void* ciphertext, size_t len,
   void* plaintext);

Decrypts a stream of data and updates the AES context such that (1) it will return AES_INVALID_OPERATION if used with encryption, and (2) you can pass the next stream of data to aes_decrypt() using the same context.
context: Pointer to an initalized AES context.
plaintext: Pointer to stream of data to decrypt.
len: Length of data to decrypt.
ciphertext: Pointer to buffer to write decrypted data to.
output: An aes_error_t indicating the status of the AES operation.

rsa_error_t cryptx_rsa_encrypt(
   const void* msg, size_t msglen,
   const void* pubkey, size_t keylen,
   void* ciphertext, uint8_t oaep_hash_alg);

Encrypts a message using the RSA algorithm, applying the Optimal Asymmetic Encryption Padding scheme (OAEPv2.2) prior to encryption.
msg: Pointer to message to encrypt.
msglen: Length of message to encrypt.
pubkey: Pointer to public modulus to encrypt with.
keylen: Length, in bytes, of the public modulus.
ciphertext: Pointer to buffer to write encrypted message to.
oaep_hash_algs: The numeric ID of the hashing algorithm to use within OAEP encoding.
 See cryptx_hash_algorithms.
output: An rsa_error_t indicating the status of the RSA operation

ecdh_error_t cryptx_ecdh_init(struct cryptx_ecdh_ctx* context);

Fills the private key with random bytes and generates a public key given base point G and the randomized private key.
context: Pointer to an ECDH context.
output: An ecdh_error_t indicating the status of the ECDH operation.
The context is updated with the public key, accessible at context.pubkey.
The private key is also accessible at context.privkey.
Do not edit the context manually after calling this function, you will corrupt the state. Only access these structure members directly to read out the public key for sending to the remote host.

ecdh_error_t cryptx_ecdh_secret(
   struct cryptx_ecdh_ctx* context,
   const uint8_t* rpubkey, uint8_t* secret);

Generates a shared secret given an ECDH context and a remote public key.
Uses the cofactor variant of ECDH. SECT233k1 has a cofactor of 4.
context: Pointer to an initialized ECDH context.
rpubkey: Pointer to a remote public key.
secret: Pointer to buffer to write the secret to.
output: An ecdh_error_t indicating the status of the ECDH operation.
Do not edit the context manually, you will corrupt the state.
Do not use the generated secret directly for symmetric encryption. Pass it to a KDF or cryptographic hash to generate a digest and use that as your encryption key.

Addendum: Authenticated Encryption

Authenticated encryption is an encryption scheme that produces a ciphertext that is not only obfuscated but also has its integrity and authenticity verifiable. This can be accomplished in a few ways, the most common of which are: (1) appending a signature, hash, or keyed hash to a message, and (2) implementing a cipher mode that integrates authentication.

#2 above is not implemented in CryptX. Most of the authenticating cipher modes are computationally-intensive without hardware acceleration and may not be feasible for use on the TI-84+ CE. While consid- eration is being given to potentially adding a cipher mode such as OCB or GCM if a sufficiently-optimized implementation for this platform can be found (or devised), it is possible to construct a ciphertext guarded against tampering by using method #1, which this library does provide for.

It is recommended that whenever you are sending data you need to be truly secure with this library, you always embed a keyed hash into the message that the recipient can validate. This functionality is provided by the HMAC implementation shown earlier in this document. Proper application of HMAC for ciphertext integrity requires the following considerations:

Example of authenticated encryption, CryptX. by Anthony Cagliano

#include <hashlib.h>
#include <encrypt.h>

// this assumes that the AES secret ‘aes_key‘ and the HMAC secret ‘hmac_key‘
// have been negotiated beforehand.

// let’s send a simple ascii string
char* msg = "The daring fox jumped over the moon."

// the header is a size word, containing size of string plus our IV
// header can really be whatever you want, but some arbitrary nonsense as an example
size_t header = sizeof(msg)+AES_IVSIZE;

// allocate the memory we need
cryptx_aes_ctx ctx;
cryptx_hmac_ctx hmac;
uint8_t iv[AES_IVSIZE];

// doing this first allows us to
hmac_init(&hmac, hmac_key, sizeof hmac_key, SHA256);

// allocate a digest of the size we need
uint8_t hmac_digest[hmac.digest_len];

// !!!! NEVER PROCEED IF csrand_init() FAILS !!!
if(!cryptx_csrand_init()) return false;
cryptx_csrand_fill(iv, AES_IVSIZE);

// initialize AES context with mode , key , and iv
cryptx_aes_init(&ctx, aes_key, sizeof aes_key, iv, CRYPTX_AES_CTR_FLAGS(8, 8));

// encrypt message
// aes_encrypt supports in-place encryption
cryptx_aes_encrypt(&ctx, msg, strlen(msg), msg);

// hash everything you are sending, except the hash itself
cryptx_hmac_update(&hmac, &header, sizeof header);
cryptx_hmac_update(&hmac, iv, sizeof iv);
cryptx_hmac_update(&hmac, msg, strlen(msg));
cryptx_hmac_final(&hmac, hmac_digest);

// ps_queue/send are psuedo functions implying queueing data to send
// and then sending it
ps_queue(&header, sizeof header);
ps_queue(iv, sizeof iv);
ps_queue(msg, sizeof msg);
ps_queue(hmac_digest, sizeof hmac_digest);
ps_send();