Roy Want / Mbed OS beaconCompileReadyFork
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers EIDFrame.cpp Source File

EIDFrame.cpp

00001 /*
00002  * Copyright (c) 2016, Google Inc, All Rights Reserved
00003  * SPDX-License-Identifier: Apache-2.0
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License"); you may
00006  * not use this file except in compliance with the License
00007  * You may obtain a copy of the License at
00008  *
00009  * http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
00013  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 
00018 #include "EIDFrame.h"
00019 #include "EddystoneService.h"
00020 #include "EntropySource/EntropySource.h"
00021 
00022 EIDFrame::EIDFrame()
00023 {
00024     mbedtls_entropy_init(&entropy);
00025     // init entropy source
00026     eddystoneRegisterEntropySource(&entropy);
00027     // init Random
00028     mbedtls_ctr_drbg_init(&ctr_drbg);
00029 }
00030 
00031 void EIDFrame::clearFrame(uint8_t* frame) {
00032     frame[FRAME_LEN_OFFSET] = 0; // Set frame length to zero to clear it
00033 }
00034 
00035 
00036 void EIDFrame::setData(uint8_t *rawFrame, int8_t advTxPower, const uint8_t* eidData)
00037 {
00038     size_t index = 0;
00039     rawFrame[index++] = EDDYSTONE_UUID_SIZE + EID_FRAME_LEN;   // EID length + overhead of four bytes below
00040     rawFrame[index++] = EDDYSTONE_UUID[0];                      // 16-bit Eddystone UUID
00041     rawFrame[index++] = EDDYSTONE_UUID[1];
00042     rawFrame[index++] = FRAME_TYPE_EID;                         // 1B  Type
00043     rawFrame[index++] = advTxPower;                             // 1B  Power @ 0meter
00044 
00045     memcpy(rawFrame + index, eidData, EID_LENGTH);              // EID = 8 BYTE ID
00046 }
00047 
00048 uint8_t* EIDFrame::getData(uint8_t* rawFrame)
00049 {
00050         return &(rawFrame[EID_DATA_OFFSET]);
00051 }
00052 
00053 uint8_t  EIDFrame::getDataLength(uint8_t* rawFrame)
00054 {
00055      return rawFrame[FRAME_LEN_OFFSET] - EDDYSTONE_UUID_LEN;
00056 }
00057 
00058 uint8_t* EIDFrame::getAdvFrame(uint8_t* rawFrame)
00059 {
00060     return &(rawFrame[ADV_FRAME_OFFSET]);
00061 }
00062 
00063 uint8_t EIDFrame::getAdvFrameLength(uint8_t* rawFrame)
00064 {
00065     return rawFrame[FRAME_LEN_OFFSET];
00066 }
00067 
00068 uint8_t* EIDFrame::getEid(uint8_t* rawFrame)
00069 {
00070     return &(rawFrame[EID_VALUE_OFFSET]);
00071 }
00072 
00073 uint8_t EIDFrame::getEidLength(uint8_t* rawFrame)
00074 {
00075     return rawFrame[FRAME_LEN_OFFSET] - EID_HEADER_LEN;
00076 }
00077 
00078 void EIDFrame::setAdvTxPower(uint8_t* rawFrame, int8_t advTxPower)
00079 {
00080     rawFrame[EID_TXPOWER_OFFSET] = advTxPower;
00081 }
00082 
00083 // Mote: This is only called after the rotation period is due, or on writing/creating a new eidIdentityKey
00084 void EIDFrame::update(uint8_t* rawFrame, uint8_t* eidIdentityKey, uint8_t rotationPeriodExp,  uint32_t timeSecs)
00085 {  
00086     // Calculate the temporary key datastructure 1
00087     uint8_t ts[4]; // big endian representation of time
00088     ts[0] = (timeSecs  >> 24) & 0xff;
00089     ts[1] = (timeSecs >> 16) & 0xff;
00090 
00091     uint8_t tmpEidDS1[16] = { 0,0,0,0,0,0,0,0,0,0,0, SALT, 0, 0, ts[0], ts[1] };
00092     
00093     // Perform the aes encryption to generate the final temporary key.
00094     uint8_t tmpKey[16];
00095     aes128Encrypt(eidIdentityKey, tmpEidDS1, tmpKey);
00096     
00097     // Compute the EID 
00098     uint8_t eid[16];
00099     uint32_t scaledTime = (timeSecs >> rotationPeriodExp) << rotationPeriodExp;
00100     ts[0] = (scaledTime  >> 24) & 0xff;
00101     ts[1] = (scaledTime >> 16) & 0xff;
00102     ts[2] = (scaledTime >> 8) & 0xff;
00103     ts[3] = scaledTime & 0xff;
00104     uint8_t tmpEidDS2[16] = { 0,0,0,0,0,0,0,0,0,0,0, rotationPeriodExp, ts[0], ts[1], ts[2], ts[3] };
00105     aes128Encrypt(tmpKey, tmpEidDS2, eid);
00106     
00107     // copy the leading 8 bytes of the eid result (full result length = 16) into the ADV frame
00108     memcpy(rawFrame + 5, eid, EID_LENGTH); 
00109     
00110 }
00111 
00112 /** AES128 encrypts a 16-byte input array with a key, resulting in a 16-byte output array */
00113 void EIDFrame::aes128Encrypt(uint8_t key[], uint8_t input[], uint8_t output[]) {
00114     mbedtls_aes_context ctx;
00115     mbedtls_aes_init(&ctx); 
00116     mbedtls_aes_setkey_enc(&ctx, key, 8 * sizeof(Lock_t));
00117     mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, input, output);
00118     mbedtls_aes_free(&ctx);
00119 }
00120 
00121 int EIDFrame::genBeaconKeys(PrivateEcdhKey_t beaconPrivateEcdhKey, PublicEcdhKey_t beaconPublicEcdhKey) {
00122     mbedtls_ecdh_init( &ecdh_ctx );
00123     
00124     int i = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0);
00125     if (i != 0) {
00126         return i; // return EID_RND_FAIL;
00127     }
00128  
00129     if (mbedtls_ecp_group_load(&ecdh_ctx.grp, MBEDTLS_ECP_DP_CURVE25519) != 0) {
00130         return EID_GRP_FAIL;
00131     }
00132     if (mbedtls_ecdh_gen_public(&ecdh_ctx.grp, &ecdh_ctx.d, &ecdh_ctx.Q, mbedtls_ctr_drbg_random, &ctr_drbg) != 0) {
00133         return EID_GENKEY_FAIL;
00134     }
00135     
00136     mbedtls_mpi_write_binary(&ecdh_ctx.d, beaconPrivateEcdhKey, sizeof(PrivateEcdhKey_t));
00137     mbedtls_mpi_write_binary(&ecdh_ctx.Q.X, beaconPublicEcdhKey, sizeof(PublicEcdhKey_t));
00138     
00139     mbedtls_ecdh_free( &ecdh_ctx );
00140     return EID_SUCCESS;
00141 }
00142 
00143 int EIDFrame::genEcdhSharedKey(PrivateEcdhKey_t beaconPrivateEcdhKey, PublicEcdhKey_t beaconPublicEcdhKey, PublicEcdhKey_t serverPublicEcdhKey, EidIdentityKey_t eidIdentityKey) {
00144   int16_t ret = 0;
00145   uint8_t tmp[32];
00146   // initialize context
00147   mbedtls_ecdh_init( &ecdh_ctx );
00148   mbedtls_ecp_group_load( &ecdh_ctx.grp, MBEDTLS_ECP_DP_CURVE25519 );
00149 
00150   // copy binary beacon private key (previously generated!) into context 
00151   // Note: As the PrivateKey is generated locally, it is Big Endian
00152   ret = mbedtls_mpi_read_binary( &ecdh_ctx.d, beaconPrivateEcdhKey, sizeof(PrivateEcdhKey_t) );
00153   
00154   // copy server-public-key (received through GATT characteristic 10) into context
00155   ret = mbedtls_mpi_lset( &ecdh_ctx.Qp.Z, 1 );
00156   EddystoneService::swapEndianArray(serverPublicEcdhKey, tmp, 32); // To make it Big Endian
00157   ret = mbedtls_mpi_read_binary( &ecdh_ctx.Qp.X, tmp , sizeof(PublicEcdhKey_t) ); 
00158 
00159   // ECDH point multiplication
00160   size_t olen; // actual size of shared secret 
00161   uint8_t sharedSecret[32]; // shared ECDH secret
00162   memset(sharedSecret, 0, 32);
00163   ret = mbedtls_ecdh_calc_secret( &ecdh_ctx, &olen, sharedSecret, sizeof(sharedSecret), NULL, NULL );
00164   LOG(("size of olen= %d  ret=%x\r\n", olen, ret));
00165   EddystoneService::swapEndianArray(sharedSecret, tmp, 32);
00166   memcpy(sharedSecret, tmp, 32);
00167   LOG(("Shared secret=")); EddystoneService::logPrintHex(sharedSecret, 32);
00168   if (olen != sizeof(sharedSecret)) {
00169       return EID_GENKEY_FAIL;
00170   }
00171   if (ret == MBEDTLS_ERR_ECP_BAD_INPUT_DATA) {
00172       return EID_RC_SS_IS_ZERO;
00173   }
00174 
00175   // Convert the shared secret to key material using HKDF-SHA256. HKDF is used with 
00176   // the salt set to a concatenation of the resolver's public key and beacon's
00177   // public key, with a null context. 
00178 
00179   // build HKDF key
00180   unsigned char k[ 64 ];
00181   EddystoneService::swapEndianArray(beaconPublicEcdhKey, tmp, 32);
00182   memcpy( &k[0], serverPublicEcdhKey, sizeof(PublicEcdhKey_t) );
00183   memcpy( &k[32], tmp, sizeof(PublicEcdhKey_t) );
00184 
00185   // compute HKDF: see https://tools.ietf.org/html/rfc5869
00186   // mbedtls_md_context_t md_ctx;
00187   mbedtls_md_init( &md_ctx );
00188   mbedtls_md_setup( &md_ctx, mbedtls_md_info_from_type( MBEDTLS_MD_SHA256 ), 1 );
00189   mbedtls_md_hmac_starts( &md_ctx, k, sizeof( k ) );
00190   mbedtls_md_hmac_update( &md_ctx, sharedSecret, sizeof(sharedSecret) );
00191   unsigned char prk[ 32 ];
00192   mbedtls_md_hmac_finish( &md_ctx, prk );
00193   mbedtls_md_hmac_starts( &md_ctx, prk, sizeof( prk ) );
00194   const unsigned char const1[] = { 0x01 };
00195   mbedtls_md_hmac_update( &md_ctx, const1, sizeof( const1 ) );
00196   unsigned char t[ 32 ];
00197   mbedtls_md_hmac_finish( &md_ctx, t );
00198 
00199   //Truncate the key material to 16 bytes (128 bits) to convert it to an AES-128 secret key.
00200   memcpy( eidIdentityKey, t, sizeof(EidIdentityKey_t) );
00201   LOG(("\r\nEIDIdentityKey=")); EddystoneService::logPrintHex(t, 32); LOG(("\r\n"));
00202 
00203   mbedtls_md_free( &md_ctx );
00204   mbedtls_ecdh_free( &ecdh_ctx );
00205   return EID_SUCCESS;
00206 }