Adding first filesystem layout.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
This commit is contained in:
@@ -18,10 +18,71 @@
|
||||
#include "files.h"
|
||||
|
||||
extern const uint8_t openpgp_aid[];
|
||||
extern const uint8_t openpgp_aid_full[];
|
||||
|
||||
#define ACL_NONE {0xff,0xff,0xff,0xff,0xff,0xff,0xff}
|
||||
#define ACL_ALL {0}
|
||||
#define ACL_RO {0xff,0xff,0xff,0xff,0xff,0xff,0x00}
|
||||
#define ACL_RW {0xff,0xff,0xff,0xff,0x00,0x00,0x00}
|
||||
#define ACL_R_WP {0xff,0xff,0xff,0xff,0x90,0x90,0x00}
|
||||
#define ACL_WP {0xff,0xff,0xff,0xff,0x90,0x90,0xff}
|
||||
|
||||
extern int parse_ch_data(const file_t *f, int mode);
|
||||
extern int parse_sec_tpl(const file_t *f, int mode);
|
||||
extern int parse_ch_cert(const file_t *f, int mode);
|
||||
extern int parse_exlen_info(const file_t *f, int mode);
|
||||
extern int parse_gfm(const file_t *f, int mode);
|
||||
extern int parse_fp(const file_t *f, int mode);
|
||||
extern int parse_cafp(const file_t *f, int mode);
|
||||
extern int parse_ts(const file_t *f, int mode);
|
||||
extern int parse_keyinfo(const file_t *f, int mode);
|
||||
extern int parse_algoinfo(const file_t *f, int mode);
|
||||
extern int parse_app_data(const file_t *f, int mode);
|
||||
extern int parse_discrete_do(const file_t *f, int mode);
|
||||
|
||||
file_t file_entries[] = {
|
||||
{ .fid = 0x0000, .parent = 0, .name = openpgp_aid, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} },
|
||||
/* 25 */ { .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_UNKNOWN, .data = NULL, .ef_structure = 0, .acl = {0} } //end
|
||||
/* 0 */ { .fid = 0x3f00, .parent = 0xff, .name = NULL, .type = FILE_TYPE_DF, .data = NULL, .ef_structure = 0, .acl = ACL_NONE }, // MF
|
||||
/* 1 */ { .fid = EF_FULL_AID, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = (uint8_t *)openpgp_aid_full, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
|
||||
/* 2 */ { .fid = EF_CH_NAME, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
|
||||
/* 3 */ { .fid = EF_LOGIN_DATA, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
|
||||
/* 4 */ { .fid = EF_LANG_PREF, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
|
||||
/* 5 */ { .fid = EF_SEX, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
|
||||
/* 6 */ { .fid = EF_URI_URL, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
|
||||
/* 7 */ { .fid = EF_HIST_BYTES, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF | FILE_DATA_FLASH, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
|
||||
/* 8 */ { .fid = EF_CH_DATA, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF | FILE_DATA_FUNC, .data = (uint8_t *)parse_ch_data, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
|
||||
/* 9 */ { .fid = EF_SEC_TPL, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF | FILE_DATA_FUNC, .data = (uint8_t *)parse_sec_tpl, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
|
||||
/* 10 */ { .fid = EF_CH_CERT, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF | FILE_DATA_FUNC, .data = (uint8_t *)parse_ch_cert, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
|
||||
/* 11 */ { .fid = EF_EXLEN_INFO, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF | FILE_DATA_FUNC, .data = (uint8_t *)parse_exlen_info, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
|
||||
/* 12 */ { .fid = EF_GFM, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF | FILE_DATA_FUNC, .data = (uint8_t *)parse_gfm, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
|
||||
/* 13 */ { .fid = EF_SIG_COUNT, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
|
||||
/* 14 */ { .fid = EF_EXT_CAP, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
|
||||
/* 15 */ { .fid = EF_ALGO_SIG, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
|
||||
/* 16 */ { .fid = EF_ALGO_DEC, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
|
||||
/* 17 */ { .fid = EF_ALGO_AUT, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
|
||||
/* 18 */ { .fid = EF_PW_STATUS, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
|
||||
/* 19 */ { .fid = EF_FP, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF | FILE_DATA_FUNC, .data = (uint8_t *)parse_fp, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
|
||||
/* 20 */ { .fid = EF_FP_SIG, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
|
||||
/* 21 */ { .fid = EF_FP_DEC, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
|
||||
/* 22 */ { .fid = EF_FP_AUT, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
|
||||
/* 23 */ { .fid = EF_CA_FP, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF | FILE_DATA_FUNC, .data = (uint8_t *)parse_cafp, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
|
||||
/* 24 */ { .fid = EF_FP_CA1, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
|
||||
/* 25 */ { .fid = EF_FP_CA2, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
|
||||
/* 26 */ { .fid = EF_FP_CA3, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
|
||||
/* 27 */ { .fid = EF_TS_ALL, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF | FILE_DATA_FUNC, .data = (uint8_t *)parse_ts, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
|
||||
/* 28 */ { .fid = EF_TS_SIG, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
|
||||
/* 29 */ { .fid = EF_TS_DEC, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
|
||||
/* 30 */ { .fid = EF_TS_AUT, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
|
||||
/* 31 */ { .fid = EF_RESET_CODE, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_WP },
|
||||
/* 32 */ { .fid = EF_UIF_SIG, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
|
||||
/* 33 */ { .fid = EF_UIF_DEC, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
|
||||
/* 34 */ { .fid = EF_UIF_AUT, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_R_WP },
|
||||
/* 35 */ { .fid = EF_KEY_INFO, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF | FILE_DATA_FUNC, .data = (uint8_t *)parse_keyinfo, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
|
||||
/* 36 */ { .fid = EF_ALGO_INFO, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF | FILE_DATA_FUNC, .data = (uint8_t *)parse_algoinfo, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
|
||||
/* 37 */ { .fid = EF_APP_DATA, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF | FILE_DATA_FUNC, .data = (uint8_t *)parse_app_data, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
|
||||
/* 38 */ { .fid = EF_DISCRETE_DO, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF | FILE_DATA_FUNC, .data = (uint8_t *)parse_discrete_do, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
|
||||
|
||||
/* 39 */ { .fid = 0x0000, .parent = 0, .name = openpgp_aid, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = ACL_RO },
|
||||
/* 40 */ { .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_UNKNOWN, .data = NULL, .ef_structure = 0, .acl = ACL_NONE } //end
|
||||
};
|
||||
|
||||
const file_t *MF = &file_entries[0];
|
||||
|
||||
@@ -21,4 +21,44 @@
|
||||
|
||||
#include "file.h"
|
||||
|
||||
#define EF_EXT_HEADER 0x004d //C
|
||||
#define EF_FULL_AID 0x004f //S
|
||||
#define EF_CH_NAME 0x005b //S
|
||||
#define EF_LOGIN_DATA 0x005e //S
|
||||
#define EF_CH_DATA 0x0065 //C
|
||||
#define EF_APP_DATA 0x006e //C
|
||||
#define EF_DISCRETE_DO 0x0073 //C
|
||||
#define EF_SEC_TPL 0x007a //C
|
||||
#define EF_SIG_COUNT 0x0093 //S
|
||||
#define EF_EXT_CAP 0x00c0 //S
|
||||
#define EF_ALGO_SIG 0x00c1 //S
|
||||
#define EF_ALGO_DEC 0x00c2 //S
|
||||
#define EF_ALGO_AUT 0x00c3 //S
|
||||
#define EF_PW_STATUS 0x00c4 //S
|
||||
#define EF_FP 0x00c5 //S
|
||||
#define EF_CA_FP 0x00c6 //S
|
||||
#define EF_FP_SIG 0x00c7 //S
|
||||
#define EF_FP_DEC 0x00c8 //S
|
||||
#define EF_FP_AUT 0x00c9 //S
|
||||
#define EF_FP_CA1 0x00ca //S
|
||||
#define EF_FP_CA2 0x00cb //S
|
||||
#define EF_FP_CA3 0x00cc //S
|
||||
#define EF_TS_ALL 0x00cd //S
|
||||
#define EF_TS_SIG 0x00ce //S
|
||||
#define EF_TS_DEC 0x00cf //S
|
||||
#define EF_TS_AUT 0x00d0 //S
|
||||
#define EF_RESET_CODE 0x00d3 //S
|
||||
#define EF_UIF_SIG 0x00d6 //S
|
||||
#define EF_UIF_DEC 0x00d7 //S
|
||||
#define EF_UIF_AUT 0x00d8 //S
|
||||
#define EF_KEY_INFO 0x00de //S
|
||||
#define EF_ALGO_INFO 0x00fa //C
|
||||
#define EF_LANG_PREF 0x5f2d //S
|
||||
#define EF_SEX 0x5f35 //S
|
||||
#define EF_URI_URL 0x5f50 //S
|
||||
#define EF_HIST_BYTES 0x5f52 //S
|
||||
#define EF_CH_CERT 0x7f21 //C
|
||||
#define EF_EXLEN_INFO 0x7f66 //C
|
||||
#define EF_GFM 0x7f74 //C
|
||||
|
||||
#endif
|
||||
|
||||
@@ -17,11 +17,18 @@
|
||||
|
||||
#include "openpgp.h"
|
||||
#include "version.h"
|
||||
#include "files.h"
|
||||
#include "eac.h"
|
||||
|
||||
const uint8_t openpgp_aid[] = {
|
||||
uint8_t openpgp_aid[] = {
|
||||
6,
|
||||
0xD2,0x76,0x00,0x01,0x24,0x01
|
||||
0xD2,0x76,0x00,0x01,0x24,0x01,
|
||||
};
|
||||
|
||||
uint8_t openpgp_aid_full[] = {
|
||||
16,00,
|
||||
0xD2,0x76,0x00,0x01,0x24,0x01,
|
||||
OPGP_VERSION_MAJOR,OPGP_VERSION_MINOR,0xff,0xfe,0xff,0xff,0xff,0xff,0x00,0x00
|
||||
};
|
||||
|
||||
char atr_openpgp[] = {
|
||||
@@ -121,8 +128,17 @@ static int cmd_select() {
|
||||
return SW_OK ();
|
||||
}
|
||||
|
||||
void scan_files() {
|
||||
file_t *ef;
|
||||
if ((ef = search_by_fid(EF_FULL_AID, NULL, SPECIFY_ANY))) {
|
||||
ef->data = openpgp_aid_full;
|
||||
pico_get_unique_board_id_string(ef->data+12, 4);
|
||||
}
|
||||
}
|
||||
|
||||
void init_openpgp() {
|
||||
isUserAuthenticated = false;
|
||||
scan_files();
|
||||
cmd_select();
|
||||
}
|
||||
|
||||
@@ -132,11 +148,7 @@ int openpgp_unload() {
|
||||
}
|
||||
|
||||
app_t *openpgp_select_aid(app_t *a) {
|
||||
printf("AIDS \r\n");
|
||||
DEBUG_PAYLOAD(apdu.cmd_apdu_data,apdu.cmd_apdu_data_len);
|
||||
DEBUG_PAYLOAD(openpgp_aid+1,openpgp_aid[0]);
|
||||
if (!memcmp(apdu.cmd_apdu_data, openpgp_aid+1, MIN(apdu.cmd_apdu_data_len,openpgp_aid[0]))) {
|
||||
printf("SELECTING OPENPGP\r\n");
|
||||
if (!memcmp(apdu.cmd_apdu_data, openpgp_aid+1, openpgp_aid[0])) {
|
||||
a->aid = openpgp_aid;
|
||||
a->process_apdu = openpgp_process_apdu;
|
||||
a->unload = openpgp_unload;
|
||||
@@ -151,13 +163,147 @@ void __attribute__ ((constructor)) openpgp_ctor() {
|
||||
register_app(openpgp_select_aid);
|
||||
}
|
||||
|
||||
int parse_do(uint16_t *fids, int mode) {
|
||||
int len = 0;
|
||||
file_t *ef;
|
||||
for (int i = 0; i < fids[0]; i++) {
|
||||
if ((ef = search_by_fid(fids[i+1], NULL, SPECIFY_EF)) && ef->data) {
|
||||
uint16_t data_len;
|
||||
if ((ef->type & FILE_DATA_FUNC) == FILE_DATA_FUNC) {
|
||||
data_len = ((int (*)(const file_t *, int))(ef->data))((const file_t *)ef, 1);
|
||||
}
|
||||
else {
|
||||
data_len = file_read_uint16(ef->data);
|
||||
if (mode == 1) {
|
||||
if (fids[0] > 1) {
|
||||
if (fids[i+1] < 0x0100) {
|
||||
res_APDU[res_APDU_size++] = fids[i+1] & 0xff;
|
||||
}
|
||||
else {
|
||||
res_APDU[res_APDU_size++] = fids[i+1] >> 8;
|
||||
res_APDU[res_APDU_size++] = fids[i+1] & 0xff;
|
||||
}
|
||||
res_APDU_size += format_tlv_len(data_len, res_APDU);
|
||||
}
|
||||
memcpy(res_APDU+res_APDU_size, file_read(ef->data+2), data_len);
|
||||
res_APDU_size += data_len;
|
||||
}
|
||||
}
|
||||
len += data_len;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
int parse_trium(uint16_t fid, uint8_t num, size_t size) {
|
||||
for (uint8_t i = 0; i < num; i++) {
|
||||
file_t *ef;
|
||||
if ((ef = search_by_fid(fid+i, NULL, SPECIFY_EF)) && ef->data) {
|
||||
uint16_t data_len = file_read_uint16(ef->data);
|
||||
memcpy(res_APDU+res_APDU_size, file_read(ef->data+2), data_len);
|
||||
res_APDU_size += data_len;
|
||||
}
|
||||
else {
|
||||
memset(res_APDU+res_APDU_size, 0, size);
|
||||
res_APDU_size += size;
|
||||
}
|
||||
}
|
||||
return num*size;
|
||||
}
|
||||
|
||||
int parse_ch_data(const file_t *f, int mode) {
|
||||
uint16_t fids[] = {
|
||||
3,
|
||||
EF_CH_NAME, EF_LANG_PREF, EF_SEX
|
||||
};
|
||||
return parse_do(fids, mode);
|
||||
}
|
||||
|
||||
int parse_sec_tpl(const file_t *f, int mode) {
|
||||
|
||||
}
|
||||
|
||||
int parse_ch_cert(const file_t *f, int mode) {
|
||||
|
||||
}
|
||||
|
||||
int parse_exlen_info(const file_t *f, int mode) {
|
||||
|
||||
}
|
||||
|
||||
int parse_gfm(const file_t *f, int mode) {
|
||||
|
||||
}
|
||||
|
||||
int parse_fp(const file_t *f, int mode) {
|
||||
return parse_trium(EF_FP_SIG, 3, 20);
|
||||
}
|
||||
|
||||
int parse_cafp(const file_t *f, int mode) {
|
||||
return parse_trium(EF_FP_CA1, 3, 20);
|
||||
}
|
||||
|
||||
int parse_ts(const file_t *f, int mode) {
|
||||
return parse_trium(EF_TS_SIG, 3, 4);
|
||||
}
|
||||
|
||||
int parse_keyinfo(const file_t *f, int mode) {
|
||||
|
||||
}
|
||||
|
||||
int parse_algoinfo(const file_t *f, int mode) {
|
||||
|
||||
}
|
||||
|
||||
int parse_app_data(const file_t *f, int mode) {
|
||||
uint16_t fids[] = {
|
||||
5,
|
||||
EF_FULL_AID, EF_HIST_BYTES, EF_EXLEN_INFO, EF_GFM, EF_DISCRETE_DO
|
||||
};
|
||||
return parse_do(fids, mode);
|
||||
}
|
||||
|
||||
int parse_discrete_do(const file_t *f, int mode) {
|
||||
uint16_t fids[] = {
|
||||
11,
|
||||
EF_EXT_CAP, EF_ALGO_SIG, EF_ALGO_DEC, EF_ALGO_AUT, EF_PW_STATUS, EF_FP, EF_CA_FP, EF_TS_ALL, EF_UIF_SIG, EF_UIF_DEC, EF_UIF_AUT
|
||||
};
|
||||
return parse_do(fids, mode);
|
||||
}
|
||||
|
||||
|
||||
static int cmd_get_data() {
|
||||
if (apdu.cmd_apdu_data_len > 0)
|
||||
return SW_WRONG_LENGTH();
|
||||
uint16_t fid = (P1(apdu) << 8) | P2(apdu);
|
||||
file_t *ef;
|
||||
if (!(ef = search_by_fid(fid, NULL, SPECIFY_EF)))
|
||||
return SW_FILE_NOT_FOUND();
|
||||
if (!authenticate_action(ef, ACL_OP_READ_SEARCH)) {
|
||||
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
||||
}
|
||||
if (ef->data) {
|
||||
uint16_t fids[] = {1,fid};
|
||||
uint16_t data_len = parse_do(fids, 1);
|
||||
if (apdu.expected_res_size > data_len)
|
||||
apdu.expected_res_size = data_len;
|
||||
}
|
||||
|
||||
return SW_OK();
|
||||
}
|
||||
|
||||
typedef struct cmd
|
||||
{
|
||||
uint8_t ins;
|
||||
int (*cmd_handler)();
|
||||
} cmd_t;
|
||||
|
||||
#define INS_SELECT 0xA4
|
||||
#define INS_GET_DATA 0xCA
|
||||
|
||||
static const cmd_t cmds[] = {
|
||||
{ INS_GET_DATA, cmd_get_data },
|
||||
{ INS_SELECT, cmd_select },
|
||||
{ 0x00, 0x0}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user