encrypt.c 3.08 KB
Newer Older
lwc-tester committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
/* ======================================================================== */
/*
 * Saturnin-Short (NIST API).
 */

#include "crypto_aead.h"

#include <string.h>
#include <stdint.h>

void saturnin_block_encrypt(int R, int D, const uint8_t *key, uint8_t *buf);
void saturnin_block_decrypt(int R, int D, const uint8_t *key, uint8_t *buf);

/*
 * For the Short mode, we use 10 super-rounds, and the domain is 6.
 */
#define SATURNIN_SHORT_R   10
#define SATURNIN_SHORT_D    6

int
crypto_aead_encrypt(unsigned char *c, unsigned long long *clen,
	const unsigned char *m, unsigned long long mlen,
	const unsigned char *ad, unsigned long long adlen,
	const unsigned char *nsec,
	const unsigned char *npub,
	const unsigned char *k)
{
	uint8_t tmp[32];

	(void)ad;
	(void)nsec;

	/*
	 * Saturnin-Short does not support additional data.
	 */
	if (adlen != 0) {
		return -2;
	}

	/*
	 * Plaintext size MUST be less than 128 bits.
	 */
	if (mlen > 15) {
		return -2;
	}

	memcpy(tmp, npub, 16);
	if (mlen > 0) {
		memcpy(tmp + 16, m, mlen);
	}
	tmp[16 + mlen] = 0x80;
	memset(tmp + 16 + mlen + 1, 0x00, 15 - mlen);
	saturnin_block_encrypt(SATURNIN_SHORT_R, SATURNIN_SHORT_D,
		(const uint8_t *)k, tmp);
	memcpy(c, tmp, 32);
	*clen = 32;
	return 0;
}

int
crypto_aead_decrypt(unsigned char *m, unsigned long long *mlen,
	unsigned char *nsec,
	const unsigned char *c, unsigned long long clen,
	const unsigned char *ad, unsigned long long adlen,
	const unsigned char *npub,
	const unsigned char *k)
{
	uint8_t tmp[32];
	unsigned tcc, notfound, u;
	int i;

	(void)ad;
	(void)nsec;

	/*
	 * Saturnin-Short does not support additional data.
	 */
	if (adlen != 0) {
		return -2;
	}

	/*
	 * Saturnin-Short ciphertext always has length exactly 32 bytes.
	 */
	if (clen != 32) {
		return -1;
	}

	memcpy(tmp, c, 32);
	saturnin_block_decrypt(SATURNIN_SHORT_R, SATURNIN_SHORT_D,
		(const uint8_t *)k, tmp);

	/*
	 * The first half of the block should be equal to the nonce
	 * (16 bytes), and the second half should be a valid padded
	 * plaintext. We perform a fully constant-time check of both
	 * properties, which is probably overkill.
	 */

	/*
	 * Compare the first half with the nonce. After this loop, tcc is
	 * zero if and only if the first half was equal to the nonce.
	 */
	tcc = 0;
	for (i = 0; i < 16; i ++) {
		tcc |= tmp[i] ^ npub[i];
	}

	/*
	 * Check and remove padding.
	 */
	notfound = 0xFF;
	u = 0;
	for (i = 15; i >= 0; i --) {
		unsigned b, f;

		b = tmp[16 + i];

		/*
		 * f is set to 0xFF if notfound is still 0xFF, and b == 0x80.
		 * Otherwise, f is zero.
		 */
		f = notfound & -(1 - (((b ^ 0x80) + 0xFF) >> 8));

		/*
		 * If f == 0xFF, then we found the message length.
		 */
		u |= f & (unsigned)i;

		/*
		 * We clear notfound if we found the 0x80 byte.
		 */
		notfound &= ~f;

		/*
		 * If notfound != 0, then we have not found the 0x80 byte
		 * yet, and the byte b should be 0x00. If this is not the
		 * case, then tcc is set to a non-zero value, which will
		 * trigger a failure.
		 */
		tcc |= notfound & ((b + 0xFF) >> 8);
	}
	tcc |= notfound;

	if (tcc != 0) {
		return -1;
	}
	memcpy(m, tmp + 16, u);
	*mlen = u;
	return 0;
}