youtubeUnblock/deps/cyclone/gcm.c

630 lines
18 KiB
C
Raw Permalink Normal View History

2024-09-21 19:13:32 +00:00
/**
* @file gcm.c
* @brief Galois/Counter Mode (GCM)
*
* @section License
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* Copyright (C) 2010-2024 Oryx Embedded SARL. All rights reserved.
*
* This file is part of CycloneCRYPTO Open.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @section Description
*
* The Galois/Counter Mode (GCM) is an authenticated encryption algorithm
* designed to provide both data authenticity (integrity) and confidentiality.
* Refer to SP 800-38D for more details
*
* @author Oryx Embedded SARL (www.oryx-embedded.com)
* @version 2.4.4
**/
//Switch to the appropriate trace level
#define TRACE_LEVEL CRYPTO_TRACE_LEVEL
//Dependencies
#include "core/crypto.h"
#include "aead/gcm.h"
//Check crypto library configuration
#if (GCM_SUPPORT == ENABLED)
//Reduction table
static const uint32_t r[GCM_TABLE_N] =
{
#if (GCM_TABLE_W == 4)
0x00000000, 0x1C200000, 0x38400000, 0x24600000, 0x70800000, 0x6CA00000, 0x48C00000, 0x54E00000,
0xE1000000, 0xFD200000, 0xD9400000, 0xC5600000, 0x91800000, 0x8DA00000, 0xA9C00000, 0xB5E00000
#else
0x00000000, 0x01C20000, 0x03840000, 0x02460000, 0x07080000, 0x06CA0000, 0x048C0000, 0x054E0000,
0x0E100000, 0x0FD20000, 0x0D940000, 0x0C560000, 0x09180000, 0x08DA0000, 0x0A9C0000, 0x0B5E0000,
0x1C200000, 0x1DE20000, 0x1FA40000, 0x1E660000, 0x1B280000, 0x1AEA0000, 0x18AC0000, 0x196E0000,
0x12300000, 0x13F20000, 0x11B40000, 0x10760000, 0x15380000, 0x14FA0000, 0x16BC0000, 0x177E0000,
0x38400000, 0x39820000, 0x3BC40000, 0x3A060000, 0x3F480000, 0x3E8A0000, 0x3CCC0000, 0x3D0E0000,
0x36500000, 0x37920000, 0x35D40000, 0x34160000, 0x31580000, 0x309A0000, 0x32DC0000, 0x331E0000,
0x24600000, 0x25A20000, 0x27E40000, 0x26260000, 0x23680000, 0x22AA0000, 0x20EC0000, 0x212E0000,
0x2A700000, 0x2BB20000, 0x29F40000, 0x28360000, 0x2D780000, 0x2CBA0000, 0x2EFC0000, 0x2F3E0000,
0x70800000, 0x71420000, 0x73040000, 0x72C60000, 0x77880000, 0x764A0000, 0x740C0000, 0x75CE0000,
0x7E900000, 0x7F520000, 0x7D140000, 0x7CD60000, 0x79980000, 0x785A0000, 0x7A1C0000, 0x7BDE0000,
0x6CA00000, 0x6D620000, 0x6F240000, 0x6EE60000, 0x6BA80000, 0x6A6A0000, 0x682C0000, 0x69EE0000,
0x62B00000, 0x63720000, 0x61340000, 0x60F60000, 0x65B80000, 0x647A0000, 0x663C0000, 0x67FE0000,
0x48C00000, 0x49020000, 0x4B440000, 0x4A860000, 0x4FC80000, 0x4E0A0000, 0x4C4C0000, 0x4D8E0000,
0x46D00000, 0x47120000, 0x45540000, 0x44960000, 0x41D80000, 0x401A0000, 0x425C0000, 0x439E0000,
0x54E00000, 0x55220000, 0x57640000, 0x56A60000, 0x53E80000, 0x522A0000, 0x506C0000, 0x51AE0000,
0x5AF00000, 0x5B320000, 0x59740000, 0x58B60000, 0x5DF80000, 0x5C3A0000, 0x5E7C0000, 0x5FBE0000,
0xE1000000, 0xE0C20000, 0xE2840000, 0xE3460000, 0xE6080000, 0xE7CA0000, 0xE58C0000, 0xE44E0000,
0xEF100000, 0xEED20000, 0xEC940000, 0xED560000, 0xE8180000, 0xE9DA0000, 0xEB9C0000, 0xEA5E0000,
0xFD200000, 0xFCE20000, 0xFEA40000, 0xFF660000, 0xFA280000, 0xFBEA0000, 0xF9AC0000, 0xF86E0000,
0xF3300000, 0xF2F20000, 0xF0B40000, 0xF1760000, 0xF4380000, 0xF5FA0000, 0xF7BC0000, 0xF67E0000,
0xD9400000, 0xD8820000, 0xDAC40000, 0xDB060000, 0xDE480000, 0xDF8A0000, 0xDDCC0000, 0xDC0E0000,
0xD7500000, 0xD6920000, 0xD4D40000, 0xD5160000, 0xD0580000, 0xD19A0000, 0xD3DC0000, 0xD21E0000,
0xC5600000, 0xC4A20000, 0xC6E40000, 0xC7260000, 0xC2680000, 0xC3AA0000, 0xC1EC0000, 0xC02E0000,
0xCB700000, 0xCAB20000, 0xC8F40000, 0xC9360000, 0xCC780000, 0xCDBA0000, 0xCFFC0000, 0xCE3E0000,
0x91800000, 0x90420000, 0x92040000, 0x93C60000, 0x96880000, 0x974A0000, 0x950C0000, 0x94CE0000,
0x9F900000, 0x9E520000, 0x9C140000, 0x9DD60000, 0x98980000, 0x995A0000, 0x9B1C0000, 0x9ADE0000,
0x8DA00000, 0x8C620000, 0x8E240000, 0x8FE60000, 0x8AA80000, 0x8B6A0000, 0x892C0000, 0x88EE0000,
0x83B00000, 0x82720000, 0x80340000, 0x81F60000, 0x84B80000, 0x857A0000, 0x873C0000, 0x86FE0000,
0xA9C00000, 0xA8020000, 0xAA440000, 0xAB860000, 0xAEC80000, 0xAF0A0000, 0xAD4C0000, 0xAC8E0000,
0xA7D00000, 0xA6120000, 0xA4540000, 0xA5960000, 0xA0D80000, 0xA11A0000, 0xA35C0000, 0xA29E0000,
0xB5E00000, 0xB4220000, 0xB6640000, 0xB7A60000, 0xB2E80000, 0xB32A0000, 0xB16C0000, 0xB0AE0000,
0xBBF00000, 0xBA320000, 0xB8740000, 0xB9B60000, 0xBCF80000, 0xBD3A0000, 0xBF7C0000, 0xBEBE0000
#endif
};
/**
* @brief Initialize GCM context
* @param[in] context Pointer to the GCM context
* @param[in] cipherAlgo Cipher algorithm
* @param[in] cipherContext Pointer to the cipher algorithm context
* @return Error code
**/
__weak_func error_t gcmInit(GcmContext *context, const CipherAlgo *cipherAlgo,
void *cipherContext)
{
uint_t i;
uint_t j;
uint32_t c;
uint32_t h[4];
//Check parameters
if(context == NULL || cipherAlgo == NULL || cipherContext == NULL)
return ERROR_INVALID_PARAMETER;
//GCM supports only symmetric block ciphers whose block size is 128 bits
if(cipherAlgo->type != CIPHER_ALGO_TYPE_BLOCK || cipherAlgo->blockSize != 16)
return ERROR_INVALID_PARAMETER;
//Save cipher algorithm context
context->cipherAlgo = cipherAlgo;
context->cipherContext = cipherContext;
//Let H = 0
h[0] = 0;
h[1] = 0;
h[2] = 0;
h[3] = 0;
//Generate the hash subkey H
context->cipherAlgo->encryptBlock(context->cipherContext, (uint8_t *) h,
(uint8_t *) h);
//Pre-compute M(0) = H * 0
j = GCM_REVERSE_BITS(0);
context->m[j][0] = 0;
context->m[j][1] = 0;
context->m[j][2] = 0;
context->m[j][3] = 0;
//Pre-compute M(1) = H * 1
j = GCM_REVERSE_BITS(1);
context->m[j][0] = betoh32(h[3]);
context->m[j][1] = betoh32(h[2]);
context->m[j][2] = betoh32(h[1]);
context->m[j][3] = betoh32(h[0]);
//Pre-compute all multiples of H (Shoup's method)
for(i = 2; i < GCM_TABLE_N; i++)
{
//Odd value?
if((i & 1) != 0)
{
//Compute M(i) = M(i - 1) + H
j = GCM_REVERSE_BITS(i - 1);
h[0] = context->m[j][0];
h[1] = context->m[j][1];
h[2] = context->m[j][2];
h[3] = context->m[j][3];
//An addition in GF(2^128) is identical to a bitwise exclusive-OR
//operation
j = GCM_REVERSE_BITS(1);
h[0] ^= context->m[j][0];
h[1] ^= context->m[j][1];
h[2] ^= context->m[j][2];
h[3] ^= context->m[j][3];
}
else
{
//Compute M(i) = M(i / 2) * x
j = GCM_REVERSE_BITS(i / 2);
h[0] = context->m[j][0];
h[1] = context->m[j][1];
h[2] = context->m[j][2];
h[3] = context->m[j][3];
//The multiplication of a polynomial by x in GF(2^128) corresponds
//to a shift of indices
c = h[0] & 0x01;
h[0] = (h[0] >> 1) | (h[1] << 31);
h[1] = (h[1] >> 1) | (h[2] << 31);
h[2] = (h[2] >> 1) | (h[3] << 31);
h[3] >>= 1;
//If the highest term of the result is equal to one, then perform
//reduction
h[3] ^= r[GCM_REVERSE_BITS(1)] & ~(c - 1);
}
//Save M(i)
j = GCM_REVERSE_BITS(i);
context->m[j][0] = h[0];
context->m[j][1] = h[1];
context->m[j][2] = h[2];
context->m[j][3] = h[3];
}
//Successful initialization
return NO_ERROR;
}
/**
* @brief Authenticated encryption using GCM
* @param[in] context Pointer to the GCM context
* @param[in] iv Initialization vector
* @param[in] ivLen Length of the initialization vector
* @param[in] a Additional authenticated data
* @param[in] aLen Length of the additional data
* @param[in] p Plaintext to be encrypted
* @param[out] c Ciphertext resulting from the encryption
* @param[in] length Total number of data bytes to be encrypted
* @param[out] t Authentication tag
* @param[in] tLen Length of the authentication tag
* @return Error code
**/
__weak_func error_t gcmEncrypt(GcmContext *context, const uint8_t *iv,
size_t ivLen, const uint8_t *a, size_t aLen, const uint8_t *p,
uint8_t *c, size_t length, uint8_t *t, size_t tLen)
{
size_t k;
size_t n;
uint8_t b[16];
uint8_t j[16];
uint8_t s[16];
//Make sure the GCM context is valid
if(context == NULL)
return ERROR_INVALID_PARAMETER;
//The length of the IV shall meet SP 800-38D requirements
if(ivLen < 1)
return ERROR_INVALID_LENGTH;
//Check the length of the authentication tag
if(tLen < 4 || tLen > 16)
return ERROR_INVALID_LENGTH;
//Check whether the length of the IV is 96 bits
if(ivLen == 12)
{
//When the length of the IV is 96 bits, the padding string is appended
//to the IV to form the pre-counter block
osMemcpy(j, iv, 12);
STORE32BE(1, j + 12);
}
else
{
//Initialize GHASH calculation
osMemset(j, 0, 16);
//Length of the IV
n = ivLen;
//Process the initialization vector
while(n > 0)
{
//The IV is processed in a block-by-block fashion
k = MIN(n, 16);
//Apply GHASH function
gcmXorBlock(j, j, iv, k);
gcmMul(context, j);
//Next block
iv += k;
n -= k;
}
//The string is appended with 64 additional 0 bits, followed by the
//64-bit representation of the length of the IV
osMemset(b, 0, 8);
STORE64BE(ivLen * 8, b + 8);
//The GHASH function is applied to the resulting string to form the
//pre-counter block
gcmXorBlock(j, j, b, 16);
gcmMul(context, j);
}
//Compute MSB(CIPH(J(0)))
context->cipherAlgo->encryptBlock(context->cipherContext, j, b);
osMemcpy(t, b, tLen);
//Initialize GHASH calculation
osMemset(s, 0, 16);
//Length of the AAD
n = aLen;
//Process AAD
while(n > 0)
{
//Additional data are processed in a block-by-block fashion
k = MIN(n, 16);
//Apply GHASH function
gcmXorBlock(s, s, a, k);
gcmMul(context, s);
//Next block
a += k;
n -= k;
}
//Length of the plaintext
n = length;
//Process plaintext
while(n > 0)
{
//The encryption operates in a block-by-block fashion
k = MIN(n, 16);
//Increment counter
gcmIncCounter(j);
//Encrypt plaintext
context->cipherAlgo->encryptBlock(context->cipherContext, j, b);
gcmXorBlock(c, p, b, k);
//Apply GHASH function
gcmXorBlock(s, s, c, k);
gcmMul(context, s);
//Next block
p += k;
c += k;
n -= k;
}
//Append the 64-bit representation of the length of the AAD and the
//ciphertext
STORE64BE(aLen * 8, b);
STORE64BE(length * 8, b + 8);
//The GHASH function is applied to the result to produce a single output
//block S
gcmXorBlock(s, s, b, 16);
gcmMul(context, s);
//Let T = MSB(GCTR(J(0), S)
gcmXorBlock(t, t, s, tLen);
//Successful encryption
return NO_ERROR;
}
/**
* @brief Authenticated decryption using GCM
* @param[in] context Pointer to the GCM context
* @param[in] iv Initialization vector
* @param[in] ivLen Length of the initialization vector
* @param[in] a Additional authenticated data
* @param[in] aLen Length of the additional data
* @param[in] c Ciphertext to be decrypted
* @param[out] p Plaintext resulting from the decryption
* @param[in] length Total number of data bytes to be decrypted
* @param[in] t Authentication tag
* @param[in] tLen Length of the authentication tag
* @return Error code
**/
__weak_func error_t gcmDecrypt(GcmContext *context, const uint8_t *iv,
size_t ivLen, const uint8_t *a, size_t aLen, const uint8_t *c,
uint8_t *p, size_t length, const uint8_t *t, size_t tLen)
{
uint8_t mask;
size_t k;
size_t n;
uint8_t b[16];
uint8_t j[16];
uint8_t r[16];
uint8_t s[16];
//Make sure the GCM context is valid
if(context == NULL)
return ERROR_INVALID_PARAMETER;
//The length of the IV shall meet SP 800-38D requirements
if(ivLen < 1)
return ERROR_INVALID_LENGTH;
//Check the length of the authentication tag
if(tLen < 4 || tLen > 16)
return ERROR_INVALID_LENGTH;
//Check whether the length of the IV is 96 bits
if(ivLen == 12)
{
//When the length of the IV is 96 bits, the padding string is appended
//to the IV to form the pre-counter block
osMemcpy(j, iv, 12);
STORE32BE(1, j + 12);
}
else
{
//Initialize GHASH calculation
osMemset(j, 0, 16);
//Length of the IV
n = ivLen;
//Process the initialization vector
while(n > 0)
{
//The IV is processed in a block-by-block fashion
k = MIN(n, 16);
//Apply GHASH function
gcmXorBlock(j, j, iv, k);
gcmMul(context, j);
//Next block
iv += k;
n -= k;
}
//The string is appended with 64 additional 0 bits, followed by the
//64-bit representation of the length of the IV
osMemset(b, 0, 8);
STORE64BE(ivLen * 8, b + 8);
//The GHASH function is applied to the resulting string to form the
//pre-counter block
gcmXorBlock(j, j, b, 16);
gcmMul(context, j);
}
//Compute MSB(CIPH(J(0)))
context->cipherAlgo->encryptBlock(context->cipherContext, j, b);
osMemcpy(r, b, tLen);
//Initialize GHASH calculation
osMemset(s, 0, 16);
//Length of the AAD
n = aLen;
//Process AAD
while(n > 0)
{
//Additional data are processed in a block-by-block fashion
k = MIN(n, 16);
//Apply GHASH function
gcmXorBlock(s, s, a, k);
gcmMul(context, s);
//Next block
a += k;
n -= k;
}
//Length of the ciphertext
n = length;
//Process ciphertext
while(n > 0)
{
//The decryption operates in a block-by-block fashion
k = MIN(n, 16);
//Apply GHASH function
gcmXorBlock(s, s, c, k);
gcmMul(context, s);
//Increment counter
gcmIncCounter(j);
//Decrypt ciphertext
context->cipherAlgo->encryptBlock(context->cipherContext, j, b);
gcmXorBlock(p, c, b, k);
//Next block
c += k;
p += k;
n -= k;
}
//Append the 64-bit representation of the length of the AAD and the
//ciphertext
STORE64BE(aLen * 8, b);
STORE64BE(length * 8, b + 8);
//The GHASH function is applied to the result to produce a single output
//block S
gcmXorBlock(s, s, b, 16);
gcmMul(context, s);
//Let R = MSB(GCTR(J(0), S))
gcmXorBlock(r, r, s, tLen);
//The calculated tag is bitwise compared to the received tag. The message
//is authenticated if and only if the tags match
for(mask = 0, n = 0; n < tLen; n++)
{
mask |= r[n] ^ t[n];
}
//Return status code
return (mask == 0) ? NO_ERROR : ERROR_FAILURE;
}
/**
* @brief Multiplication operation in GF(2^128)
* @param[in] context Pointer to the GCM context
* @param[in, out] x 16-byte block to be multiplied by H
**/
__weak_func void gcmMul(GcmContext *context, uint8_t *x)
{
int_t i;
uint8_t b;
uint8_t c;
uint32_t z[4];
//Let Z = 0
z[0] = 0;
z[1] = 0;
z[2] = 0;
z[3] = 0;
//Fast table-driven implementation (Shoup's method)
for(i = 15; i >= 0; i--)
{
#if (GCM_TABLE_W == 4)
//Get the lower nibble
b = x[i] & 0x0F;
//Multiply 4 bits at a time
c = z[0] & 0x0F;
z[0] = (z[0] >> 4) | (z[1] << 28);
z[1] = (z[1] >> 4) | (z[2] << 28);
z[2] = (z[2] >> 4) | (z[3] << 28);
z[3] >>= 4;
z[0] ^= context->m[b][0];
z[1] ^= context->m[b][1];
z[2] ^= context->m[b][2];
z[3] ^= context->m[b][3];
//Perform reduction
z[3] ^= r[c];
//Get the upper nibble
b = (x[i] >> 4) & 0x0F;
//Multiply 4 bits at a time
c = z[0] & 0x0F;
z[0] = (z[0] >> 4) | (z[1] << 28);
z[1] = (z[1] >> 4) | (z[2] << 28);
z[2] = (z[2] >> 4) | (z[3] << 28);
z[3] >>= 4;
z[0] ^= context->m[b][0];
z[1] ^= context->m[b][1];
z[2] ^= context->m[b][2];
z[3] ^= context->m[b][3];
//Perform reduction
z[3] ^= r[c];
#else
//Get current byte
b = x[i];
//Multiply 8 bits at a time
c = z[0] & 0xFF;
z[0] = (z[0] >> 8) | (z[1] << 24);
z[1] = (z[1] >> 8) | (z[2] << 24);
z[2] = (z[2] >> 8) | (z[3] << 24);
z[3] >>= 8;
z[0] ^= context->m[b][0];
z[1] ^= context->m[b][1];
z[2] ^= context->m[b][2];
z[3] ^= context->m[b][3];
//Perform reduction
z[3] ^= r[c];
#endif
}
//Save the result
STORE32BE(z[3], x);
STORE32BE(z[2], x + 4);
STORE32BE(z[1], x + 8);
STORE32BE(z[0], x + 12);
}
/**
* @brief XOR operation
* @param[out] x Block resulting from the XOR operation
* @param[in] a First block
* @param[in] b Second block
* @param[in] n Size of the block
**/
void gcmXorBlock(uint8_t *x, const uint8_t *a, const uint8_t *b, size_t n)
{
size_t i;
//Perform XOR operation
for(i = 0; i < n; i++)
{
x[i] = a[i] ^ b[i];
}
}
/**
* @brief Increment counter block
* @param[in,out] ctr Pointer to the counter block
**/
void gcmIncCounter(uint8_t *ctr)
{
uint16_t temp;
//The function increments the right-most 32 bits of the block. The remaining
//left-most 96 bits remain unchanged
temp = ctr[15] + 1;
ctr[15] = temp & 0xFF;
temp = (temp >> 8) + ctr[14];
ctr[14] = temp & 0xFF;
temp = (temp >> 8) + ctr[13];
ctr[13] = temp & 0xFF;
temp = (temp >> 8) + ctr[12];
ctr[12] = temp & 0xFF;
}
#endif