Skip to content

Commit

Permalink
SCP11: Use existing functions to encrypt/decrypt messages sent over s…
Browse files Browse the repository at this point in the history
…ecure channel
  • Loading branch information
aveenismail committed Dec 12, 2024
1 parent 60d90e3 commit be6779a
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 92 deletions.
113 changes: 37 additions & 76 deletions lib/aes_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
#include <openssl/x509.h>
#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
#include <openssl/core_names.h>
#include <openssl/aes.h>
#endif

//static void dump_byte_array(uint8_t *a, size_t len, const char* label) {
Expand Down Expand Up @@ -164,37 +163,28 @@ static ykpiv_rc scp11_get_iv(uint8_t *key, uint32_t counter, uint8_t *iv, bool d
iv_data[0] = 0x80;
}
uint32_t c = htonl(counter);
memcpy(iv_data + AES_BLOCK_SIZE - sizeof(int), &c, sizeof(int));
memcpy(iv_data + SCP11_AES_BLOCK_SIZE - sizeof(int), &c, sizeof(int));

EVP_CIPHER_CTX *ctx;
if (!(ctx = EVP_CIPHER_CTX_new())) {
DBG("Failed to create cipher context");
return YKPIV_AUTHENTICATION_ERROR;
}
EVP_CIPHER_CTX_set_padding(ctx, 0);

int len, tmp_len;
if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key, NULL)) {
DBG("Failed to initiate cipher operation");
res = YKPIV_AUTHENTICATION_ERROR;
cipher_key enc_key = NULL;
cipher_rc drc = cipher_import_key(YKPIV_ALGO_AES128, key, SCP11_SESSION_KEY_LEN, &enc_key);
if (drc != CIPHER_OK) {
DBG("%s: cipher_import_key: %d", ykpiv_strerror(YKPIV_ALGORITHM_ERROR), drc);
res = YKPIV_ALGORITHM_ERROR;
goto enc_clean;
}

if (1 != EVP_EncryptUpdate(ctx, iv, &len, iv_data, sizeof(iv_data))) {
DBG("Failed to encrypt data");
res = YKPIV_AUTHENTICATION_ERROR;
goto enc_clean;
}

// Finalise the encryption. Further ciphertext bytes may be written at this stage
if (1 != EVP_EncryptFinal_ex(ctx, iv + len, &tmp_len)) {
DBG("Failed to finalize encryption operation");
res = YKPIV_AUTHENTICATION_ERROR;
int len = SCP11_AES_BLOCK_SIZE;
drc = cipher_encrypt(enc_key, iv_data, sizeof(iv_data), NULL, 0, iv, &len);
if (drc != CIPHER_OK) {
DBG("%s: cipher_encrypt: %d", ykpiv_strerror(YKPIV_KEY_ERROR), drc);
res = YKPIV_KEY_ERROR;
goto enc_clean;
}

enc_clean:
EVP_CIPHER_CTX_free(ctx);
if(enc_key) {
cipher_destroy_key(enc_key);
}
return res;
}
#endif
Expand All @@ -216,41 +206,25 @@ aescbc_encrypt_data(uint8_t *key, uint32_t counter, const uint8_t *data, size_t
padded[data_len] = 0x80;
memset(padded + data_len + 1, 0, pad_len - 1);

EVP_CIPHER_CTX *ctx;
if (!(ctx = EVP_CIPHER_CTX_new())) {
DBG("Failed to create cipher context");
rc = YKPIV_AUTHENTICATION_ERROR;
cipher_key enc_key = NULL;
cipher_rc drc = cipher_import_key_cbc(YKPIV_ALGO_AES128, key, SCP11_SESSION_KEY_LEN, &enc_key);
if (drc != CIPHER_OK) {
DBG("%s: cipher_import_key: %d", ykpiv_strerror(YKPIV_ALGORITHM_ERROR), drc);
rc = YKPIV_ALGORITHM_ERROR;
goto enc_clean;
}

EVP_CIPHER_CTX_init(ctx);
EVP_CIPHER_CTX_set_padding(ctx, 0);

int len, tmp_len;
if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv)) {
DBG("Failed to initiate cipher operation");
rc = YKPIV_AUTHENTICATION_ERROR;
drc = cipher_encrypt(enc_key, padded, data_len + pad_len, iv, SCP11_AES_BLOCK_SIZE, enc, (uint32_t *) enc_len);
if (drc != CIPHER_OK) {
DBG("%s: cipher_encrypt: %d", ykpiv_strerror(YKPIV_KEY_ERROR), drc);
rc = YKPIV_KEY_ERROR;
goto enc_clean;
}

if (1 != EVP_EncryptUpdate(ctx, enc, &len, padded, data_len + pad_len)) {
DBG("Failed to encrypt data");
rc = YKPIV_AUTHENTICATION_ERROR;
goto enc_clean;
}

// Finalise the encryption. Further ciphertext bytes may be written at this stage
if (1 != EVP_EncryptFinal_ex(ctx, enc + len, &tmp_len)) {
DBG("Failed to finalize encryption operation");
rc = YKPIV_AUTHENTICATION_ERROR;
goto enc_clean;
}
*enc_len = len + tmp_len;

enc_clean:
free(padded);
if (ctx) {
EVP_CIPHER_CTX_free(ctx);
if(enc_key) {
cipher_destroy_key(enc_key);
}
#endif
return rc;
Expand All @@ -273,35 +247,20 @@ aescbc_decrypt_data(uint8_t *key, uint32_t counter, uint8_t *enc, size_t enc_len
return rc;
}

EVP_CIPHER_CTX *ctx;
if (!(ctx = EVP_CIPHER_CTX_new())) {
DBG("Failed to create cipher context");
return YKPIV_AUTHENTICATION_ERROR;
}
EVP_CIPHER_CTX_init(ctx);
EVP_CIPHER_CTX_set_padding(ctx, 0);

int len, tmp_len;

if (1 != EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv)) {
DBG("Failed to initiate cipher operation");
rc = YKPIV_AUTHENTICATION_ERROR;
goto aes_dec_clean;
}

if (1 != EVP_DecryptUpdate(ctx, data, &len, enc, enc_len)) {
DBG("Failed to decrypt data");
rc = YKPIV_AUTHENTICATION_ERROR;
cipher_key dec_key = NULL;
cipher_rc drc = cipher_import_key_cbc(YKPIV_ALGO_AES128, key, SCP11_SESSION_KEY_LEN, &dec_key);
if (drc != CIPHER_OK) {
DBG("%s: cipher_import_key: %d", ykpiv_strerror(YKPIV_ALGORITHM_ERROR), drc);
rc = YKPIV_ALGORITHM_ERROR;
goto aes_dec_clean;
}

// Finalise the encryption. Further ciphertext bytes may be written at this stage
if (1 != EVP_DecryptFinal_ex(ctx, data + len, &tmp_len)) {
DBG("Failed to finalize encryption operation");
rc = YKPIV_AUTHENTICATION_ERROR;
drc = cipher_decrypt(dec_key, enc, enc_len, iv, SCP11_AES_BLOCK_SIZE, data, (uint32_t *) data_len);
if (drc != CIPHER_OK) {
DBG("%s: cipher_decrypt: %d", ykpiv_strerror(YKPIV_KEY_ERROR), drc);
rc = YKPIV_KEY_ERROR;
goto aes_dec_clean;
}
*data_len = len + tmp_len;

// Remove padding
while (data[(*data_len) - 1] == 0x00) {
Expand All @@ -312,7 +271,9 @@ aescbc_decrypt_data(uint8_t *key, uint32_t counter, uint8_t *enc, size_t enc_len
}

aes_dec_clean:
EVP_CIPHER_CTX_free(ctx);
if(dec_key) {
cipher_destroy_key(dec_key);
}
#endif
return rc;
}
51 changes: 51 additions & 0 deletions lib/cmac_util.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//
// Created by aveen on 12/12/24.
//

#ifndef YUBICO_PIV_TOOL_CMAC_H
#define YUBICO_PIV_TOOL_CMAC_H

#ifdef _WIN32
#include <windows.h>
#include <bcrypt.h>
#include <ntstatus.h>
#else
#include <openssl/evp.h>
#endif

struct _mac_key {
#ifdef _WIN32
BCRYPT_ALG_HANDLE hAlgCBC;
BCRYPT_ALG_HANDLE hAlgECB;
BCRYPT_KEY_HANDLE hKeyCBC;
BCRYPT_KEY_HANDLE hKeyECB;
PBYTE pbKeyCBCObj;
PBYTE pbKeyECBObj;
size_t cbKeyObj;
#else
EVP_CIPHER_CTX *ctx;
uint8_t key[SCP11_SESSION_KEY_LEN];
#endif
};


typedef struct {
aes_context *aes_ctx;
uint8_t k1[AES_BLOCK_SIZE];
uint8_t k2[AES_BLOCK_SIZE];
} aes_cmac_context_t;

#ifndef __WIN32
#define YH_INTERNAL __attribute__((visibility("hidden")))
#else
#define YH_INTERNAL
#endif

int YH_INTERNAL aes_cmac_init(aes_context *aes_ctx, aes_cmac_context_t *ctx);
int YH_INTERNAL aes_cmac_encrypt(aes_cmac_context_t *ctx,
const uint8_t *message,
const uint16_t message_len, uint8_t *mac);
void YH_INTERNAL aes_cmac_destroy(aes_cmac_context_t *ctx);


#endif //YUBICO_PIV_TOOL_CMAC_H
67 changes: 58 additions & 9 deletions lib/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,23 @@ cipher_rc cipher_import_key(unsigned char algo, const unsigned char* keyraw, uin
return 0;
}

cipher_rc cipher_import_key_cbc(unsigned char algo, const unsigned char* keyraw, uint32_t keyrawlen, cipher_key* key) {
*key = calloc(1, sizeof(**key));
if (!*key) {
return CIPHER_MEMORY_ERROR;
}
if (!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&(*key)->hAlg, bcrypt_algo(algo), NULL, 0))) {
return CIPHER_INVALID_PARAMETER;
}
if (!BCRYPT_SUCCESS(BCryptSetProperty((*key)->hAlg, BCRYPT_CHAINING_MODE, (PBYTE)BCRYPT_CHAIN_MODE_CBC, sizeof(BCRYPT_CHAIN_MODE_CBC), 0))) {
return CIPHER_INVALID_PARAMETER;
}
if (!BCRYPT_SUCCESS(BCryptGenerateSymmetricKey((*key)->hAlg, &(*key)->hKey, NULL, 0, (PUCHAR)keyraw, keyrawlen, 0))) {
return CIPHER_INVALID_PARAMETER;
}
return 0;
}

cipher_rc cipher_destroy_key(cipher_key key) {
if (key == NULL) {
return CIPHER_MEMORY_ERROR;
Expand All @@ -135,21 +152,21 @@ cipher_rc cipher_destroy_key(cipher_key key) {
return CIPHER_OK;
}

cipher_rc cipher_encrypt(cipher_key key, const unsigned char* in, uint32_t inlen, unsigned char* out, uint32_t* outlen) {
if (key == NULL) {
cipher_rc cipher_encrypt(cipher_key key, const unsigned char* in, uint32_t inlen, const unsigned char *iv, uint32_t iv_len, unsigned char* out, uint32_t* outlen) {
if (key == NULL) {
return CIPHER_MEMORY_ERROR;
}
if(!BCRYPT_SUCCESS(BCryptEncrypt(key->hKey, (PUCHAR)in, inlen, NULL, NULL, 0, out, *outlen, (PULONG)outlen, 0))) {
if(!BCRYPT_SUCCESS(BCryptEncrypt(key->hKey, (PUCHAR)in, inlen, NULL, iv, iv_len, out, *outlen, (PULONG)outlen, 0))) {
return CIPHER_INVALID_PARAMETER;
}
return CIPHER_OK;
}

cipher_rc cipher_decrypt(cipher_key key, const unsigned char* in, uint32_t inlen, unsigned char* out, uint32_t* outlen) {
cipher_rc cipher_decrypt(cipher_key key, const unsigned char* in, uint32_t inlen, const unsigned char *iv, uint32_t iv_len, unsigned char* out, uint32_t* outlen) {
if (key == NULL) {
return CIPHER_MEMORY_ERROR;
}
if(!BCRYPT_SUCCESS(BCryptDecrypt(key->hKey, (PUCHAR)in, inlen, NULL, NULL, 0, out, *outlen, (PULONG)outlen, 0))) {
if(!BCRYPT_SUCCESS(BCryptDecrypt(key->hKey, (PUCHAR)in, inlen, NULL, iv, iv_len, out, *outlen, (PULONG)outlen, 0))) {
return CIPHER_INVALID_PARAMETER;
}
return CIPHER_OK;
Expand Down Expand Up @@ -225,6 +242,38 @@ cipher_rc cipher_import_key(unsigned char algo, const unsigned char* keyraw, uin
EXIT:
return rc;

ERROR_EXIT:
if (key) {
cipher_destroy_key(*key);
*key = NULL;
}
goto EXIT;
}
cipher_rc cipher_import_key_cbc(unsigned char algo, const unsigned char* keyraw, uint32_t keyrawlen, cipher_key* key) {
cipher_rc rc = CIPHER_OK;

*key = calloc(1, sizeof(**key));
(*key)->ctx = EVP_CIPHER_CTX_new();

switch (algo) {
case YKPIV_ALGO_AES128:
(*key)->cipher = EVP_aes_128_cbc();
break;
default:
rc = CIPHER_INVALID_PARAMETER;
goto ERROR_EXIT;
}

if((*key)->cipher == NULL || EVP_CIPHER_key_length((*key)->cipher) != keyrawlen) {
rc = CIPHER_INVALID_PARAMETER;
goto ERROR_EXIT;
}

memcpy((*key)->key, keyraw, keyrawlen);

EXIT:
return rc;

ERROR_EXIT:
if (key) {
cipher_destroy_key(*key);
Expand All @@ -242,23 +291,23 @@ cipher_rc cipher_destroy_key(cipher_key key) {
return CIPHER_OK;
}

cipher_rc cipher_encrypt(cipher_key key, const unsigned char* in, uint32_t inlen, unsigned char* out, uint32_t* outlen) {
cipher_rc cipher_encrypt(cipher_key key, const unsigned char* in, uint32_t inlen, const unsigned char *iv, uint32_t iv_len, unsigned char* out, uint32_t* outlen) {
cipher_rc rc = CIPHER_OK;

if (!key || !outlen || (*outlen < inlen) || !in || !out) { rc = CIPHER_INVALID_PARAMETER; goto EXIT; }

rc = encrypt_ex(in, out, inlen, NULL, 1, key);
rc = encrypt_ex(in, out, inlen, iv, 1, key);

EXIT:
return rc;
}

cipher_rc cipher_decrypt(cipher_key key, const unsigned char* in, uint32_t inlen, unsigned char* out, uint32_t* outlen) {
cipher_rc cipher_decrypt(cipher_key key, const unsigned char* in, uint32_t inlen, const unsigned char *iv, uint32_t iv_len, unsigned char* out, uint32_t* outlen) {
cipher_rc rc = CIPHER_OK;

if (!key || !outlen || (*outlen < inlen) || !in || !out) { rc = CIPHER_INVALID_PARAMETER; goto EXIT; }

rc = encrypt_ex(in, out, inlen, NULL, 0, key);
rc = encrypt_ex(in, out, inlen, iv, 0, key);

EXIT:
return rc;
Expand Down
9 changes: 5 additions & 4 deletions lib/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,18 +192,19 @@ union u_APDU {
unsigned char p1;
unsigned char p2;
unsigned char lc;
unsigned char data[YKPIV_OBJ_MAX_SIZE - 6]; // Max message bytes - first bytes in apdu - Le
unsigned char data[YKPIV_OBJ_MAX_SIZE - 5]; // Max message bytes - first bytes in apdu - Le
} st;
unsigned char raw[YKPIV_OBJ_MAX_SIZE - 1]; // Max message size the yubikey can receive
unsigned char raw[YKPIV_OBJ_MAX_SIZE]; // Max message size the yubikey can receive
};

typedef union u_APDU APDU;
typedef struct _cipher_key *cipher_key;

cipher_rc cipher_import_key(unsigned char algo, const unsigned char *keyraw, uint32_t keyrawlen, cipher_key *key);
cipher_rc cipher_import_key_cbc(unsigned char algo, const unsigned char *keyraw, uint32_t keyrawlen, cipher_key *key);
cipher_rc cipher_destroy_key(cipher_key key);
cipher_rc cipher_encrypt(cipher_key key, const unsigned char *in, uint32_t inlen, unsigned char *out, uint32_t *outlen);
cipher_rc cipher_decrypt(cipher_key key, const unsigned char *in, uint32_t inlen, unsigned char *out, uint32_t *outlen);
cipher_rc cipher_encrypt(cipher_key key, const unsigned char *in, uint32_t inlen, const unsigned char *iv, uint32_t iv_len, unsigned char *out, uint32_t *outlen);
cipher_rc cipher_decrypt(cipher_key key, const unsigned char *in, uint32_t inlen, const unsigned char *iv, uint32_t iv_len, unsigned char *out, uint32_t *outlen);
uint32_t cipher_blocksize(cipher_key key);

pkcs5_rc pkcs5_pbkdf2_sha1(const uint8_t* password, const size_t cb_password, const uint8_t* salt, const size_t cb_salt, uint64_t iterations, const uint8_t* key, const size_t cb_key);
Expand Down
2 changes: 1 addition & 1 deletion lib/tests/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -705,7 +705,7 @@ static void test_authenticate_helper(bool full) {
crc = cipher_import_key(YKPIV_ALGO_3DES, key, key_len, &cipher);
ck_assert_int_eq(crc, CIPHER_OK);
uint32_t cipher_len = (uint32_t)data_len;
crc = cipher_encrypt(cipher, data, cipher_len, data, &cipher_len);
crc = cipher_encrypt(cipher, data, cipher_len, NULL, 0, data, &cipher_len);
data_len = cipher_len;
ck_assert_int_eq(crc, CIPHER_OK);
crc = cipher_destroy_key(cipher);
Expand Down
4 changes: 2 additions & 2 deletions lib/ykpiv.c
Original file line number Diff line number Diff line change
Expand Up @@ -1551,7 +1551,7 @@ static ykpiv_rc _ykpiv_authenticate2(ykpiv_state *state, unsigned const char *ke
*dataptr++ = 0x80;
*dataptr++ = challenge_len;
uint32_t out_len = challenge_len;
drc = cipher_decrypt(mgm_key, challenge, challenge_len, dataptr, &out_len);
drc = cipher_decrypt(mgm_key, challenge, challenge_len, NULL, 0, dataptr, &out_len);
if (drc != CIPHER_OK) {
DBG("%s: cipher_decrypt: %d", ykpiv_strerror(YKPIV_AUTHENTICATION_ERROR), drc);
res = YKPIV_AUTHENTICATION_ERROR;
Expand Down Expand Up @@ -1579,7 +1579,7 @@ static ykpiv_rc _ykpiv_authenticate2(ykpiv_state *state, unsigned const char *ke

/* compare the response from the card with our challenge */
out_len = challenge_len;
drc = cipher_encrypt(mgm_key, challenge, challenge_len, challenge, &out_len);
drc = cipher_encrypt(mgm_key, challenge, challenge_len, NULL, 0, challenge, &out_len);

if (drc != CIPHER_OK) {
DBG("%s: cipher_encrypt: %d", ykpiv_strerror(YKPIV_AUTHENTICATION_ERROR), drc);
Expand Down

0 comments on commit be6779a

Please sign in to comment.