mirror of
https://protopirate.net/ProtoPirate/ProtoPirate.git
synced 2026-03-29 14:30:23 +00:00
455 lines
15 KiB
C
455 lines
15 KiB
C
#include "aut64.h"
|
|
#include <string.h>
|
|
|
|
// https://www.usenix.org/system/files/conference/usenixsecurity16/sec16_paper_garcia.pdf
|
|
|
|
/*
|
|
* AUT64 algorithm, 12 rounds
|
|
* 8 bytes block size, 8 bytes key size
|
|
* 8 bytes pbox size, 16 bytes sbox size
|
|
*
|
|
* Based on: Reference AUT64 implementation in JavaScript (aut64.js)
|
|
* Vencislav Atanasov, 2025-09-13
|
|
*
|
|
* Based on: Reference AUT64 implementation in python
|
|
* C Hicks, hkscy.org, 03-01-19
|
|
*/
|
|
|
|
static const uint8_t table_ln[AUT64_NUM_ROUNDS][AUT64_BLOCK_SIZE] = {
|
|
{0x4, 0x5, 0x6, 0x7, 0x0, 0x1, 0x2, 0x3}, // Round 0
|
|
{0x5, 0x4, 0x7, 0x6, 0x1, 0x0, 0x3, 0x2}, // Round 1
|
|
{0x6, 0x7, 0x4, 0x5, 0x2, 0x3, 0x0, 0x1}, // Round 2
|
|
{0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0}, // Round 3
|
|
{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}, // Round 4
|
|
{0x1, 0x0, 0x3, 0x2, 0x5, 0x4, 0x7, 0x6}, // Round 5
|
|
{0x2, 0x3, 0x0, 0x1, 0x6, 0x7, 0x4, 0x5}, // Round 6
|
|
{0x3, 0x2, 0x1, 0x0, 0x7, 0x6, 0x5, 0x4}, // Round 7
|
|
{0x5, 0x4, 0x7, 0x6, 0x1, 0x0, 0x3, 0x2}, // Round 8
|
|
{0x4, 0x5, 0x6, 0x7, 0x0, 0x1, 0x2, 0x3}, // Round 9
|
|
{0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0}, // Round 10
|
|
{0x6, 0x7, 0x4, 0x5, 0x2, 0x3, 0x0, 0x1}, // Round 11
|
|
};
|
|
|
|
static const uint8_t table_un[AUT64_NUM_ROUNDS][AUT64_BLOCK_SIZE] = {
|
|
{0x1, 0x0, 0x3, 0x2, 0x5, 0x4, 0x7, 0x6}, // Round 0
|
|
{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}, // Round 1
|
|
{0x3, 0x2, 0x1, 0x0, 0x7, 0x6, 0x5, 0x4}, // Round 2
|
|
{0x2, 0x3, 0x0, 0x1, 0x6, 0x7, 0x4, 0x5}, // Round 3
|
|
{0x5, 0x4, 0x7, 0x6, 0x1, 0x0, 0x3, 0x2}, // Round 4
|
|
{0x4, 0x5, 0x6, 0x7, 0x0, 0x1, 0x2, 0x3}, // Round 5
|
|
{0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0}, // Round 6
|
|
{0x6, 0x7, 0x4, 0x5, 0x2, 0x3, 0x0, 0x1}, // Round 7
|
|
{0x3, 0x2, 0x1, 0x0, 0x7, 0x6, 0x5, 0x4}, // Round 8
|
|
{0x2, 0x3, 0x0, 0x1, 0x6, 0x7, 0x4, 0x5}, // Round 9
|
|
{0x1, 0x0, 0x3, 0x2, 0x5, 0x4, 0x7, 0x6}, // Round 10
|
|
{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}, // Round 11
|
|
};
|
|
|
|
static const uint8_t table_offset[AUT64_OFFSET_TABLE_SIZE] = {
|
|
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // 0
|
|
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, // 1
|
|
0x0, 0x2, 0x4, 0x6, 0x8, 0xA, 0xC, 0xE, 0x3, 0x1, 0x7, 0x5, 0xB, 0x9, 0xF, 0xD, // 2
|
|
0x0, 0x3, 0x6, 0x5, 0xC, 0xF, 0xA, 0x9, 0xB, 0x8, 0xD, 0xE, 0x7, 0x4, 0x1, 0x2, // 3
|
|
0x0, 0x4, 0x8, 0xC, 0x3, 0x7, 0xB, 0xF, 0x6, 0x2, 0xE, 0xA, 0x5, 0x1, 0xD, 0x9, // 4
|
|
0x0, 0x5, 0xA, 0xF, 0x7, 0x2, 0xD, 0x8, 0xE, 0xB, 0x4, 0x1, 0x9, 0xC, 0x3, 0x6, // 5
|
|
0x0, 0x6, 0xC, 0xA, 0xB, 0xD, 0x7, 0x1, 0x5, 0x3, 0x9, 0xF, 0xE, 0x8, 0x2, 0x4, // 6
|
|
0x0, 0x7, 0xE, 0x9, 0xF, 0x8, 0x1, 0x6, 0xD, 0xA, 0x3, 0x4, 0x2, 0x5, 0xC, 0xB, // 7
|
|
0x0, 0x8, 0x3, 0xB, 0x6, 0xE, 0x5, 0xD, 0xC, 0x4, 0xF, 0x7, 0xA, 0x2, 0x9, 0x1, // 8
|
|
0x0, 0x9, 0x1, 0x8, 0x2, 0xB, 0x3, 0xA, 0x4, 0xD, 0x5, 0xC, 0x6, 0xF, 0x7, 0xE, // 9
|
|
0x0, 0xA, 0x7, 0xD, 0xE, 0x4, 0x9, 0x3, 0xF, 0x5, 0x8, 0x2, 0x1, 0xB, 0x6, 0xC, // A
|
|
0x0, 0xB, 0x5, 0xE, 0xA, 0x1, 0xF, 0x4, 0x7, 0xC, 0x2, 0x9, 0xD, 0x6, 0x8, 0x3, // B
|
|
0x0, 0xC, 0xB, 0x7, 0x5, 0x9, 0xE, 0x2, 0xA, 0x6, 0x1, 0xD, 0xF, 0x3, 0x4, 0x8, // C
|
|
0x0, 0xD, 0x9, 0x4, 0x1, 0xC, 0x8, 0x5, 0x2, 0xF, 0xB, 0x6, 0x3, 0xE, 0xA, 0x7, // D
|
|
0x0, 0xE, 0xF, 0x1, 0xD, 0x3, 0x2, 0xC, 0x9, 0x7, 0x6, 0x8, 0x4, 0xA, 0xB, 0x5, // E
|
|
0x0, 0xF, 0xD, 0x2, 0x9, 0x6, 0x4, 0xB, 0x1, 0xE, 0xC, 0x3, 0x8, 0x7, 0x5, 0xA // F
|
|
};
|
|
|
|
static const uint8_t table_sub[AUT64_SBOX_SIZE] = {
|
|
0x0,
|
|
0x1,
|
|
0x9,
|
|
0xE,
|
|
0xD,
|
|
0xB,
|
|
0x7,
|
|
0x6,
|
|
0xF,
|
|
0x2,
|
|
0xC,
|
|
0x5,
|
|
0xA,
|
|
0x4,
|
|
0x3,
|
|
0x8,
|
|
};
|
|
|
|
// Build an inverse/permutation table.
|
|
// Sentinel 0xFF is used to detect missing entries and duplicates.
|
|
// Returns AUT64_OK on success, otherwise AUT64_ERR_INVALID_KEY.
|
|
static int reverse_box(uint8_t* reversed, const uint8_t* box, size_t len) {
|
|
size_t i;
|
|
|
|
for(i = 0; i < len; i++) {
|
|
reversed[i] = 0xFF;
|
|
}
|
|
|
|
for(i = 0; i < len; i++) {
|
|
const uint8_t v = box[i];
|
|
#ifdef AUT64_ENABLE_VALIDATIONS
|
|
if(v >= len) {
|
|
return AUT64_ERR_INVALID_KEY;
|
|
}
|
|
if(reversed[v] != 0xFF) {
|
|
// Duplicate value means it is not a permutation.
|
|
return AUT64_ERR_INVALID_KEY;
|
|
}
|
|
#endif
|
|
reversed[v] = (uint8_t)i;
|
|
}
|
|
|
|
#ifdef AUT64_ENABLE_VALIDATIONS
|
|
for(i = 0; i < len; i++) {
|
|
if(reversed[i] == 0xFF) {
|
|
// Missing mapping.
|
|
return AUT64_ERR_INVALID_KEY;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return AUT64_OK;
|
|
}
|
|
|
|
#ifdef AUT64_ENABLE_VALIDATIONS
|
|
|
|
// Validate that 'box' is a permutation of 0..len-1.
|
|
// Uses 0xFF sentinel logic to detect duplicates/missing values.
|
|
static int validate_box_is_permutation(const uint8_t* box, size_t len) {
|
|
uint8_t inv[32]; // enough for pbox (8) and sbox (16)
|
|
size_t i;
|
|
|
|
if(len > sizeof(inv)) {
|
|
return AUT64_ERR_INVALID_KEY;
|
|
}
|
|
|
|
for(i = 0; i < len; i++) {
|
|
inv[i] = 0xFF;
|
|
}
|
|
|
|
for(i = 0; i < len; i++) {
|
|
const uint8_t v = box[i];
|
|
if(v >= len) {
|
|
return AUT64_ERR_INVALID_KEY;
|
|
}
|
|
if(inv[v] != 0xFF) {
|
|
return AUT64_ERR_INVALID_KEY;
|
|
}
|
|
inv[v] = (uint8_t)i;
|
|
}
|
|
|
|
for(i = 0; i < len; i++) {
|
|
if(inv[i] == 0xFF) {
|
|
return AUT64_ERR_INVALID_KEY;
|
|
}
|
|
}
|
|
|
|
return AUT64_OK;
|
|
}
|
|
|
|
// Validate that a key is structurally correct:
|
|
// - key nibbles are in range 0..15
|
|
// - pbox is a permutation of 0..7
|
|
// - sbox is a permutation of 0..15
|
|
// return AUT64_OK or AUT64_ERR_INVALID_KEY/AUT64_ERR_NULL_POINTER
|
|
int aut64_validate_key(const struct aut64_key* key) {
|
|
uint8_t i;
|
|
int rc;
|
|
|
|
if(!key) {
|
|
return AUT64_ERR_NULL_POINTER;
|
|
}
|
|
|
|
// key->key[] is treated as nibbles in multiple places (table_sub indexing, offset building).
|
|
for(i = 0; i < AUT64_KEY_SIZE; i++) {
|
|
if(key->key[i] >= AUT64_SBOX_SIZE) {
|
|
return AUT64_ERR_INVALID_KEY;
|
|
}
|
|
}
|
|
|
|
rc = validate_box_is_permutation(key->pbox, AUT64_PBOX_SIZE);
|
|
if(rc != AUT64_OK) {
|
|
return rc;
|
|
}
|
|
|
|
rc = validate_box_is_permutation(key->sbox, AUT64_SBOX_SIZE);
|
|
if(rc != AUT64_OK) {
|
|
return rc;
|
|
}
|
|
|
|
return AUT64_OK;
|
|
}
|
|
|
|
#endif // AUT64_ENABLE_VALIDATIONS
|
|
|
|
// Compute one 4-bit contribution to the round key
|
|
static uint8_t key_nibble(
|
|
const struct aut64_key* key,
|
|
uint8_t nibble,
|
|
const uint8_t table[AUT64_BLOCK_SIZE],
|
|
uint8_t iteration) {
|
|
const uint8_t keyValue = key->key[table[iteration]];
|
|
const uint8_t offset = (uint8_t)((keyValue << 4) | nibble);
|
|
return table_offset[offset];
|
|
}
|
|
|
|
// Compute the round compression byte derived from the current state and the key for a given round.
|
|
static uint8_t round_key(const struct aut64_key* key, const uint8_t* state, uint8_t roundN) {
|
|
uint8_t result_hi = 0, result_lo = 0;
|
|
|
|
for(int i = 0; i < AUT64_BLOCK_SIZE - 1; i++) {
|
|
result_hi ^= key_nibble(key, (uint8_t)(state[i] >> 4), table_un[roundN], (uint8_t)i);
|
|
result_lo ^= key_nibble(key, (uint8_t)(state[i] & 0x0F), table_ln[roundN], (uint8_t)i);
|
|
}
|
|
|
|
return (uint8_t)((result_hi << 4) | result_lo);
|
|
}
|
|
|
|
// Compute the transformed key nibble used as an offset for final-byte processing in a round.
|
|
static uint8_t
|
|
final_byte_nibble(const struct aut64_key* key, const uint8_t table[AUT64_BLOCK_SIZE]) {
|
|
const uint8_t keyValue = key->key[table[AUT64_BLOCK_SIZE - 1]];
|
|
return (uint8_t)(table_sub[keyValue] << 4);
|
|
}
|
|
|
|
// Compute the inverse lookup for a final-byte nibble during encryption.
|
|
static uint8_t encrypt_final_byte_nibble(
|
|
const struct aut64_key* key,
|
|
uint8_t nibble,
|
|
const uint8_t table[AUT64_BLOCK_SIZE]) {
|
|
const uint8_t offset = final_byte_nibble(key, table);
|
|
|
|
for(int i = 0; i < 16; i++) {
|
|
if(table_offset[(uint8_t)(offset + i)] == nibble) {
|
|
return (uint8_t)i;
|
|
}
|
|
}
|
|
// Should never happen for valid inputs; return 0 as a defined value.
|
|
return 0;
|
|
}
|
|
|
|
// Perform the compression step for one encryption round, producing the new last byte.
|
|
static uint8_t
|
|
encrypt_compress(const struct aut64_key* key, const uint8_t* state, uint8_t roundN) {
|
|
const uint8_t roundKey = round_key(key, state, roundN);
|
|
uint8_t result_hi = (uint8_t)(roundKey >> 4), result_lo = (uint8_t)(roundKey & 0x0F);
|
|
|
|
result_hi ^= encrypt_final_byte_nibble(
|
|
key, (uint8_t)(state[AUT64_BLOCK_SIZE - 1] >> 4), table_un[roundN]);
|
|
result_lo ^= encrypt_final_byte_nibble(
|
|
key, (uint8_t)(state[AUT64_BLOCK_SIZE - 1] & 0x0F), table_ln[roundN]);
|
|
|
|
return (uint8_t)((result_hi << 4) | result_lo);
|
|
}
|
|
|
|
// Reverse the final-byte nibble transformation during decryption.
|
|
static uint8_t decrypt_final_byte_nibble(
|
|
const struct aut64_key* key,
|
|
uint8_t nibble,
|
|
const uint8_t table[AUT64_BLOCK_SIZE],
|
|
uint8_t result) {
|
|
const uint8_t offset = final_byte_nibble(key, table);
|
|
return table_offset[(uint8_t)((result ^ nibble) + offset)];
|
|
}
|
|
|
|
// Perform the compression step for one decryption round, restoring the previous last byte.
|
|
static uint8_t
|
|
decrypt_compress(const struct aut64_key* key, const uint8_t* state, uint8_t roundN) {
|
|
const uint8_t roundKey = round_key(key, state, roundN);
|
|
uint8_t result_hi = (uint8_t)(roundKey >> 4), result_lo = (uint8_t)(roundKey & 0x0F);
|
|
|
|
result_hi = decrypt_final_byte_nibble(
|
|
key, (uint8_t)(state[AUT64_BLOCK_SIZE - 1] >> 4), table_un[roundN], result_hi);
|
|
result_lo = decrypt_final_byte_nibble(
|
|
key, (uint8_t)(state[AUT64_BLOCK_SIZE - 1] & 0x0F), table_ln[roundN], result_lo);
|
|
|
|
return (uint8_t)((result_hi << 4) | result_lo);
|
|
}
|
|
|
|
// Apply the S-box substitution to a single byte.
|
|
static uint8_t substitute(const struct aut64_key* key, uint8_t byte) {
|
|
return (uint8_t)((key->sbox[byte >> 4] << 4) | key->sbox[byte & 0x0F]);
|
|
}
|
|
|
|
// Apply the byte-level permutation (pbox) to the 8-byte state block.
|
|
static void permute_bytes(const struct aut64_key* key, uint8_t* state) {
|
|
// Key is validated up-front, so pbox[] is a correct permutation of 0..7.
|
|
uint8_t result[AUT64_PBOX_SIZE];
|
|
|
|
for(int i = 0; i < AUT64_PBOX_SIZE; i++) {
|
|
result[key->pbox[i]] = state[i];
|
|
}
|
|
|
|
memcpy(state, result, AUT64_PBOX_SIZE);
|
|
}
|
|
|
|
// Apply bit-level permutation to a single byte using the pbox mapping.
|
|
static uint8_t permute_bits(const struct aut64_key* key, uint8_t byte) {
|
|
// Key is validated up-front, so pbox[] is a correct permutation of 0..7.
|
|
uint8_t result = 0;
|
|
|
|
for(int i = 0; i < 8; i++) {
|
|
if(byte & (1 << i)) {
|
|
result |= (uint8_t)(1 << key->pbox[i]);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Encrypt one 8-byte block in place using the provided validated key.
|
|
int aut64_encrypt(const struct aut64_key* key, uint8_t* message) {
|
|
int rc;
|
|
|
|
#ifdef AUT64_ENABLE_VALIDATIONS
|
|
if(!key || !message) {
|
|
return AUT64_ERR_NULL_POINTER;
|
|
}
|
|
// Validate key before doing anything. This prevents silent, unsafe behavior.
|
|
rc = aut64_validate_key(key);
|
|
if(rc != AUT64_OK) {
|
|
return rc;
|
|
}
|
|
#endif
|
|
|
|
// Build a reversed key (inverse pbox and sbox) ...
|
|
// Fully initialize to avoid any uninitialized fields/padding.
|
|
struct aut64_key reverse_key = (struct aut64_key){0};
|
|
reverse_key.index = key->index;
|
|
memcpy(reverse_key.key, key->key, AUT64_KEY_SIZE);
|
|
|
|
rc = reverse_box(reverse_key.pbox, key->pbox, AUT64_PBOX_SIZE);
|
|
if(rc != AUT64_OK) {
|
|
return rc;
|
|
}
|
|
|
|
rc = reverse_box(reverse_key.sbox, key->sbox, AUT64_SBOX_SIZE);
|
|
if(rc != AUT64_OK) {
|
|
return rc;
|
|
}
|
|
|
|
for(int i = 0; i < AUT64_NUM_ROUNDS; i++) {
|
|
permute_bytes(&reverse_key, message);
|
|
message[AUT64_BLOCK_SIZE - 1] = encrypt_compress(&reverse_key, message, (uint8_t)i);
|
|
message[AUT64_BLOCK_SIZE - 1] = substitute(&reverse_key, message[AUT64_BLOCK_SIZE - 1]);
|
|
message[AUT64_BLOCK_SIZE - 1] = permute_bits(&reverse_key, message[AUT64_BLOCK_SIZE - 1]);
|
|
message[AUT64_BLOCK_SIZE - 1] = substitute(&reverse_key, message[AUT64_BLOCK_SIZE - 1]);
|
|
}
|
|
|
|
return AUT64_OK;
|
|
}
|
|
|
|
// Decrypt one 8-byte block in place using the provided validated key.
|
|
int aut64_decrypt(const struct aut64_key* key, uint8_t* message) {
|
|
#ifdef AUT64_ENABLE_VALIDATIONS
|
|
if(!key || !message) {
|
|
return AUT64_ERR_NULL_POINTER;
|
|
}
|
|
int rc = aut64_validate_key(key);
|
|
if(rc != AUT64_OK) {
|
|
return rc;
|
|
}
|
|
#endif
|
|
|
|
for(int i = AUT64_NUM_ROUNDS - 1; i >= 0; i--) {
|
|
message[AUT64_BLOCK_SIZE - 1] = substitute(key, message[AUT64_BLOCK_SIZE - 1]);
|
|
message[AUT64_BLOCK_SIZE - 1] = permute_bits(key, message[AUT64_BLOCK_SIZE - 1]);
|
|
message[AUT64_BLOCK_SIZE - 1] = substitute(key, message[AUT64_BLOCK_SIZE - 1]);
|
|
message[AUT64_BLOCK_SIZE - 1] = decrypt_compress(key, message, (uint8_t)i);
|
|
permute_bytes(key, message);
|
|
}
|
|
|
|
return AUT64_OK;
|
|
}
|
|
|
|
#ifdef AUT64_PACK_SUPPORT
|
|
// Serialize a validated key structure into its 16-byte packed format.
|
|
int aut64_pack(uint8_t* dest, const struct aut64_key* src) {
|
|
#ifdef AUT64_ENABLE_VALIDATIONS
|
|
if(!dest || !src) {
|
|
return AUT64_ERR_NULL_POINTER;
|
|
}
|
|
// Validate the key we are about to pack. This prevents producing garbage packed keys.
|
|
int rc = aut64_validate_key(src);
|
|
if(rc != AUT64_OK) {
|
|
return rc;
|
|
}
|
|
#endif
|
|
|
|
// Initialize the output so callers never observe stale bytes.
|
|
memset(dest, 0, AUT64_PACKED_KEY_SIZE);
|
|
|
|
dest[0] = src->index;
|
|
|
|
for(uint8_t i = 0; i < AUT64_KEY_SIZE / 2; i++) {
|
|
dest[i + 1] = (uint8_t)((src->key[i * 2] << 4) | src->key[i * 2 + 1]);
|
|
}
|
|
|
|
uint32_t pbox = 0;
|
|
for(uint8_t i = 0; i < AUT64_PBOX_SIZE; i++) {
|
|
pbox = (pbox << 3) | src->pbox[i];
|
|
}
|
|
|
|
dest[5] = (uint8_t)(pbox >> 16);
|
|
dest[6] = (uint8_t)((pbox >> 8) & 0xFF);
|
|
dest[7] = (uint8_t)(pbox & 0xFF);
|
|
|
|
for(uint8_t i = 0; i < AUT64_SBOX_SIZE / 2; i++) {
|
|
dest[i + 8] = (uint8_t)((src->sbox[i * 2] << 4) | src->sbox[i * 2 + 1]);
|
|
}
|
|
|
|
return AUT64_OK;
|
|
}
|
|
#endif // AUT64_PACK_SUPPORT
|
|
|
|
// Deserialize a 16-byte packed key into a key structure and validate it.
|
|
int aut64_unpack(struct aut64_key* dest, const uint8_t* src) {
|
|
#ifdef AUT64_ENABLE_VALIDATIONS
|
|
if(!dest || !src) {
|
|
return AUT64_ERR_NULL_POINTER;
|
|
}
|
|
#endif
|
|
|
|
// Clear the whole struct first, so all fields are in a defined state.
|
|
*dest = (struct aut64_key){0};
|
|
|
|
dest->index = src[0];
|
|
|
|
for(uint8_t i = 0; i < AUT64_KEY_SIZE / 2; i++) {
|
|
dest->key[i * 2] = (uint8_t)(src[i + 1] >> 4);
|
|
dest->key[i * 2 + 1] = (uint8_t)(src[i + 1] & 0xF);
|
|
}
|
|
|
|
uint32_t pbox = ((uint32_t)src[5] << 16) | ((uint32_t)src[6] << 8) | (uint32_t)src[7];
|
|
|
|
for(int8_t i = AUT64_PBOX_SIZE - 1; i >= 0; i--) {
|
|
dest->pbox[i] = (uint8_t)(pbox & 0x7);
|
|
pbox >>= 3;
|
|
}
|
|
|
|
for(uint8_t i = 0; i < AUT64_SBOX_SIZE / 2; i++) {
|
|
dest->sbox[i * 2] = (uint8_t)(src[i + 8] >> 4);
|
|
dest->sbox[i * 2 + 1] = (uint8_t)(src[i + 8] & 0xF);
|
|
}
|
|
|
|
#ifdef AUT64_ENABLE_VALIDATIONS
|
|
// Validate what we just unpacked. If invalid, return error.
|
|
// We do not fix up broken keys silently.
|
|
int rc = aut64_validate_key(dest);
|
|
if(rc != AUT64_OK) {
|
|
return AUT64_ERR_INVALID_PACKED;
|
|
}
|
|
#endif
|
|
|
|
return AUT64_OK;
|
|
}
|