Sarah Marsh / Mbed OS EddystoneBeacon
Committer:
sarahmarshy
Date:
Tue Nov 29 06:29:10 2016 +0000
Revision:
0:1c7da5f83647
Initial commit

Who changed what in which revision?

UserRevisionLine numberNew contents of line
sarahmarshy 0:1c7da5f83647 1 /*
sarahmarshy 0:1c7da5f83647 2 * Copyright (c) 2016, Google Inc, All Rights Reserved
sarahmarshy 0:1c7da5f83647 3 * SPDX-License-Identifier: Apache-2.0
sarahmarshy 0:1c7da5f83647 4 *
sarahmarshy 0:1c7da5f83647 5 * Licensed under the Apache License, Version 2.0 (the "License"); you may
sarahmarshy 0:1c7da5f83647 6 * not use this file except in compliance with the License
sarahmarshy 0:1c7da5f83647 7 * You may obtain a copy of the License at
sarahmarshy 0:1c7da5f83647 8 *
sarahmarshy 0:1c7da5f83647 9 * http://www.apache.org/licenses/LICENSE-2.0
sarahmarshy 0:1c7da5f83647 10 *
sarahmarshy 0:1c7da5f83647 11 * Unless required by applicable law or agreed to in writing, software
sarahmarshy 0:1c7da5f83647 12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
sarahmarshy 0:1c7da5f83647 13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
sarahmarshy 0:1c7da5f83647 14 * See the License for the specific language governing permissions and
sarahmarshy 0:1c7da5f83647 15 * limitations under the License.
sarahmarshy 0:1c7da5f83647 16 */
sarahmarshy 0:1c7da5f83647 17
sarahmarshy 0:1c7da5f83647 18 #include "EIDFrame.h"
sarahmarshy 0:1c7da5f83647 19 #include "EddystoneService.h"
sarahmarshy 0:1c7da5f83647 20 #include "EntropySource/EntropySource.h"
sarahmarshy 0:1c7da5f83647 21
sarahmarshy 0:1c7da5f83647 22 EIDFrame::EIDFrame()
sarahmarshy 0:1c7da5f83647 23 {
sarahmarshy 0:1c7da5f83647 24 mbedtls_entropy_init(&entropy);
sarahmarshy 0:1c7da5f83647 25 // init entropy source
sarahmarshy 0:1c7da5f83647 26 eddystoneRegisterEntropySource(&entropy);
sarahmarshy 0:1c7da5f83647 27 // init Random
sarahmarshy 0:1c7da5f83647 28 mbedtls_ctr_drbg_init(&ctr_drbg);
sarahmarshy 0:1c7da5f83647 29 }
sarahmarshy 0:1c7da5f83647 30
sarahmarshy 0:1c7da5f83647 31 void EIDFrame::clearFrame(uint8_t* frame) {
sarahmarshy 0:1c7da5f83647 32 frame[FRAME_LEN_OFFSET] = 0; // Set frame length to zero to clear it
sarahmarshy 0:1c7da5f83647 33 }
sarahmarshy 0:1c7da5f83647 34
sarahmarshy 0:1c7da5f83647 35
sarahmarshy 0:1c7da5f83647 36 void EIDFrame::setData(uint8_t *rawFrame, int8_t advTxPower, const uint8_t* eidData)
sarahmarshy 0:1c7da5f83647 37 {
sarahmarshy 0:1c7da5f83647 38 size_t index = 0;
sarahmarshy 0:1c7da5f83647 39 rawFrame[index++] = EDDYSTONE_UUID_SIZE + EID_FRAME_LEN; // EID length + overhead of four bytes below
sarahmarshy 0:1c7da5f83647 40 rawFrame[index++] = EDDYSTONE_UUID[0]; // 16-bit Eddystone UUID
sarahmarshy 0:1c7da5f83647 41 rawFrame[index++] = EDDYSTONE_UUID[1];
sarahmarshy 0:1c7da5f83647 42 rawFrame[index++] = FRAME_TYPE_EID; // 1B Type
sarahmarshy 0:1c7da5f83647 43 rawFrame[index++] = advTxPower; // 1B Power @ 0meter
sarahmarshy 0:1c7da5f83647 44
sarahmarshy 0:1c7da5f83647 45 memcpy(rawFrame + index, eidData, EID_LENGTH); // EID = 8 BYTE ID
sarahmarshy 0:1c7da5f83647 46 }
sarahmarshy 0:1c7da5f83647 47
sarahmarshy 0:1c7da5f83647 48 uint8_t* EIDFrame::getData(uint8_t* rawFrame)
sarahmarshy 0:1c7da5f83647 49 {
sarahmarshy 0:1c7da5f83647 50 return &(rawFrame[EID_DATA_OFFSET]);
sarahmarshy 0:1c7da5f83647 51 }
sarahmarshy 0:1c7da5f83647 52
sarahmarshy 0:1c7da5f83647 53 uint8_t EIDFrame::getDataLength(uint8_t* rawFrame)
sarahmarshy 0:1c7da5f83647 54 {
sarahmarshy 0:1c7da5f83647 55 return rawFrame[FRAME_LEN_OFFSET] - EDDYSTONE_UUID_LEN;
sarahmarshy 0:1c7da5f83647 56 }
sarahmarshy 0:1c7da5f83647 57
sarahmarshy 0:1c7da5f83647 58 uint8_t* EIDFrame::getAdvFrame(uint8_t* rawFrame)
sarahmarshy 0:1c7da5f83647 59 {
sarahmarshy 0:1c7da5f83647 60 return &(rawFrame[ADV_FRAME_OFFSET]);
sarahmarshy 0:1c7da5f83647 61 }
sarahmarshy 0:1c7da5f83647 62
sarahmarshy 0:1c7da5f83647 63 uint8_t EIDFrame::getAdvFrameLength(uint8_t* rawFrame)
sarahmarshy 0:1c7da5f83647 64 {
sarahmarshy 0:1c7da5f83647 65 return rawFrame[FRAME_LEN_OFFSET];
sarahmarshy 0:1c7da5f83647 66 }
sarahmarshy 0:1c7da5f83647 67
sarahmarshy 0:1c7da5f83647 68 uint8_t* EIDFrame::getEid(uint8_t* rawFrame)
sarahmarshy 0:1c7da5f83647 69 {
sarahmarshy 0:1c7da5f83647 70 return &(rawFrame[EID_VALUE_OFFSET]);
sarahmarshy 0:1c7da5f83647 71 }
sarahmarshy 0:1c7da5f83647 72
sarahmarshy 0:1c7da5f83647 73 uint8_t EIDFrame::getEidLength(uint8_t* rawFrame)
sarahmarshy 0:1c7da5f83647 74 {
sarahmarshy 0:1c7da5f83647 75 return rawFrame[FRAME_LEN_OFFSET] - EID_HEADER_LEN;
sarahmarshy 0:1c7da5f83647 76 }
sarahmarshy 0:1c7da5f83647 77
sarahmarshy 0:1c7da5f83647 78 void EIDFrame::setAdvTxPower(uint8_t* rawFrame, int8_t advTxPower)
sarahmarshy 0:1c7da5f83647 79 {
sarahmarshy 0:1c7da5f83647 80 rawFrame[EID_TXPOWER_OFFSET] = advTxPower;
sarahmarshy 0:1c7da5f83647 81 }
sarahmarshy 0:1c7da5f83647 82
sarahmarshy 0:1c7da5f83647 83 // Mote: This is only called after the rotation period is due, or on writing/creating a new eidIdentityKey
sarahmarshy 0:1c7da5f83647 84 void EIDFrame::update(uint8_t* rawFrame, uint8_t* eidIdentityKey, uint8_t rotationPeriodExp, uint32_t timeSecs)
sarahmarshy 0:1c7da5f83647 85 {
sarahmarshy 0:1c7da5f83647 86 // Calculate the temporary key datastructure 1
sarahmarshy 0:1c7da5f83647 87 uint8_t ts[4]; // big endian representation of time
sarahmarshy 0:1c7da5f83647 88 ts[0] = (timeSecs >> 24) & 0xff;
sarahmarshy 0:1c7da5f83647 89 ts[1] = (timeSecs >> 16) & 0xff;
sarahmarshy 0:1c7da5f83647 90
sarahmarshy 0:1c7da5f83647 91 uint8_t tmpEidDS1[16] = { 0,0,0,0,0,0,0,0,0,0,0, SALT, 0, 0, ts[0], ts[1] };
sarahmarshy 0:1c7da5f83647 92
sarahmarshy 0:1c7da5f83647 93 // Perform the aes encryption to generate the final temporary key.
sarahmarshy 0:1c7da5f83647 94 uint8_t tmpKey[16];
sarahmarshy 0:1c7da5f83647 95 aes128Encrypt(eidIdentityKey, tmpEidDS1, tmpKey);
sarahmarshy 0:1c7da5f83647 96
sarahmarshy 0:1c7da5f83647 97 // Compute the EID
sarahmarshy 0:1c7da5f83647 98 uint8_t eid[16];
sarahmarshy 0:1c7da5f83647 99 uint32_t scaledTime = (timeSecs >> rotationPeriodExp) << rotationPeriodExp;
sarahmarshy 0:1c7da5f83647 100 ts[0] = (scaledTime >> 24) & 0xff;
sarahmarshy 0:1c7da5f83647 101 ts[1] = (scaledTime >> 16) & 0xff;
sarahmarshy 0:1c7da5f83647 102 ts[2] = (scaledTime >> 8) & 0xff;
sarahmarshy 0:1c7da5f83647 103 ts[3] = scaledTime & 0xff;
sarahmarshy 0:1c7da5f83647 104 uint8_t tmpEidDS2[16] = { 0,0,0,0,0,0,0,0,0,0,0, rotationPeriodExp, ts[0], ts[1], ts[2], ts[3] };
sarahmarshy 0:1c7da5f83647 105 aes128Encrypt(tmpKey, tmpEidDS2, eid);
sarahmarshy 0:1c7da5f83647 106
sarahmarshy 0:1c7da5f83647 107 // copy the leading 8 bytes of the eid result (full result length = 16) into the ADV frame
sarahmarshy 0:1c7da5f83647 108 memcpy(rawFrame + 5, eid, EID_LENGTH);
sarahmarshy 0:1c7da5f83647 109
sarahmarshy 0:1c7da5f83647 110 }
sarahmarshy 0:1c7da5f83647 111
sarahmarshy 0:1c7da5f83647 112 /** AES128 encrypts a 16-byte input array with a key, resulting in a 16-byte output array */
sarahmarshy 0:1c7da5f83647 113 void EIDFrame::aes128Encrypt(uint8_t key[], uint8_t input[], uint8_t output[]) {
sarahmarshy 0:1c7da5f83647 114 mbedtls_aes_context ctx;
sarahmarshy 0:1c7da5f83647 115 mbedtls_aes_init(&ctx);
sarahmarshy 0:1c7da5f83647 116 mbedtls_aes_setkey_enc(&ctx, key, 8 * sizeof(Lock_t));
sarahmarshy 0:1c7da5f83647 117 mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, input, output);
sarahmarshy 0:1c7da5f83647 118 mbedtls_aes_free(&ctx);
sarahmarshy 0:1c7da5f83647 119 }
sarahmarshy 0:1c7da5f83647 120
sarahmarshy 0:1c7da5f83647 121 int EIDFrame::genBeaconKeys(PrivateEcdhKey_t beaconPrivateEcdhKey, PublicEcdhKey_t beaconPublicEcdhKey) {
sarahmarshy 0:1c7da5f83647 122 mbedtls_ecdh_init( &ecdh_ctx );
sarahmarshy 0:1c7da5f83647 123
sarahmarshy 0:1c7da5f83647 124 int i = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0);
sarahmarshy 0:1c7da5f83647 125 if (i != 0) {
sarahmarshy 0:1c7da5f83647 126 return i; // return EID_RND_FAIL;
sarahmarshy 0:1c7da5f83647 127 }
sarahmarshy 0:1c7da5f83647 128
sarahmarshy 0:1c7da5f83647 129 if (mbedtls_ecp_group_load(&ecdh_ctx.grp, MBEDTLS_ECP_DP_CURVE25519) != 0) {
sarahmarshy 0:1c7da5f83647 130 return EID_GRP_FAIL;
sarahmarshy 0:1c7da5f83647 131 }
sarahmarshy 0:1c7da5f83647 132 if (mbedtls_ecdh_gen_public(&ecdh_ctx.grp, &ecdh_ctx.d, &ecdh_ctx.Q, mbedtls_ctr_drbg_random, &ctr_drbg) != 0) {
sarahmarshy 0:1c7da5f83647 133 return EID_GENKEY_FAIL;
sarahmarshy 0:1c7da5f83647 134 }
sarahmarshy 0:1c7da5f83647 135
sarahmarshy 0:1c7da5f83647 136 mbedtls_mpi_write_binary(&ecdh_ctx.d, beaconPrivateEcdhKey, sizeof(PrivateEcdhKey_t));
sarahmarshy 0:1c7da5f83647 137 mbedtls_mpi_write_binary(&ecdh_ctx.Q.X, beaconPublicEcdhKey, sizeof(PublicEcdhKey_t));
sarahmarshy 0:1c7da5f83647 138
sarahmarshy 0:1c7da5f83647 139 mbedtls_ecdh_free( &ecdh_ctx );
sarahmarshy 0:1c7da5f83647 140 return EID_SUCCESS;
sarahmarshy 0:1c7da5f83647 141 }
sarahmarshy 0:1c7da5f83647 142
sarahmarshy 0:1c7da5f83647 143 int EIDFrame::genEcdhSharedKey(PrivateEcdhKey_t beaconPrivateEcdhKey, PublicEcdhKey_t beaconPublicEcdhKey, PublicEcdhKey_t serverPublicEcdhKey, EidIdentityKey_t eidIdentityKey) {
sarahmarshy 0:1c7da5f83647 144 int16_t ret = 0;
sarahmarshy 0:1c7da5f83647 145 uint8_t tmp[32];
sarahmarshy 0:1c7da5f83647 146 // initialize context
sarahmarshy 0:1c7da5f83647 147 mbedtls_ecdh_init( &ecdh_ctx );
sarahmarshy 0:1c7da5f83647 148 mbedtls_ecp_group_load( &ecdh_ctx.grp, MBEDTLS_ECP_DP_CURVE25519 );
sarahmarshy 0:1c7da5f83647 149
sarahmarshy 0:1c7da5f83647 150 // copy binary beacon private key (previously generated!) into context
sarahmarshy 0:1c7da5f83647 151 // Note: As the PrivateKey is generated locally, it is Big Endian
sarahmarshy 0:1c7da5f83647 152 ret = mbedtls_mpi_read_binary( &ecdh_ctx.d, beaconPrivateEcdhKey, sizeof(PrivateEcdhKey_t) );
sarahmarshy 0:1c7da5f83647 153
sarahmarshy 0:1c7da5f83647 154 // copy server-public-key (received through GATT characteristic 10) into context
sarahmarshy 0:1c7da5f83647 155 ret = mbedtls_mpi_lset( &ecdh_ctx.Qp.Z, 1 );
sarahmarshy 0:1c7da5f83647 156 EddystoneService::swapEndianArray(serverPublicEcdhKey, tmp, 32); // To make it Big Endian
sarahmarshy 0:1c7da5f83647 157 ret = mbedtls_mpi_read_binary( &ecdh_ctx.Qp.X, tmp , sizeof(PublicEcdhKey_t) );
sarahmarshy 0:1c7da5f83647 158
sarahmarshy 0:1c7da5f83647 159 // ECDH point multiplication
sarahmarshy 0:1c7da5f83647 160 size_t olen; // actual size of shared secret
sarahmarshy 0:1c7da5f83647 161 uint8_t sharedSecret[32]; // shared ECDH secret
sarahmarshy 0:1c7da5f83647 162 memset(sharedSecret, 0, 32);
sarahmarshy 0:1c7da5f83647 163 ret = mbedtls_ecdh_calc_secret( &ecdh_ctx, &olen, sharedSecret, sizeof(sharedSecret), NULL, NULL );
sarahmarshy 0:1c7da5f83647 164 LOG(("size of olen= %d ret=%x\r\n", olen, ret));
sarahmarshy 0:1c7da5f83647 165 EddystoneService::swapEndianArray(sharedSecret, tmp, 32);
sarahmarshy 0:1c7da5f83647 166 memcpy(sharedSecret, tmp, 32);
sarahmarshy 0:1c7da5f83647 167 LOG(("Shared secret=")); EddystoneService::logPrintHex(sharedSecret, 32);
sarahmarshy 0:1c7da5f83647 168 if (olen != sizeof(sharedSecret)) {
sarahmarshy 0:1c7da5f83647 169 return EID_GENKEY_FAIL;
sarahmarshy 0:1c7da5f83647 170 }
sarahmarshy 0:1c7da5f83647 171 if (ret == MBEDTLS_ERR_ECP_BAD_INPUT_DATA) {
sarahmarshy 0:1c7da5f83647 172 return EID_RC_SS_IS_ZERO;
sarahmarshy 0:1c7da5f83647 173 }
sarahmarshy 0:1c7da5f83647 174
sarahmarshy 0:1c7da5f83647 175 // Convert the shared secret to key material using HKDF-SHA256. HKDF is used with
sarahmarshy 0:1c7da5f83647 176 // the salt set to a concatenation of the resolver's public key and beacon's
sarahmarshy 0:1c7da5f83647 177 // public key, with a null context.
sarahmarshy 0:1c7da5f83647 178
sarahmarshy 0:1c7da5f83647 179 // build HKDF key
sarahmarshy 0:1c7da5f83647 180 unsigned char k[ 64 ];
sarahmarshy 0:1c7da5f83647 181 EddystoneService::swapEndianArray(beaconPublicEcdhKey, tmp, 32);
sarahmarshy 0:1c7da5f83647 182 memcpy( &k[0], serverPublicEcdhKey, sizeof(PublicEcdhKey_t) );
sarahmarshy 0:1c7da5f83647 183 memcpy( &k[32], tmp, sizeof(PublicEcdhKey_t) );
sarahmarshy 0:1c7da5f83647 184
sarahmarshy 0:1c7da5f83647 185 // compute HKDF: see https://tools.ietf.org/html/rfc5869
sarahmarshy 0:1c7da5f83647 186 // mbedtls_md_context_t md_ctx;
sarahmarshy 0:1c7da5f83647 187 mbedtls_md_init( &md_ctx );
sarahmarshy 0:1c7da5f83647 188 mbedtls_md_setup( &md_ctx, mbedtls_md_info_from_type( MBEDTLS_MD_SHA256 ), 1 );
sarahmarshy 0:1c7da5f83647 189 mbedtls_md_hmac_starts( &md_ctx, k, sizeof( k ) );
sarahmarshy 0:1c7da5f83647 190 mbedtls_md_hmac_update( &md_ctx, sharedSecret, sizeof(sharedSecret) );
sarahmarshy 0:1c7da5f83647 191 unsigned char prk[ 32 ];
sarahmarshy 0:1c7da5f83647 192 mbedtls_md_hmac_finish( &md_ctx, prk );
sarahmarshy 0:1c7da5f83647 193 mbedtls_md_hmac_starts( &md_ctx, prk, sizeof( prk ) );
sarahmarshy 0:1c7da5f83647 194 const unsigned char const1[] = { 0x01 };
sarahmarshy 0:1c7da5f83647 195 mbedtls_md_hmac_update( &md_ctx, const1, sizeof( const1 ) );
sarahmarshy 0:1c7da5f83647 196 unsigned char t[ 32 ];
sarahmarshy 0:1c7da5f83647 197 mbedtls_md_hmac_finish( &md_ctx, t );
sarahmarshy 0:1c7da5f83647 198
sarahmarshy 0:1c7da5f83647 199 //Truncate the key material to 16 bytes (128 bits) to convert it to an AES-128 secret key.
sarahmarshy 0:1c7da5f83647 200 memcpy( eidIdentityKey, t, sizeof(EidIdentityKey_t) );
sarahmarshy 0:1c7da5f83647 201 LOG(("\r\nEIDIdentityKey=")); EddystoneService::logPrintHex(t, 32); LOG(("\r\n"));
sarahmarshy 0:1c7da5f83647 202
sarahmarshy 0:1c7da5f83647 203 mbedtls_md_free( &md_ctx );
sarahmarshy 0:1c7da5f83647 204 mbedtls_ecdh_free( &ecdh_ctx );
sarahmarshy 0:1c7da5f83647 205 return EID_SUCCESS;
sarahmarshy 0:1c7da5f83647 206 }