mirror of
https://github.com/polhenarejos/pico-hsm.git
synced 2026-01-17 17:28:06 +00:00
In order to announce the public key, the response must be cvc request authenticated.
Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
This commit is contained in:
parent
1688ea540e
commit
e1126b5951
1 changed files with 175 additions and 44 deletions
219
sc_hsm.c
219
sc_hsm.c
|
|
@ -477,35 +477,39 @@ struct ec_curve_mbed_id ec_curves_mbed[] = {
|
|||
|
||||
//Stores the private and public keys in flash
|
||||
int store_key_rsa(mbedtls_rsa_context *rsa, int key_bits, uint8_t key_id, sc_context_t *ctx) {
|
||||
int key_size = key_bits/8;
|
||||
uint8_t *pq = (uint8_t *)malloc(key_size), *asn1bin;
|
||||
int key_size = key_bits/8, r;
|
||||
uint8_t *pq = (uint8_t *)calloc(1, key_size), *asn1bin;
|
||||
size_t asn1len = 0;
|
||||
file_t *fpk;
|
||||
|
||||
mbedtls_mpi_write_binary(&rsa->P, pq, key_size/2);
|
||||
mbedtls_mpi_write_binary(&rsa->Q, pq+key_size/2, key_size/2);
|
||||
file_t *fpk = file_new((KEY_PREFIX << 8) | key_id);
|
||||
int r = flash_write_data_to_file(fpk, pq, key_size);
|
||||
fpk = file_new((KEY_PREFIX << 8) | key_id);
|
||||
r = flash_write_data_to_file(fpk, pq, key_size);
|
||||
free(pq);
|
||||
if (r != HSM_OK)
|
||||
return r;
|
||||
add_file_to_chain(fpk, &ef_kf);
|
||||
|
||||
struct sc_pkcs15_object p15o;
|
||||
struct sc_pkcs15_object *p15o = (struct sc_pkcs15_object *)calloc(1,sizeof (struct sc_pkcs15_object));
|
||||
|
||||
sc_pkcs15_prkey_info_t prkd;
|
||||
memset(&prkd, 0, sizeof(sc_pkcs15_prkey_info_t));
|
||||
prkd.id.len = 1;
|
||||
prkd.id.value[0] = key_id;
|
||||
prkd.usage = SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER | SC_PKCS15_PRKEY_USAGE_UNWRAP;
|
||||
prkd.access_flags = SC_PKCS15_PRKEY_ACCESS_SENSITIVE | SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE | SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE | SC_PKCS15_PRKEY_ACCESS_LOCAL;
|
||||
prkd.native = 1;
|
||||
prkd.key_reference = key_id;
|
||||
prkd.modulus_length = key_size;
|
||||
sc_pkcs15_prkey_info_t *prkd = (sc_pkcs15_prkey_info_t *)calloc(1, sizeof (sc_pkcs15_prkey_info_t));
|
||||
memset(prkd, 0, sizeof(sc_pkcs15_prkey_info_t));
|
||||
prkd->id.len = 1;
|
||||
prkd->id.value[0] = key_id;
|
||||
prkd->usage = SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER | SC_PKCS15_PRKEY_USAGE_UNWRAP;
|
||||
prkd->access_flags = SC_PKCS15_PRKEY_ACCESS_SENSITIVE | SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE | SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE | SC_PKCS15_PRKEY_ACCESS_LOCAL;
|
||||
prkd->native = 1;
|
||||
prkd->key_reference = key_id;
|
||||
prkd->modulus_length = key_size;
|
||||
|
||||
p15o.data = &prkd;
|
||||
p15o.type = SC_PKCS15_TYPE_PRKEY_RSA;
|
||||
p15o->data = prkd;
|
||||
p15o->type = SC_PKCS15_TYPE_PRKEY_RSA;
|
||||
|
||||
r = sc_pkcs15_encode_prkdf_entry(ctx, &p15o, &asn1bin, &asn1len);
|
||||
r = sc_pkcs15_encode_prkdf_entry(ctx, p15o, &asn1bin, &asn1len);
|
||||
free(prkd);
|
||||
printf("r %d\r\n",r);
|
||||
//sc_asn1_print_tags(asn1bin, asn1len);
|
||||
fpk = file_new((PRKD_PREFIX << 8) | key_id);
|
||||
r = flash_write_data_to_file(fpk, asn1bin, asn1len);
|
||||
free(asn1bin);
|
||||
|
|
@ -513,21 +517,24 @@ int store_key_rsa(mbedtls_rsa_context *rsa, int key_bits, uint8_t key_id, sc_con
|
|||
return r;
|
||||
add_file_to_chain(fpk, &ef_prkdf);
|
||||
|
||||
sc_pkcs15_pubkey_info_t pukd;
|
||||
memset(&pukd, 0, sizeof(sc_pkcs15_pubkey_info_t));
|
||||
pukd.id.len = 1;
|
||||
pukd.id.value[0] = key_id;
|
||||
pukd.usage = SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP | SC_PKCS15_PRKEY_USAGE_VERIFY;
|
||||
pukd.access_flags = SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE;
|
||||
pukd.native = 1;
|
||||
pukd.key_reference = key_id;
|
||||
pukd.modulus_length = key_size;
|
||||
sc_pkcs15_pubkey_info_t *pukd = (sc_pkcs15_pubkey_info_t *)calloc(1, sizeof(sc_pkcs15_pubkey_info_t));
|
||||
memset(pukd, 0, sizeof(sc_pkcs15_pubkey_info_t));
|
||||
pukd->id.len = 1;
|
||||
pukd->id.value[0] = key_id;
|
||||
pukd->usage = SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP | SC_PKCS15_PRKEY_USAGE_VERIFY;
|
||||
pukd->access_flags = SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE;
|
||||
pukd->native = 1;
|
||||
pukd->key_reference = key_id;
|
||||
pukd->modulus_length = key_size;
|
||||
|
||||
p15o.data = &pukd;
|
||||
p15o.type = SC_PKCS15_TYPE_PUBKEY_RSA;
|
||||
p15o->data = pukd;
|
||||
p15o->type = SC_PKCS15_TYPE_PUBKEY_RSA;
|
||||
|
||||
r = sc_pkcs15_encode_pukdf_entry(ctx, &p15o, &asn1bin, &asn1len);
|
||||
r = sc_pkcs15_encode_pukdf_entry(ctx, p15o, &asn1bin, &asn1len);
|
||||
free(pukd);
|
||||
free(p15o);
|
||||
printf("r %d\r\n",r);
|
||||
//sc_asn1_print_tags(asn1bin, asn1len);
|
||||
fpk = file_new((CD_PREFIX << 8) | key_id);
|
||||
r = flash_write_data_to_file(fpk, asn1bin, asn1len);
|
||||
free(asn1bin);
|
||||
|
|
@ -544,21 +551,136 @@ sc_context_t *create_context() {
|
|||
sc_context_param_t ctx_opts;
|
||||
memset(&ctx_opts, 0, sizeof(sc_context_param_t));
|
||||
sc_context_create(&ctx, &ctx_opts);
|
||||
ctx->debug = 9;
|
||||
ctx->debug = 0;
|
||||
ctx->debug_file = stdout;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
#define C_ASN1_CVC_PUBKEY_SIZE 10
|
||||
static const struct sc_asn1_entry c_asn1_cvc_pubkey[C_ASN1_CVC_PUBKEY_SIZE] = {
|
||||
{ "publicKeyOID", SC_ASN1_OBJECT, SC_ASN1_UNI | SC_ASN1_OBJECT, 0, NULL, NULL },
|
||||
{ "primeOrModulus", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 1, SC_ASN1_OPTIONAL | SC_ASN1_ALLOC, NULL, NULL },
|
||||
{ "coefficientAorExponent", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 2, SC_ASN1_OPTIONAL | SC_ASN1_ALLOC, NULL, NULL },
|
||||
{ "coefficientB", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 3, SC_ASN1_OPTIONAL | SC_ASN1_ALLOC, NULL, NULL },
|
||||
{ "basePointG", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 4, SC_ASN1_OPTIONAL | SC_ASN1_ALLOC, NULL, NULL },
|
||||
{ "order", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 5, SC_ASN1_OPTIONAL | SC_ASN1_ALLOC, NULL, NULL },
|
||||
{ "publicPoint", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 6, SC_ASN1_OPTIONAL | SC_ASN1_ALLOC, NULL, NULL },
|
||||
{ "cofactor", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 7, SC_ASN1_OPTIONAL | SC_ASN1_ALLOC, NULL, NULL },
|
||||
{ "modulusSize", SC_ASN1_INTEGER, SC_ASN1_UNI | SC_ASN1_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL },
|
||||
{ NULL, 0, 0, 0, NULL, NULL }
|
||||
};
|
||||
#define C_ASN1_CVC_BODY_SIZE 5
|
||||
static const struct sc_asn1_entry c_asn1_cvc_body[C_ASN1_CVC_BODY_SIZE] = {
|
||||
{ "certificateProfileIdentifier", SC_ASN1_INTEGER, SC_ASN1_APP | 0x1F29, 0, NULL, NULL },
|
||||
{ "certificationAuthorityReference", SC_ASN1_PRINTABLESTRING, SC_ASN1_APP | 2, 0, NULL, NULL },
|
||||
{ "publicKey", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_APP | 0x1F49, 0, NULL, NULL },
|
||||
{ "certificateHolderReference", SC_ASN1_PRINTABLESTRING, SC_ASN1_APP | 0x1F20, 0, NULL, NULL },
|
||||
{ NULL, 0, 0, 0, NULL, NULL }
|
||||
};
|
||||
#define C_ASN1_CVCERT_SIZE 3
|
||||
static const struct sc_asn1_entry c_asn1_cvcert[C_ASN1_CVCERT_SIZE] = {
|
||||
{ "certificateBody", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_APP | 0x1F4E, 0, NULL, NULL },
|
||||
{ "signature", SC_ASN1_OCTET_STRING, SC_ASN1_APP | 0x1F37, SC_ASN1_ALLOC, NULL, NULL },
|
||||
{ NULL, 0, 0, 0, NULL, NULL }
|
||||
};
|
||||
#define C_ASN1_CVC_SIZE 2
|
||||
static const struct sc_asn1_entry c_asn1_cvc[C_ASN1_CVC_SIZE] = {
|
||||
{ "certificate", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_APP | 0x1F21, 0, NULL, NULL },
|
||||
{ NULL, 0, 0, 0, NULL, NULL }
|
||||
};
|
||||
#define C_ASN1_AUTHREQ_SIZE 4
|
||||
static const struct sc_asn1_entry c_asn1_authreq[C_ASN1_AUTHREQ_SIZE] = {
|
||||
{ "certificate", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_APP | 0x1F21, 0, NULL, NULL },
|
||||
{ "outerCAR", SC_ASN1_PRINTABLESTRING, SC_ASN1_APP | 2, 0, NULL, NULL },
|
||||
{ "signature", SC_ASN1_OCTET_STRING, SC_ASN1_APP | 0x1F37, SC_ASN1_ALLOC, NULL, NULL },
|
||||
{ NULL, 0, 0, 0, NULL, NULL }
|
||||
};
|
||||
#define C_ASN1_REQ_SIZE 2
|
||||
static const struct sc_asn1_entry c_asn1_req[C_ASN1_REQ_SIZE] = {
|
||||
{ "authenticatedrequest", SC_ASN1_STRUCT, SC_ASN1_CONS | SC_ASN1_APP | 7, 0, NULL, NULL },
|
||||
{ NULL, 0, 0, 0, NULL, NULL }
|
||||
};
|
||||
|
||||
int sc_pkcs15emu_sc_hsm_encode_cvc_req(sc_pkcs15_card_t * p15card, sc_cvc_t *cvc, u8 ** buf, size_t *buflen)
|
||||
{
|
||||
sc_card_t *card = p15card->card;
|
||||
struct sc_asn1_entry asn1_req[C_ASN1_REQ_SIZE];
|
||||
struct sc_asn1_entry asn1_authreq[C_ASN1_AUTHREQ_SIZE];
|
||||
struct sc_asn1_entry asn1_cvc[C_ASN1_CVC_SIZE];
|
||||
struct sc_asn1_entry asn1_cvcert[C_ASN1_CVCERT_SIZE];
|
||||
struct sc_asn1_entry asn1_cvc_body[C_ASN1_CVC_BODY_SIZE];
|
||||
struct sc_asn1_entry asn1_cvc_pubkey[C_ASN1_CVC_PUBKEY_SIZE];
|
||||
size_t lenchr;
|
||||
size_t lencar;
|
||||
size_t lenoutCar;
|
||||
int r;
|
||||
|
||||
sc_copy_asn1_entry(c_asn1_req, asn1_req);
|
||||
sc_copy_asn1_entry(c_asn1_authreq, asn1_authreq);
|
||||
sc_copy_asn1_entry(c_asn1_cvc, asn1_cvc);
|
||||
sc_copy_asn1_entry(c_asn1_cvcert, asn1_cvcert);
|
||||
sc_copy_asn1_entry(c_asn1_cvc_body, asn1_cvc_body);
|
||||
sc_copy_asn1_entry(c_asn1_cvc_pubkey, asn1_cvc_pubkey);
|
||||
|
||||
asn1_cvc_pubkey[1].flags = SC_ASN1_OPTIONAL;
|
||||
asn1_cvcert[1].flags = SC_ASN1_OPTIONAL;
|
||||
|
||||
sc_format_asn1_entry(asn1_cvc_pubkey , &cvc->pukoid, NULL, 1);
|
||||
if (cvc->primeOrModulus && (cvc->primeOrModuluslen > 0)) {
|
||||
sc_format_asn1_entry(asn1_cvc_pubkey + 1, cvc->primeOrModulus, &cvc->primeOrModuluslen, 1);
|
||||
}
|
||||
sc_format_asn1_entry(asn1_cvc_pubkey + 2, cvc->coefficientAorExponent, &cvc->coefficientAorExponentlen, 1);
|
||||
if (cvc->coefficientB && (cvc->coefficientBlen > 0)) {
|
||||
sc_format_asn1_entry(asn1_cvc_pubkey + 3, cvc->coefficientB, &cvc->coefficientBlen, 1);
|
||||
sc_format_asn1_entry(asn1_cvc_pubkey + 4, cvc->basePointG, &cvc->basePointGlen, 1);
|
||||
sc_format_asn1_entry(asn1_cvc_pubkey + 5, cvc->order, &cvc->orderlen, 1);
|
||||
if (cvc->publicPoint && (cvc->publicPointlen > 0)) {
|
||||
sc_format_asn1_entry(asn1_cvc_pubkey + 6, cvc->publicPoint, &cvc->publicPointlen, 1);
|
||||
}
|
||||
sc_format_asn1_entry(asn1_cvc_pubkey + 7, cvc->cofactor, &cvc->cofactorlen, 1);
|
||||
}
|
||||
if (cvc->modulusSize > 0) {
|
||||
sc_format_asn1_entry(asn1_cvc_pubkey + 8, &cvc->modulusSize, NULL, 1);
|
||||
}
|
||||
|
||||
sc_format_asn1_entry(asn1_cvc_body , &cvc->cpi, NULL, 1);
|
||||
lencar = strnlen(cvc->car, sizeof cvc->car);
|
||||
sc_format_asn1_entry(asn1_cvc_body + 1, &cvc->car, &lencar, 1);
|
||||
sc_format_asn1_entry(asn1_cvc_body + 2, &asn1_cvc_pubkey, NULL, 1);
|
||||
lenchr = strnlen(cvc->chr, sizeof cvc->chr);
|
||||
sc_format_asn1_entry(asn1_cvc_body + 3, &cvc->chr, &lenchr, 1);
|
||||
|
||||
sc_format_asn1_entry(asn1_cvcert , &asn1_cvc_body, NULL, 1);
|
||||
if (cvc->signature && (cvc->signatureLen > 0)) {
|
||||
sc_format_asn1_entry(asn1_cvcert + 1, cvc->signature, &cvc->signatureLen, 1);
|
||||
}
|
||||
|
||||
sc_format_asn1_entry(asn1_authreq , &asn1_cvcert, NULL, 1);
|
||||
lenoutCar = strnlen(cvc->outer_car, sizeof cvc->outer_car);
|
||||
sc_format_asn1_entry(asn1_authreq + 1, &cvc->outer_car, &lenoutCar, 1);
|
||||
if (cvc->outerSignature && (cvc->outerSignatureLen > 0)) {
|
||||
sc_format_asn1_entry(asn1_authreq + 2, cvc->outerSignature, &cvc->outerSignatureLen, 1);
|
||||
}
|
||||
|
||||
sc_format_asn1_entry(asn1_req , &asn1_authreq, NULL, 1);
|
||||
|
||||
|
||||
r = sc_asn1_encode(card->ctx, asn1_req, buf, buflen);
|
||||
LOG_TEST_RET(card->ctx, r, "Could not encode card verifiable certificate");
|
||||
|
||||
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
|
||||
}
|
||||
|
||||
static int cmd_keypair_gen() {
|
||||
uint8_t key_id = P1(apdu);
|
||||
uint8_t auth_key_id = P2(apdu);
|
||||
sc_context_t *ctx = create_context();
|
||||
struct sc_pkcs15_card p15card;
|
||||
p15card.card = (sc_card_t *)malloc(sizeof(sc_card_t));
|
||||
p15card.card = (sc_card_t *)calloc(1, sizeof(sc_card_t));
|
||||
p15card.card->ctx = ctx;
|
||||
|
||||
size_t tout = 0;
|
||||
sc_asn1_print_tags(apdu.cmd_apdu_data, apdu.cmd_apdu_data_len);
|
||||
//sc_asn1_print_tags(apdu.cmd_apdu_data, apdu.cmd_apdu_data_len);
|
||||
const uint8_t *p = sc_asn1_find_tag(ctx, (const uint8_t *)apdu.cmd_apdu_data, apdu.cmd_apdu_data_len, 0x7f49, &tout);
|
||||
if (p) {
|
||||
size_t oid_len = 0;
|
||||
|
|
@ -569,8 +691,8 @@ static int cmd_keypair_gen() {
|
|||
const uint8_t *ex = sc_asn1_find_tag(ctx, p, tout, 0x82, &ex_len);
|
||||
const uint8_t *ks = sc_asn1_find_tag(ctx, p, tout, 0x2, &ks_len);
|
||||
int exponent = 65537, key_size = 2048;
|
||||
uint8_t *cvcbin;
|
||||
size_t cvclen;
|
||||
uint8_t *cvcbin, *cvcpo;
|
||||
size_t cvclen, taglen;
|
||||
if (ex) {
|
||||
sc_asn1_decode_integer(ex, ex_len, &exponent, 0);
|
||||
}
|
||||
|
|
@ -589,38 +711,47 @@ static int cmd_keypair_gen() {
|
|||
strlcpy(cvc.car, "UTCA00001", sizeof cvc.car);
|
||||
strlcpy(cvc.chr, "ESHSMCVCA", sizeof cvc.chr);
|
||||
strlcat(cvc.chr, "00001", sizeof cvc.chr);
|
||||
strlcpy(cvc.outer_car, "ESHSM00001", sizeof(cvc.outer_car));
|
||||
struct sc_object_id rsa15withSHA256 = { { 0,4,0,127,0,7,2,2,2,1,2,-1 } };
|
||||
cvc.coefficientAorExponentlen = ex_len;
|
||||
cvc.coefficientAorExponent = malloc(ex_len);
|
||||
cvc.coefficientAorExponent = calloc(1, ex_len);
|
||||
memcpy(cvc.coefficientAorExponent, &exponent, ex_len);
|
||||
|
||||
cvc.pukoid = rsa15withSHA256;
|
||||
cvc.modulusSize = key_size;
|
||||
cvc.primeOrModuluslen = key_size/8;
|
||||
cvc.primeOrModulus = (uint8_t *)malloc(cvc.primeOrModuluslen);
|
||||
mbedtls_mpi_write_binary(&rsa.N, cvc.primeOrModulus, key_size/8);
|
||||
int r = sc_pkcs15emu_sc_hsm_encode_cvc(&p15card, &cvc, &cvcbin, &cvclen);
|
||||
cvc.primeOrModulus = (uint8_t *)calloc(1, cvc.primeOrModuluslen);
|
||||
cvc.signatureLen = key_size/8;
|
||||
cvc.signature = (uint8_t *)malloc(key_size/8);
|
||||
cvc.signature = (uint8_t *)calloc(1, key_size/8);
|
||||
cvc.outerSignatureLen = key_size/8;
|
||||
cvc.outerSignature = (uint8_t *)calloc(1, key_size/8);
|
||||
mbedtls_mpi_write_binary(&rsa.N, cvc.primeOrModulus, key_size/8);
|
||||
unsigned int cla,tag;
|
||||
int r = sc_pkcs15emu_sc_hsm_encode_cvc_req(&p15card, &cvc, &cvcbin, &cvclen);
|
||||
//cvcpo = cvcbin;
|
||||
//sc_asn1_read_tag((const u8 **)&cvcpo, cvclen, &cla, &tag, &taglen);
|
||||
uint8_t hsh[32];
|
||||
hash(cvcbin, cvclen, hsh);
|
||||
ret = mbedtls_rsa_rsassa_pkcs1_v15_sign(&rsa, random_gen, &index, MBEDTLS_MD_SHA256, 32, hsh, cvc.signature);
|
||||
printf("ret %d\r\n");
|
||||
free(cvcbin);
|
||||
|
||||
r = sc_pkcs15emu_sc_hsm_encode_cvc(&p15card, &cvc, &cvcbin, &cvclen);
|
||||
r = sc_pkcs15emu_sc_hsm_encode_cvc_req(&p15card, &cvc, &cvcbin, &cvclen);
|
||||
//cvcpo = cvcbin;
|
||||
//sc_asn1_read_tag((const u8 **)&cvcpo, cvclen, &cla, &tag, &taglen);
|
||||
printf("r %d\r\n",r);
|
||||
memcpy(res_APDU, cvcbin, cvclen);
|
||||
free(cvcbin);
|
||||
sc_pkcs15emu_sc_hsm_free_cvc(&cvc);
|
||||
free(p15card.card);
|
||||
res_APDU_size = cvclen;
|
||||
apdu.expected_res_size = cvclen;
|
||||
//sc_asn1_print_tags(cvcbin, cvclen);
|
||||
|
||||
r = store_key_rsa(&rsa, key_size, key_id, ctx);
|
||||
printf("r %d\r\n");
|
||||
|
||||
sc_pkcs15emu_sc_hsm_free_cvc(&cvc);
|
||||
mbedtls_rsa_free(&rsa);
|
||||
free(p15card.card);
|
||||
free(cvcbin);
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -700,7 +831,7 @@ int cmd_update_ef() {
|
|||
else {
|
||||
if (!ef->data)
|
||||
return SW_DATA_INVALID();
|
||||
uint8_t *data_merge = (uint8_t *)malloc(offset+data_len);
|
||||
uint8_t *data_merge = (uint8_t *)calloc(1, offset+data_len);
|
||||
memcpy(data_merge, file_read(ef->data), offset);
|
||||
memcpy(data_merge+offset, data, data_len);
|
||||
int r = flash_write_data_to_file(ef, data_merge, data_len);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue