Add AES support for PIV General Authenticate

This adds algorithm IDs 0xA, 0xA, 0xC which as documented
by the NIST PIV specification is algorithms AES-128, AES-192
and AES-256 respectively.

This patch also addresses some of the hardcodes that prevented
nonces greater than the single byte TLV length tags would allow.
It was explicitly tested with AES-256 and 256 byte nonces.

Signed-off-by: William Roberts <w2.roberts@samsung.com>
This commit is contained in:
William Roberts 2014-07-08 13:52:48 -07:00
parent 5279bfa2d1
commit 295c523e4e
3 changed files with 296 additions and 93 deletions

View File

@ -1367,21 +1367,58 @@ static int piv_write_binary(sc_card_t *card, unsigned int idx,
} }
/* /*
* Card initialization is not standard. * Card initialization is NOT standard.
* Some cards use mutual or external authentication using s 3des key. We * Some cards use mutual or external authentication usings 3des or aes key. We
* will read in the key from a file. * will read in the key from a file either binary or hex encided.
* This is only needed during initialization/personalization of the card * This is only needed during initialization/personalization of the card
*/ */
static int piv_get_3des_key(sc_card_t *card, u8 *key) #ifdef ENABLE_OPENSSL
static const EVP_CIPHER *get_cipher_for_algo(int alg_id)
{
switch (alg_id) {
case 0x0: return EVP_des_ede3_ecb();
case 0x1: return EVP_des_ede3_ecb(); /* 2TDES */
case 0x3: return EVP_des_ede3_ecb();
case 0x8: return EVP_aes_128_ecb();
case 0xA: return EVP_aes_192_ecb();
case 0xC: return EVP_aes_256_ecb();
default: return NULL;
}
}
#endif
static int get_keylen(unsigned int alg_id, size_t *size)
{
switch(alg_id) {
case 0x01: *size = 192/8; /* 2TDES still has 3 single des keys phase out by 12/31/2010 */
break;
case 0x00:
case 0x03: *size = 192/8;
break;
case 0x08: *size = 128/8;
break;
case 0x0A: *size = 192/8;
break;
case 0x0C: *size = 256/8;
break;
default:
return SC_ERROR_INVALID_ARGUMENTS;
}
return SC_SUCCESS;
}
static int piv_get_key(sc_card_t *card, unsigned int alg_id, u8 **key, size_t *len)
{ {
int r; int r;
int f = -1; size_t fsize;
char keybuf[24*3]; /* 3des key as three sets of xx:xx:xx:xx:xx:xx:xx:xx FILE *f = NULL;
* with a : between which is 71 bytes */
char * keyfilename = NULL; char * keyfilename = NULL;
size_t outlen; size_t expected_keylen;
size_t keylen;
u8 * keybuf = NULL;
u8 * tkey = NULL;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
@ -1389,38 +1426,82 @@ static int piv_get_3des_key(sc_card_t *card, u8 *key)
if (keyfilename == NULL) { if (keyfilename == NULL) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
"Unable to get PIV_EXT_AUTH_KEY=filename for general_external_authenticate\n"); "Unable to get PIV_EXT_AUTH_KEY=(null) for general_external_authenticate\n");
r = SC_ERROR_FILE_NOT_FOUND; r = SC_ERROR_FILE_NOT_FOUND;
goto err; goto err;
} }
if ((f = open(keyfilename, O_RDONLY)) < 0) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL," Unable to load 3des key for general_external_authenticate\n"); r = get_keylen(alg_id, &expected_keylen);
if(r) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Invalid cipher selector, none found for: %02x\n", alg_id);
r = SC_ERROR_INVALID_ARGUMENTS;
goto err;
}
f = fopen(keyfilename, "rb");
if (!f) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL," Unable to load key from file\n");
r = SC_ERROR_FILE_NOT_FOUND; r = SC_ERROR_FILE_NOT_FOUND;
goto err; goto err;
} }
if (read(f, keybuf, 71) != 71) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL," Unable to read 3des key for general_external_authenticate\n"); fseek(f, 0L, SEEK_END);
fsize = ftell(f);
fseek(f, 0L, SEEK_SET);
keybuf = malloc(fsize+1); /* if not binary, need null to make it a string */
if (!keybuf) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL," Unable to allocate key memory");
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
keybuf[fsize] = 0x00; /* incase it is text need null */
if (fread(keybuf, 1, fsize, f) != fsize) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL," Unable to read key\n");
r = SC_ERROR_WRONG_LENGTH; r = SC_ERROR_WRONG_LENGTH;
goto err; goto err;
} }
keybuf[23] = '\0';
keybuf[47] = '\0'; tkey = malloc(expected_keylen);
keybuf[71] = '\0'; if (!tkey) {
outlen = 8; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL," Unable to allocate key memory");
r = sc_hex_to_bin(keybuf, key, &outlen); r = SC_ERROR_OUT_OF_MEMORY;
if (r) goto err; goto err;
outlen = 8; }
r = sc_hex_to_bin(keybuf+24, key+8, &outlen);
if (r) goto err; if (fsize == expected_keylen) { /* it must be binary */
outlen = 8; memcpy(tkey, keybuf, expected_keylen);
r = sc_hex_to_bin(keybuf+48, key+16, &outlen); } else {
if (r) goto err; /* if the key-length is larger then binary length, we assume hex encoded */
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Treating key as hex-encoded!\n");
sc_right_trim(keybuf, fsize);
keylen = expected_keylen;
r = sc_hex_to_bin((char *)keybuf, tkey, &keylen);
if (keylen !=expected_keylen || r != 0 ) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Error formatting key\n");
if (r == 0)
r = SC_ERROR_INCOMPATIBLE_KEY;
goto err;
}
}
*key = tkey;
tkey = NULL;
*len = expected_keylen;
r = SC_SUCCESS;
err: err:
if (f >=0) if (f)
close(f); fclose(f);
if (keybuf) {
free(keybuf);
}
if (tkey) {
free(tkey);
}
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
return r;
} }
/* /*
@ -1440,26 +1521,28 @@ static int piv_general_mutual_authenticate(sc_card_t *card,
u8 *rbuf = NULL; u8 *rbuf = NULL;
size_t rbuflen; size_t rbuflen;
u8 nonce[8] = {0xDE, 0xE0, 0xDE, 0xE1, 0xDE, 0xE2, 0xDE, 0xE3}; u8 nonce[8] = {0xDE, 0xE0, 0xDE, 0xE1, 0xDE, 0xE2, 0xDE, 0xE3};
u8 sbuf[255], key[24]; u8 sbuf[255];
u8 *p, *q; u8 *p, *q;
u8 *key = NULL;
size_t keylen;
EVP_CIPHER_CTX ctx; EVP_CIPHER_CTX ctx;
const EVP_CIPHER *cipher; const EVP_CIPHER *cipher;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
EVP_CIPHER_CTX_init(&ctx); EVP_CIPHER_CTX_init(&ctx);
cipher = get_cipher_for_algo(alg_id);
switch (alg_id) { if(!cipher) {
case 1: cipher=EVP_des_ede3_ecb(); break; sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Invalid cipher selector, none found for: %02x\n", alg_id);
case 2: cipher=EVP_des_ede3_cbc(); break; r = SC_ERROR_INVALID_ARGUMENTS;
case 3: cipher=EVP_des_ede3_ecb(); break; goto err;
case 4: cipher=EVP_des_ede3_cbc(); break;
default: cipher=EVP_des_ede3_ecb(); break;
} }
r = piv_get_3des_key(card, key); r = piv_get_key(card, alg_id, &key, &keylen);
if (r != SC_SUCCESS) if (r) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Error geting General Auth key\n");
goto err; goto err;
}
r = sc_lock(card); r = sc_lock(card);
if (r != SC_SUCCESS) if (r != SC_SUCCESS)
@ -1578,19 +1661,30 @@ err:
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
} }
/* Currently only used for card administration */ /* Currently only used for card administration */
static int piv_general_external_authenticate(sc_card_t *card, static int piv_general_external_authenticate(sc_card_t *card,
unsigned int key_ref, unsigned int alg_id) unsigned int key_ref, unsigned int alg_id)
{ {
int r; int r;
#ifdef ENABLE_OPENSSL #ifdef ENABLE_OPENSSL
int outl, outl2; int tmplen;
int N; int outlen;
int locked = 0; int locked = 0;
u8 *rbuf = NULL; u8 *p;
u8 *rbuf = NULL;
u8 *key = NULL;
u8 *cypher_text = NULL;
u8 *output_buf = NULL;
const u8 *body = NULL;
const u8 *challenge_data = NULL;
size_t rbuflen; size_t rbuflen;
u8 sbuf[255], key[24]; size_t body_len;
u8 *p, *q; size_t output_len;
size_t challenge_len;
size_t keylen = 0;
size_t cypher_text_len = 0;
u8 sbuf[255];
EVP_CIPHER_CTX ctx; EVP_CIPHER_CTX ctx;
const EVP_CIPHER *cipher; const EVP_CIPHER *cipher;
@ -1598,17 +1692,20 @@ static int piv_general_external_authenticate(sc_card_t *card,
EVP_CIPHER_CTX_init(&ctx); EVP_CIPHER_CTX_init(&ctx);
switch (alg_id) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Selected cipher for algorithm id: %02x\n", alg_id);
case 1: cipher=EVP_des_ede3_ecb(); break;
case 2: cipher=EVP_des_ede3_cbc(); break; cipher = get_cipher_for_algo(alg_id);
case 3: cipher=EVP_des_ede3_ecb(); break; if(!cipher) {
case 4: cipher=EVP_des_ede3_cbc(); break; sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Invalid cipher selector, none found for: %02x\n", alg_id);
default: cipher=EVP_des_ede3_ecb(); break; r = SC_ERROR_INVALID_ARGUMENTS;
goto err;
} }
r = piv_get_3des_key(card, key); r = piv_get_key(card, alg_id, &key, &keylen);
if (r != SC_SUCCESS) if (r) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Error geting General Auth key\n");
goto err; goto err;
}
r = sc_lock(card); r = sc_lock(card);
if (r != SC_SUCCESS) if (r != SC_SUCCESS)
@ -1623,53 +1720,140 @@ static int piv_general_external_authenticate(sc_card_t *card,
/* get a challenge */ /* get a challenge */
r = piv_general_io(card, 0x87, alg_id, key_ref, sbuf, p - sbuf, &rbuf, &rbuflen); r = piv_general_io(card, 0x87, alg_id, key_ref, sbuf, p - sbuf, &rbuf, &rbuflen);
if (r < 0) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Error getting Challenge\n");
goto err;
}
if (r < 0) goto err; /*
q = rbuf; * the value here corresponds with the response size, so we use this
if ( (*q++ != 0x7C) * to alloc the response buffer, rather than re-computing it.
|| (*q++ != rbuflen - 2) */
|| (*q++ != 0x81) output_len = r;
|| (*q++ != rbuflen - 4)) {
/* Remove the encompassing outer TLV of 0x7C and get the data */
body = sc_asn1_find_tag(card->ctx, rbuf,
r, 0x7C, &body_len);
if (!body) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Invalid Challenge Data response of NULL\n");
r = SC_ERROR_INVALID_DATA; r = SC_ERROR_INVALID_DATA;
goto err; goto err;
} }
/* assuming challenge and response are same size i.e. des3 */ /* Get the challenge data indicated by the TAG 0x81 */
p = sbuf; challenge_data = sc_asn1_find_tag(card->ctx, body,
*p++ = 0x7c; body_len, 0x81, &challenge_len);
*p++ = *(rbuf + 1); if (!challenge_data) {
*p++ = 0x82; sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Invalid Challenge Data none found in TLV\n");
*p++ = *(rbuf + 3); r = SC_ERROR_INVALID_DATA;
N = *(rbuf + 3); /* assuming 2 * N + 6 < 128 */ goto err;
}
/* Store this to sanity check that plaintext length and cyphertext lengths match */
/* TODO is this required */
tmplen = challenge_len;
/* Encrypt the challenge with the secret */
if (!EVP_EncryptInit(&ctx, cipher, key, NULL)) { if (!EVP_EncryptInit(&ctx, cipher, key, NULL)) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Encrypt fail\n");
r = SC_ERROR_INTERNAL; r = SC_ERROR_INTERNAL;
goto err; goto err;
} }
EVP_CIPHER_CTX_set_padding(&ctx,0);
if (!EVP_EncryptUpdate(&ctx, p, &outl, q, N)) {
r = SC_ERROR_INTERNAL;
goto err;
}
if(!EVP_EncryptFinal(&ctx, p+outl, &outl2)) {
r = SC_ERROR_INTERNAL;
goto err;
}
if (outl+outl2 != N) {
r = SC_ERROR_INTERNAL;
goto err;
}
p += N;
r = piv_general_io(card, 0x87, alg_id, key_ref, sbuf, p - sbuf, NULL, NULL); cypher_text = malloc(challenge_len);
if (!cypher_text) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not allocate buffer for cipher text\n");
r = SC_ERROR_INTERNAL;
goto err;
}
EVP_CIPHER_CTX_set_padding(&ctx,0);
if (!EVP_EncryptUpdate(&ctx, cypher_text, &outlen, challenge_data, challenge_len)) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Encrypt update fail\n");
r = SC_ERROR_INTERNAL;
goto err;
}
cypher_text_len += outlen;
if (!EVP_EncryptFinal(&ctx, cypher_text + cypher_text_len, &outlen)) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Final fail\n");
r = SC_ERROR_INTERNAL;
goto err;
}
cypher_text_len += outlen;
/*
* Actually perform the sanity check on lengths plaintext length vs
* encrypted length
*/
if (cypher_text_len != (size_t)tmplen) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Length test fail\n");
r = SC_ERROR_INTERNAL;
goto err;
}
output_buf = malloc(output_len);
if(!output_buf) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not allocate output buffer: %s\n",
strerror(errno));
r = SC_ERROR_INTERNAL;
goto err;
}
p = output_buf;
/*
* Build: 7C<len>[82<len><challenge>]
* Start off by capturing the data of the response:
* - 82<len><encrypted challenege response>
* Build the outside TLV (7C)
* Advance past that tag + len
* Build the body (82)
* memcopy the body past the 7C<len> portion
* Transmit
*/
tmplen = put_tag_and_len(0x82, cypher_text_len, NULL);
tmplen = put_tag_and_len(0x7C, tmplen, &p);
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Tmplen2: %zd\n", tmplen);
/* Build the 0x82 TLV and append to the 7C<len> tag */
tmplen += put_tag_and_len(0x82, cypher_text_len, &p);
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Tmplen1: %zd\n", tmplen);
memcpy(p, cypher_text, cypher_text_len);
p += cypher_text_len;
tmplen += cypher_text_len;
/* Sanity check the lengths again */
if(output_len != (size_t)tmplen) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Allocated and computed lengths do not match! "
"Expected %zd, found: %d\n", output_len, tmplen);
r = SC_ERROR_INTERNAL;
goto err;
}
r = piv_general_io(card, 0x87, alg_id, key_ref, output_buf, output_len, NULL, NULL);
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Got response challenge\n");
err: err:
EVP_CIPHER_CTX_cleanup(&ctx);
if (locked) if (locked)
sc_unlock(card); sc_unlock(card);
EVP_CIPHER_CTX_cleanup(&ctx);
sc_mem_clear(key, sizeof(key)); if (key) {
sc_mem_clear(key, keylen);
free(key);
}
if (rbuf) if (rbuf)
free(rbuf); free(rbuf);
if (cypher_text)
free(cypher_text);
if (output_buf)
free(output_buf);
#else #else
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"OpenSSL Required"); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"OpenSSL Required");
r = SC_ERROR_NOT_SUPPORTED; r = SC_ERROR_NOT_SUPPORTED;

View File

@ -354,7 +354,7 @@ struct sc_pin_cmd_data {
#define PACE_PIN_ID_CAN 0x02 #define PACE_PIN_ID_CAN 0x02
#define PACE_PIN_ID_PIN 0x03 #define PACE_PIN_ID_PIN 0x03
#define PACE_PIN_ID_PUK 0x04 #define PACE_PIN_ID_PUK 0x04
/** /**
* Input data for EstablishPACEChannel() * Input data for EstablishPACEChannel()
*/ */
struct establish_pace_channel_input { struct establish_pace_channel_input {
@ -377,7 +377,7 @@ struct establish_pace_channel_input {
const unsigned char *certificate_description; const unsigned char *certificate_description;
}; };
/** /**
* Output data for EstablishPACEChannel() * Output data for EstablishPACEChannel()
*/ */
struct establish_pace_channel_output { struct establish_pace_channel_output {
@ -821,12 +821,12 @@ sc_reader_t *sc_ctx_get_reader(sc_context_t *ctx, unsigned int i);
* *
* @param ctx pointer to a sc_context_t * @param ctx pointer to a sc_context_t
* @param pcsc_context_handle pointer to the new context_handle to use * @param pcsc_context_handle pointer to the new context_handle to use
* @param pcsc_card_handle pointer to the new card_handle to use * @param pcsc_card_handle pointer to the new card_handle to use
* @return SC_SUCCESS on success and an error code otherwise. * @return SC_SUCCESS on success and an error code otherwise.
*/ */
int sc_ctx_use_reader(sc_context_t *ctx, void * pcsc_context_handle, void * pcsc_card_handle); int sc_ctx_use_reader(sc_context_t *ctx, void * pcsc_context_handle, void * pcsc_card_handle);
/** /**
* Returns a pointer to the specified sc_reader_t object * Returns a pointer to the specified sc_reader_t object
* @param ctx OpenSC context * @param ctx OpenSC context
* @param name name of the reader to look for * @param name name of the reader to look for
@ -835,7 +835,7 @@ int sc_ctx_use_reader(sc_context_t *ctx, void * pcsc_context_handle, void * pcsc
*/ */
sc_reader_t *sc_ctx_get_reader_by_name(sc_context_t *ctx, const char *name); sc_reader_t *sc_ctx_get_reader_by_name(sc_context_t *ctx, const char *name);
/** /**
* Returns a pointer to the specified sc_reader_t object * Returns a pointer to the specified sc_reader_t object
* @param ctx OpenSC context * @param ctx OpenSC context
* @param id id of the reader (starting from 0) * @param id id of the reader (starting from 0)
@ -911,7 +911,7 @@ int sc_detect_card_presence(sc_reader_t *reader);
* @retval = 1 if the timeout occured * @retval = 1 if the timeout occured
*/ */
int sc_wait_for_event(sc_context_t *ctx, unsigned int event_mask, int sc_wait_for_event(sc_context_t *ctx, unsigned int event_mask,
sc_reader_t **event_reader, unsigned int *event, sc_reader_t **event_reader, unsigned int *event,
int timeout, void **reader_states); int timeout, void **reader_states);
/** /**
@ -980,8 +980,8 @@ int sc_read_binary(struct sc_card *card, unsigned int idx, u8 * buf,
size_t count, unsigned long flags); size_t count, unsigned long flags);
/** /**
* Write data to a binary EF * Write data to a binary EF
* @param card struct sc_card object on which to issue the command * @param card struct sc_card object on which to issue the command
* @param idx index within the file for the data to be written * @param idx index within the file for the data to be written
* @param buf buffer with the data * @param buf buffer with the data
* @param count number of bytes to write * @param count number of bytes to write
* @param flags flags for the WRITE BINARY command (currently not used) * @param flags flags for the WRITE BINARY command (currently not used)
@ -1151,8 +1151,8 @@ int sc_file_set_content(sc_file_t *file, const u8 *content,
* Sets the content of a sc_path_t object. * Sets the content of a sc_path_t object.
* @param path sc_path_t object to set * @param path sc_path_t object to set
* @param type type of path * @param type type of path
* @param id value of the path * @param id value of the path
* @param id_len length of the path value * @param id_len length of the path value
* @param index index within the file * @param index index within the file
* @param count number of bytes * @param count number of bytes
* @return SC_SUCCESS on success and an error code otherwise * @return SC_SUCCESS on success and an error code otherwise
@ -1163,7 +1163,7 @@ int sc_path_set(sc_path_t *path, int type, const u8 *id, size_t id_len,
void sc_format_path(const char *path_in, sc_path_t *path_out); void sc_format_path(const char *path_in, sc_path_t *path_out);
/** /**
* Return string representation of the given sc_path_t object * Return string representation of the given sc_path_t object
* Warning: as static memory is used for the return value * Warning: as static memory is used for the return value
* this function is not thread-safe !!! * this function is not thread-safe !!!
* @param path sc_path_t object of the path to be printed * @param path sc_path_t object of the path to be printed
* @return pointer to a const buffer with the string representation * @return pointer to a const buffer with the string representation
@ -1179,7 +1179,7 @@ const char *sc_print_path(const sc_path_t *path);
*/ */
int sc_path_print(char *buf, size_t buflen, const sc_path_t *path); int sc_path_print(char *buf, size_t buflen, const sc_path_t *path);
/** /**
* Compares two sc_path_t objects * Compares two sc_path_t objects
* @param patha sc_path_t object of the first path * @param patha sc_path_t object of the first path
* @param pathb sc_path_t object of the second path * @param pathb sc_path_t object of the second path
* @return 1 if both paths are equal and 0 otherwise * @return 1 if both paths are equal and 0 otherwise
@ -1203,7 +1203,7 @@ int sc_concatenate_path(sc_path_t *d, const sc_path_t *p1, const sc_path_t *p2);
*/ */
int sc_append_path(sc_path_t *dest, const sc_path_t *src); int sc_append_path(sc_path_t *dest, const sc_path_t *src);
/** /**
* Checks whether one path is a prefix of another path * Checks whether one path is a prefix of another path
* @param prefix sc_path_t object with the prefix * @param prefix sc_path_t object with the prefix
* @param path sc_path_t object with the path which should start * @param path sc_path_t object with the path which should start
* with the given prefix * with the given prefix
@ -1224,6 +1224,7 @@ const sc_path_t *sc_get_mf_path(void);
int sc_hex_to_bin(const char *in, u8 *out, size_t *outlen); int sc_hex_to_bin(const char *in, u8 *out, size_t *outlen);
int sc_bin_to_hex(const u8 *, size_t, char *, size_t, int separator); int sc_bin_to_hex(const u8 *, size_t, char *, size_t, int separator);
size_t sc_right_trim(u8 *buf, size_t len);
scconf_block *sc_get_conf_block(sc_context_t *ctx, const char *name1, const char *name2, int priority); scconf_block *sc_get_conf_block(sc_context_t *ctx, const char *name1, const char *name2, int priority);
/** /**

View File

@ -111,6 +111,24 @@ int sc_bin_to_hex(const u8 *in, size_t in_len, char *out, size_t out_len,
return 0; return 0;
} }
/*
* Right trim all non-printable characters
*/
size_t sc_right_trim(u8 *buf, size_t len) {
size_t i;
for(i=len-1; i >=0; i--) {
if(!isprint(buf[i])) {
buf[i] = '\0';
len--;
continue;
}
break;
}
return len;
}
u8 *ulong2bebytes(u8 *buf, unsigned long x) u8 *ulong2bebytes(u8 *buf, unsigned long x)
{ {
if (buf != NULL) { if (buf != NULL) {