Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
source/EIDFrame.cpp@0:1c7da5f83647, 2016-11-29 (annotated)
- Committer:
- sarahmarshy
- Date:
- Tue Nov 29 06:29:10 2016 +0000
- Revision:
- 0:1c7da5f83647
Initial commit
Who changed what in which revision?
| User | Revision | Line number | New 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 | } |