cancel
Showing results for 
Search instead for 
Did you mean: 

STM32 AES GCM Tag mismatch

Dimlite
Associate III

Hi,

I am trying to send encrypted data between a C# application and STM32 in AES GCM mode.
Currently, I am only doing encryption of a known test vector.

Key and IV are both all zeros.
AAD length is 0.
My testvector is: 00 00 00 00 00 01 00 00 00 00 00 00 00 00 42 49

Using the code below, I get the same ciphertext in both applications:

03 88 DA CE 60 B7 A3 92 F3 28 C2 B9 71 B2 BC 31 

But the tag never matches.

I suspect there is something wrong in the final stage, where the tag is calculated.

void AesTest()
{
    uint8 iv[12];
    uint8 plainText[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x49 };
    uint8 header[0];
    uint8 cipherText[sizeof(plainText)];
    uint8 authTag[16];

    memset(iv, 0, sizeof(iv));

    uart_printf("ENCRYPT\r\n");

    if (!AesEncryptGcm(iv, header, sizeof(header), plainText, sizeof(plainText), cipherText, authTag))
        return;
}



bool AesEncryptGcm(const uint8 *iv, const uint8 *header, uint8 headerLen, const uint8 *plainText, uint16 len, uint8 *cipherText, uint8 *authTag)
{
    if ((headerLen % 16) != 0 || (len % 16) != 0)
    {
        uart_printf("AES encryption error: header and src length must be a multiple of 16 bytes\r\n");
        return false;
    }

    AesGcm(true, (const uint32*) iv, (const uint32*) header, headerLen / 4, (const uint32*) plainText, len / 4, (uint32*) cipherText, (uint32*) authTag);

    return true;
}


static void AesGcm(bool encrypt, const uint32 *iv, const uint32 *header, uint16 headerLen, const uint32 *src, uint16 srcLen, uint32 *dst, uint32 *authTag)
{
    // GCM INITIALIZE.

    // Must disable AES peripheral before setting mode, key etc.
    AES->CR &= ~BIT(0);

    // Chaining mode: GCM.
    AES->CR = (AES->CR & 0b11111111111111111111111110011111) | (0x3 << 5);

    // Mode: encrypt or decrypt.
    if (encrypt)
        AES->CR = (AES->CR & 0b11111111111111111111111111100111) | (0x0 << 3);  // Encryption.
    else
        AES->CR = (AES->CR & 0b11111111111111111111111111100111) | (0x2 << 3);  // Decryption.

    // Data type: ASCII stream = 8-bit data (byte swapping).
    AES->CR = (AES->CR & 0b11111111111111111111111111111001) | (0x2 << 1);

    // Key size: 128-bit.
    AES->CR &= ~BIT(18);

    // Key mode: key in AES core.
    AES->CR = (AES->CR & 0b11111100111111111111111111111111) | (0x0 << 24);

    // Select GCM initialization phase.
    AES->CR = (AES->CR & 0b11111111111111111001111111111111) | (0x0 << 13);

    // Write 96-bit IV + 32-bit counter start value.
    AES->IVR3 = iv[0];
    AES->IVR2 = iv[1];
    AES->IVR1 = iv[2];
    AES->IVR0 = 0x0002;     // Counter starts from 2 for each new IV, automatically increased by the AES engine for each block.

    // Write key. The key registers must always be written in ascending or descending order.
    AES->KEYR3 = key[0];    // Key[127:96]
    AES->KEYR2 = key[1];    // Key[95:64]
    AES->KEYR1 = key[2];    // Key[63:32]
    AES->KEYR0 = key[3];    // Key[31:0]

    // Wait until key valid.
    while ((AES->SR & BIT(7)) == 0)
        ;

    // Start hash key calculation.
    // Enable AES peripheral. Automatically cleared when the calculation is completed.
    AES->CR |= BIT(0);

    while ((AES->ISR & BIT(0)) == 0)
        ;

    AES->ICR = BIT(0); // Clear CCF flag.

    // GCM HEADER PHASE.

    // Select GCM header phase.
    AES->CR = (AES->CR & 0b11111111111111111001111111111111) | (0x1 << 13);

    // Enable AES peripheral.
    AES->CR |= BIT(0);

    // Append header data.
    // A 16-byte data block enters the AES peripheral with four successive 32-bit word writes.
    // The most significant word (bits [127:96]) first, the least significant word (bits [31:0]) last.
    // The four 32-bit words of a 16-byte data block must be stored in the memory consecutively
    // and in big-endian order, that is, with the most significant word on the lowest address.

    for (uint16 block = 0; block < headerLen; block += 4)
    {
        AES->ICR = BIT(0);  // Clear CCF flag

        for (uint8 i = 0; i < 4; i++)
            AES->DINR = header[block + i];

        // Wait for operation to complete.
        while ((AES->ISR & BIT(0)) == 0)
            ;

        // No data are read during header phase.
    }

    // GCM PAYLOAD PHASE.

    // Select GCM payload phase.
    AES->CR = (AES->CR & 0b11111111111111111001111111111111) | (0x2 << 13);

    for (uint16 block = 0; block < srcLen; block += 4)
    {
        AES->ICR = BIT(0);  // Clear CCF flag

        for (uint8 i = 0; i < 4; i++)
            AES->DINR = src[block + i];

        // Wait for operation to complete.
        while ((AES->ISR & BIT(0)) == 0)
            ;

        // Save the header block.
        // Data weights from the first to the fourth read operation are: [127:96], [95:64], [63:32], and [31:0].
        for (uint8 i = 0; i < 4; i++)
            dst[block + i] = AES->DOUTR;
    }

    // GCM FINALIZATION.

    // Select GCM final phase.
    AES->CR = (AES->CR & 0b11111111111111111001111111111111) | (0x3 << 13);

    // In the final phase, data are written to AES_DINR normally (no swapping), while swapping is
    // applied to tag data read from AES_DOUTR.

    uart_printf("header len = %d\r\n", headerLen);
    uart_printf("payload len = %d\r\n", srcLen);

    // Write the concatenated AAD and payload byte lengths.
    AES->ICR = BIT(0);  // Clear CCF flag

    AES->DINR = 0;              // AAD length [63:32]
    AES->DINR = headerLen * 4;  // AAD length [31:0]
    AES->DINR = 0;              // Payload length [63:32]
    AES->DINR = srcLen * 4;     // Payload length [31:0]

    // Wait for operation to complete.
    while ((AES->ISR & BIT(0)) == 0)
        ;

    // Get GCM authentication tag.
    // Swapping is applied to tag data read from AES_DOUTR.
    authTag[0] = __REV(AES->DOUTR);
    authTag[1] = __REV(AES->DOUTR);
    authTag[2] = __REV(AES->DOUTR);
    authTag[3] = __REV(AES->DOUTR);

    AES->ICR = BIT(0);  // Clear CCF flag

    // Disable AES peripheral.
    AES->CR &= ~BIT(0);
}

 

0 REPLIES 0