MicrochipTech / cryptoauthlib

Library for interacting with the Crypto Authentication secure elements
Other
372 stars 217 forks source link

Using Atmel508 with external library like mbedTLS #18

Closed umanayana closed 3 years ago

umanayana commented 6 years ago

Hello,

I am working on a commercial product which utilises mbedTLS to provide the TLS client connection to a server. The certificate information is stored Atmel508, but I am not sure how to retrieve it so that it can work with mbedTLS library.

The mbedTLS library can take DER and PEM certificate format. So I need to load the certificate in the library. The specific method in the library for parsing DER certificate is mbedtls_x509_crt_parse_der() What's the best way to create these certificate from the fields stored in the Atmel508?

https://github.com/ARMmbed/mbedtls

bryan-hunt commented 6 years ago

We're working on the mbedtls integration currently. It is not yet ready to publish but if you would like a preview please send in a request to your Microchip FAE or https://www.microchip.com/support

Include my name in the request so it can be forwarded to me.

umanayana commented 6 years ago

Hi Bryan,

In the process of getting the FAE contact from another engineer. In the mean time I am wondering if you could answer my question below.

What format does the method atcacert_read_cert() fill the "cert" variable with? Is this DER formatted certificate that is reconstructed from the Atmel508?

bryan-hunt commented 6 years ago

mbedtls_x509_crt_parse will accept der or pem. The output from atcacert_read_cert is der an can be directly fed into the function. This code fragment will not compile with the existing library so you'd have to modify it to load the proper public key for the ca that signed the cert in order to rebuild the certificate.

int atca_mbedtls_cert_init(mbedtls_x509_crt * cert, atcacert_def_t * cert_def)
{
    uint8_t                 ca_key[64];
    int                     ret = ATCA_SUCCESS;
    size_t                  cert_len;
    uint8_t               * cert_buf = NULL;
    int i;

    if (cert_def->next)
    {
        atcacert_device_loc_t * ca_key_cfg = &cert_def->next->public_key_dev_loc;

        if (ca_key_cfg->is_genkey)
        {
            ret = atcab_get_pubkey(ca_key_cfg->slot, ca_key);
        }
        else
        {
            ret = atcab_read_pubkey(ca_key_cfg->slot, ca_key);
        }
    }

    mbedtls_x509_crt_init(cert);

    if(NULL == (cert_buf = mbedtls_calloc(1, cert_def->cert_template_size)))
    {
        ret = -1;
    }

    if (0 == ret)
    {
        ret = atcacert_read_cert(cert_def, cert_def->next?ca_key:NULL, cert_buf, &cert_len);
    }

    if (0 == ret)
    {
        ret = mbedtls_x509_crt_parse(cert, (const unsigned char *)cert_buf, cert_len);
    }

    if (cert_buf)
    {
        mbedtls_free(cert_buf);
    }

    return ret;
}
umanayana commented 6 years ago

I want to read the signer certificate. So can I NULL the public key parameter from "atcacert_read_cert()"? Isn't the signer certificate self signed?

Just a little bit of a background context We are trying to connect to AWS IoT as a client using mbedTLS and to do this we need rebuild the signer and device certificate. Then pass these certificates into the mbedTLS.

bryan-hunt commented 6 years ago

The signer isn't ever self signed but the NULL for that functions means it shouldn't expect the public key of the signing CA to be different and thus it's the same as the template. However if you're using the definitions and provisioning scripts from the AWS Zero Touch Kit the public key for the signer's CA is stored in in a slot because the scripts to create the ecosystem create a root CA as well. That gets stored in slots along with the signer and device certificate data so it has to be extracted first before reconstructing the signer.

For mbedtls you'll also need to use the private key in the device. This has more code to it than the certificate reconstruction (with two distinct methods can be done - one to replace the mbedtls_pk_context function pointers or to defining MBEDTLS_ECDSA_SIGN_ALT in your mbedtls configuration file and implementing those functions it removes from the library separately).

A quick example of the first way is thus:

static const uint8_t ec_pubkey_asn1_header[] = {
    0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86,
    0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, 0x08, 0x2A,
    0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03,
    0x42, 0x00, 0x04
};

extern int ecdsa_signature_to_asn1(const mbedtls_mpi *r, const mbedtls_mpi *s,
    unsigned char *sig, size_t *slen);

static int atca_mbedtls_eckey_sign(void *ctx, mbedtls_md_type_t md_alg,
    const unsigned char *hash, size_t hash_len,
    unsigned char *sig, size_t *sig_len,
    int(*f_rng)(void *, unsigned char *, size_t), void *p_rng)
{
    int ret;
    mbedtls_mpi r, s;
    uint8_t raw_sig[ATCA_SIG_SIZE];

    ((void)md_alg);
    ((void)f_rng);
    ((void)p_rng);

    if (ATCA_KEY_SIZE > hash_len)
    {
        return MBEDTLS_ERR_MD_BAD_INPUT_DATA;
    }

    ret = atcab_sign(((mbedtls_ecp_keypair *)ctx)->d.s, hash, raw_sig);

    mbedtls_mpi_init(&r);
    mbedtls_mpi_init(&s);

    if (0 == ret)
    {
        mbedtls_mpi_read_binary(&r, raw_sig, ATCA_SIG_SIZE / 2);
    }

    if (0 == ret)
    {
        ret = mbedtls_mpi_read_binary(&s, &raw_sig[ATCA_SIG_SIZE / 2], ATCA_SIG_SIZE / 2);
    }

    if(0 == ret)
    {
        ret = ecdsa_signature_to_asn1(&r, &s, sig, sig_len);
    }

    mbedtls_mpi_free(&r);
    mbedtls_mpi_free(&s);

    return ret;
}

static mbedtls_pk_info_t atca_mbedtls_eckey_info;

int atca_mbedtls_pk_init(mbedtls_pk_context * pkey, uint16_t slotid)
{
    int ret;
    uint8_t pkey_buf[sizeof(ec_pubkey_asn1_header) + ATCA_PUB_KEY_SIZE];
    uint8_t * pBuf = pkey_buf;

    mbedtls_pk_init(pkey);

    memcpy(pkey_buf, ec_pubkey_asn1_header, sizeof(ec_pubkey_asn1_header));

    if (ATCA_SUCCESS == (ret = atcab_get_pubkey(slotid, &pkey_buf[sizeof(ec_pubkey_asn1_header)])))
    {
        ret = mbedtls_pk_parse_subpubkey(&pBuf, pkey_buf + sizeof(pkey_buf), pkey);

        ((mbedtls_ecp_keypair *)pkey->pk_ctx)->d.s = slotid;

        memcpy(&atca_mbedtls_eckey_info, pkey->pk_info, sizeof(mbedtls_pk_info_t));

        atca_mbedtls_eckey_info.sign_func = &atca_mbedtls_eckey_sign;

        pkey->pk_info = &atca_mbedtls_eckey_info;
    }

    return ret;
}

To put it together you get something like this:

    /* Start a session with the device */
    if (ATCA_SUCCESS != (status = atcab_init(&cfg_ateccx08a_kithid_default)))
    {
        printf("Failed to init: %d\r\n", status);
        goto exit;
    }

    /* Convert to an mbedtls key */
    if (0 != atca_mbedtls_pk_init(&pkey, 0))
    {
        printf("Failed to parse key from device\n");
        goto exit;
    }

    /* Extract the device certificate and convert to mbedtls cert */
    if (0 != atca_mbedtls_cert_init(&cert, &g_cert_def_2_device))
    {
        printf("Failed to parse cert from device\n");
        goto exit;
    }

    /* Attach the client certificate and private key to the TLS configuration */
    printf("  . Set up the client credentials.");
    fflush(stdout);
    if(0 != (ret = mbedtls_ssl_conf_own_cert(
            &conf, &cert, &pkey)))
    {
        printf(" failed\n ! mbedtls_ssl_conf_own_cert returned %d\r\n", ret);
        goto exit;
    }

This has not been extensively tested and may not be representative of the final integration when it is released.

umanayana commented 6 years ago

@bryan-hunt

Is there another TLS library that already works with this chip? Since our product already has an IP stack the TLS library is going have to be able to integrate with another IP stack.

bryan-hunt commented 6 years ago

WolfSSL has support for the ECC devices. It is listed as ATECC508A but since it does not appear to come with cryptoauthlib or be tied to a specific version you should be able to use the latest version of cryptoauthlib to pick up support of the ATECC608A.

umanayana commented 6 years ago

A quick investigation of the source and it seem that parts of the source make direct calls to methods in cryptoauthlib library. It also seems to have direct support for ATECC508A. This looks very promising, thanks for pointing it out.

On another note would it be possible for you to provide me with a list on the modules (e.g. MBEDTLS_ECDSA_SIGN_ALT) within mbedTLS that would need porting. So that we have can establish a TLS client connection using mbedTLS.

Thanks

bryan-hunt commented 6 years ago

To establish a connection with mbedtls all you need are the above code fragments (with the exception being the certificate ca public key retrieval).

umanayana commented 6 years ago

Ok, that seems straight forward. Thanks for the quick responses. You have been very helpful. I have requested a preview of the mbedTLS integration from the FAE. So hopefully this with further help us with anything.

Cheers

csl-lmather commented 6 years ago

We've previously integrated the ATECC508A into mbedTLS for ECDSA signature generation and verification, as well for ECDHE. Until recently the path of least resistance would be to use MBEDTLS_ECDSA_SIGN_ALT to inject your own signature generation code, and then to do a hacky abuse of the mbedtls_pk_context struct to provide a key pair that doesn't have a private component set.

However, as of mbedTLS 2.11 ARM have added a new API for the asynchronous offload of private-key operations --- I haven't investigated using it yet in any detail but I believe that would now be the 'correct' route to take.

bryan-hunt commented 6 years ago

From what I've seen in that new API (since it's undocumented like 90% of mbedtls seems to be) MBEDTLS_ECDSA_SIGN_ALT is still the preferred route for client authentication since what little documentation there is on it only applies to the server session API.

There simply isn't good way to keep the ECDSA hardware operations strictly to the key you specifically intend to use without extending the key structure.

But this is really up to the end customer to decide how they want to approach it - we'll be providing both an extended key and _ALT implementations that are connected to the mbedtls configuration. There are pros and cons for all the different approaches and almost impossible to meet everybody's needs at one time.

bryan-hunt commented 6 years ago

Yep I've confirmed that the new API is just for servers. https://github.com/ARMmbed/mbedtls/search?q=f_async_sign_start

The implementation of it is somewhat hacky in its own right. ssl_srv.c

mbedTLS is in desperate need of a core refactor similar to the OpenSSL 1.1 refactor.

csl-lmather commented 6 years ago

Ah yeah you're right, server only. MBEDTLS_ECDSA_SIGN_ALT it is!

umanayana commented 5 years ago

@bryan-hunt

I am trying to integrate ECDH process with mbedTLS but having some problems. During the handshaking process mbedTLS keeps returning the error A fatal alert message was received from our peer (0x7780). I have successful implemented the signing and verification process.

I have enabled MBEDTLS_ECDH_GEN_PUBLIC_ALT and MBEDTLS_ECDH_COMPUTE_SHARED_ALT in the preprocessor configuration of mbedTLS.

mbedtls_ecdh_gen_public I generate a ephemeral key pair. The generated public key is then stored in the Q parameter. Pseudo code below, can you let me know if this is correct?

/*Generate ephemeral key*/
uint8_t pub_key[65];
pub_key[0] = 0x04; 
d->s = 7;
int status = Gen_Key(d->s, PRIVATE_KEY_GENERATE, 1, &pub_key[1]);
status = mbedtls_ecp_point_read_binary(grp, Q, pub_key, sizeof(pub_key));

mbedtls_ecdh_compute_shared Using mbedtls_ecp_point_write_binary() method I take the Q parameter and convert it to 64 byte public key. Which should be the peers public key. I then used the ephemeral private key generated from above to make the pre-master secret, using the ecdh process. I store the pre-master secrete in parameter Z. Pseudo code below, can you let me know if this is correct?

uint8_t peer_pub_key[65] = { 0 };
size_t len = 0;
int status = mbedtls_ecp_point_write_binary(grp, Q, MBEDTLS_ECP_PF_UNCOMPRESSED, &len, peer_pub_key, sizeof(peer_pub_key));
uint8_t secret[PMS_SIZE];
status = Compute_Ecdh_Secret(d->s, &peer_pub_key[1], secret);
status = mbedtls_mpi_read_binary(z, secret, sizeof(secret));

I am using the ATECC508, all the command execute correctly on the ATECC508 without any errors.

umanayana commented 5 years ago

I was hoping someone could enlightenment me a little on why the process above could be failing. Has anyone gotten the ECDH process working with the Atecc508 and the mbedtls.

I have concluded that it just can't work for an unknown reason and having ECDH occur in software instead.

I have tired different endianness without any success.

bryan-hunt commented 5 years ago

Yes it has been made to work. I'm not sure what the first one is doing? For the genkey it should be of the form atcab_genkey(slot, pub_key).

We have used the mpi APIs successfully. I can't say why the ecp point functions are producing different results:

    if ((ret = atcab_genkey(slot, public_key)) != 0)
        return ret;

    if ((ret = mbedtls_mpi_read_binary(&(Q->X), public_key, ATCA_PUB_KEY_SIZE / 2)) != 0)
        return ret;

    if ((ret = mbedtls_mpi_read_binary(&(Q->Y), &public_key[ATCA_PUB_KEY_SIZE / 2], ATCA_PUB_KEY_SIZE / 2)) != 0)
        return ret;

    if ((ret = mbedtls_mpi_read_binary(&(Q->Z), &temp, 1)) != 0)
        return ret;
    if ((ret = mbedtls_mpi_write_binary(&(Q->X), public_key, ATCA_PUB_KEY_SIZE / 2)) != 0)
        return ret;

    if ((ret = mbedtls_mpi_write_binary(&(Q->Y), &public_key[ATCA_PUB_KEY_SIZE / 2], ATCA_PUB_KEY_SIZE / 2)) != 0)
        return ret;

    if (ret = atcab_ecdh_base(ECDH_MODE, slot, public_key, shared_key, NULL) != 0)
        return ret;

    if ((ret = mbedtls_mpi_read_binary(z, shared_key, ATCA_KEY_SIZE)) != 0)
        return ret;
umanayana commented 5 years ago

@bryan-hunt

What is the value of tmp when you're writing Q->Z parameter?

After generating the public key using GenKey command. I use mbedtls_ecp_check_pubkey() to check if it's a valid public key but this check fails. Do you know why it would fail? The GenKey command does not fail and does return a valid public key.

The only difference to what we are doing is that our slots require authorization before using. Will this affect anything? Note that we are correctly providing authorization because both the GenKey and ECDH commands are successful.

Can you also share the cipher suit that you're using? I am using ECDHE-ECDSA-WITH-AES-128-GCM-SHA256.

wilkxt commented 5 years ago

@umanayana Do you resolve problem about error 0x7780? I have the same when using ATECC508 and MBEDTLS_ECDH_GEN_PUBLIC_ALT.

umanayana commented 5 years ago

@wilkxt No, I haven't. Other things have taken higher priority over this. We have accepted the performance decrease in the handshake for now.

At some point we will have to setup a local server and run test.

wilkxt commented 5 years ago

@bryan-hunt I have problem with atcab_sign My modification orginal function with define MBEDTLS_ECDSA_SIGN_ALT

int mbedtls_ecdsa_write_signature_restartable( mbedtls_ecdsa_context *ctx,
                           mbedtls_md_type_t md_alg,
                           const unsigned char *hash, size_t hlen,
                           unsigned char *sig, size_t *slen,
                           int (*f_rng)(void *, unsigned char *, size_t),
                           void *p_rng,
                           mbedtls_ecdsa_restart_ctx *rs_ctx )
{
    int ret;
    mbedtls_mpi r, s;
    ECDSA_VALIDATE_RET( ctx  != NULL );
    ECDSA_VALIDATE_RET( hash != NULL );
    ECDSA_VALIDATE_RET( sig  != NULL );
    ECDSA_VALIDATE_RET( slen != NULL );

    mbedtls_mpi_init( &r );
    mbedtls_mpi_init( &s );

#if defined(MBEDTLS_ECDSA_SIGN_ALT)

    uint8_t signature[64],status;
    status = atcab_sign(1, hash, signature);
    DEBUG_UART("...sign status=0x%x\r\n",status);
     // Verify signature
   //  uint8_t is_verified;
   // status = atcab_verify_extern(hash, signature, public_key, &is_verified);
   // DEBUG_UART("...very status=0x%x %d\r\n",status,is_verified);

     mbedtls_mpi_read_binary(&r, signature, 32);
     mbedtls_mpi_read_binary(&s, signature+32, 32);

#else
    MBEDTLS_MPI_CHK( ecdsa_sign_restartable( &ctx->grp, &r, &s, &ctx->d,
                         hash, hlen, f_rng, p_rng, rs_ctx ) );
#endif /* MBEDTLS_ECDSA_SIGN_ALT */
#endif /* MBEDTLS_ECDSA_DETERMINISTIC */

    MBEDTLS_MPI_CHK( ecdsa_signature_to_asn1( &r, &s, sig, slen ) );

    //DEBUG_UART("sig l=%d 0x%x 0x%x 0x%x 0x%x\r\n",*slen,sig[31],sig[30],sig[29],sig[28]);

cleanup:
    mbedtls_mpi_free( &r );
    mbedtls_mpi_free( &s );

    return( ret );
}

But after that Firefox return SEC_ERROR_BAD_SIGNATURE. I have set

define MBEDTLS_ECDH_GEN_PUBLIC_ALT

define MBEDTLS_ECDH_COMPUTE_SHARED_ALT

and this is OK when sets

define MBEDTLS_ECDSA_SIGN_ALT i have error bad signature, what is wrong?

bryan-hunt commented 5 years ago

@wilkxt I am not very familiar with the "restartable" version of the function. I'm not sure its a good fit for the application since atcab_sign blocks.

We do have an officially released mbedtls integration available in the latest release: https://github.com/MicrochipTech/cryptoauthlib/blob/master/lib/mbedtls/atca_mbedtls_ecdsa.c

Setup is relative simple: https://github.com/MicrochipTech/mbedtls-examples/blob/91a1af2a52fa9f89a0748fc8f204c0f396ff2357/source/connect.c#L154-L166

    /* Convert to an mbedtls key */
    if (0 != atca_mbedtls_pk_init(&pkey, 0))
    {
        printf("Failed to parse key from device\n");
        goto exit;
    }

    /* Extract the device certificate and convert to mbedtls cert */
    if (0 != atca_mbedtls_cert_add(&cert, &g_cert_def_2_device))
    {
        printf("Failed to parse cert from device\n");
        goto exit;
    }
wilkxt commented 5 years ago

Thanks, I'll check it

wilkxt commented 5 years ago

@bryan-hunt Setup is necessary befor I use mbedtls_ecdsa_sign?

bryan-hunt commented 5 years ago

@wilkxt yes the key object has to be initialized using the atca_mbedtls_pk_init function similarly to how you would load a private key from a pem file. In this case it is setting up the structure with the slot that will be used for the key. You then can pass that key structure to any of the mbedtls key operations.

wilkxt commented 5 years ago

@bryan-hunt Two question: 1.Why int atca_mbedtls_ecdh_slot_cb(void) { return 0xFFFF; } return 0xFFFF when slotid is from 0 -15?

  1. I must do atca_mbedtls_pk_init(&pkey, 0)) but /* Extract the device certificate and convert to mbedtls cert */ if (0 != atca_mbedtls_cert_add(&cert, &g_cert_def_2_device)) { printf("Failed to parse cert from device\n"); goto exit; }

is necessary? I may use example cert (const char mbedtls_test_srv_crt_ec ) form mbedetTLS certs.c file? I'm just starting with SSL and I do not know how it works.

kmwebnet commented 5 years ago

in my research, 0xffff just defines to use Tempkey.

define ATCA_TEMPKEY_KEYID (0xFFFF) //!< KeyID when referencing TempKey

this seems to be related to atcab_genkey, reading public key function and atcab_ecdh_tempkey_ioenc, getting ECDH result via tempkey. I could't make mbedtls-examples/connect.c work with 0xffff option after 1 month study, but success with the slot number which configured for ECDH operation and P256 key handling.

KarthikeyanL commented 5 years ago

@kmwebnet

Below are the scenarios that could have gone wrong.

1)Improper device selection. By default the atcab_init structure cfg_ateccx08a_kithid_default.devtype is defined to use the ATECC508A device.The ECC508A device doesn't have the functionality to generate Private key in Tempkey and only ECC608A has it.This may be the reason for ECDH Working with the slots and not the tempkey.The user should change the devtype to ATECC608A for generating the private key in tempkey and using it for ECDH operations. If this is not changed, the ecdh functionality will work only for the slots and output will be in clear.

2)Improper IO protection key. If the device selected is already ECC608A and if the ECDH functionality doesn't work. Another reason could be the IO protection key in the slot. The ECC608A has the functionality to send the Encrypted Shared secret to user and the mbedtls makes use of this functionality to get the encrypted shared secret from device irrespective of the private key location. The application has to decrypt the shared secret with the help of the correct IO protection key, defined in function atca_mbedtls_ecdh_ioprot_cb(). If the key in application doesn't match with the key in ECC608 slot, the host will not be able to decode the shared key properly and could result in error.

kmwebnet commented 5 years ago

thank you. I re-checked atcab_init structure and found the mistake. after correction, ecdh operation with tempkey succeeded. in addition, I managed to rotate io protection key which places in the key slot with success by setting IoProtectionKeyEnable and IoProtectionKey in config.