Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ADU capabilities to feature branch #27

Merged
merged 39 commits into from
Oct 25, 2022
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
afc82be
add adu capability
danewalton Sep 12, 2022
afa56f3
add gate build for adu
danewalton Sep 12, 2022
94480fb
further updates to verify image hash
danewalton Sep 13, 2022
af8c9e2
add verification
danewalton Sep 13, 2022
1b313e8
add cancellation check
danewalton Sep 13, 2022
3ad087f
clang format
danewalton Sep 13, 2022
d03ce82
clang format aligned with emb c
danewalton Sep 13, 2022
ed05fbf
add feature branch
danewalton Sep 13, 2022
b5cdf70
update value names
danewalton Sep 13, 2022
12cdc49
code format
danewalton Sep 13, 2022
f8cfe77
remove todo
danewalton Sep 15, 2022
4551fa0
consolidate result
danewalton Sep 15, 2022
7718268
remove unneeded mbedtls crypto
danewalton Sep 15, 2022
584b3a8
remove header include
danewalton Sep 15, 2022
56e7391
delete pull ota script
danewalton Sep 16, 2022
93c6056
update config
danewalton Sep 16, 2022
3c33554
wrong config
danewalton Sep 16, 2022
dad13b4
refine JWS
danewalton Sep 16, 2022
f835d58
update docs
danewalton Sep 16, 2022
c393c7e
more docs
danewalton Sep 16, 2022
ecc93f2
more updates
danewalton Sep 16, 2022
2e9d6cd
add version output to binary
danewalton Sep 16, 2022
1233699
fix build
danewalton Sep 16, 2022
aeb16b4
add update check
danewalton Sep 16, 2022
20b2ec5
clang format
danewalton Sep 16, 2022
c6cf655
update library
danewalton Sep 19, 2022
370a728
Merge branch 'ota' of https://github.com/Azure/azure-sdk-for-c-arduin…
danewalton Sep 19, 2022
49fa8d1
update sample
danewalton Sep 19, 2022
09364b0
update config
danewalton Sep 19, 2022
c7bda32
update embedded SDK
danewalton Sep 23, 2022
30205c9
update usage
danewalton Sep 23, 2022
5036a78
pr comments
danewalton Sep 27, 2022
200f211
update lib and use model id
danewalton Sep 30, 2022
f70f775
udpate adu code
danewalton Oct 20, 2022
ffa4330
update to use base64 url decode
danewalton Oct 20, 2022
57d7c72
update
danewalton Oct 20, 2022
819b213
handle cancel | send in progress
danewalton Oct 21, 2022
3355188
doc updates
danewalton Oct 25, 2022
ba2f75a
update docs
danewalton Oct 25, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
branches: [ main, feature/* ]

workflow_dispatch:

Expand All @@ -31,3 +31,6 @@ jobs:

- name: Build Azure_IoT_Central_ESP32
run: arduino --verify --board esp32:esp32:esp32 -v --preserve-temp-files $GITHUB_WORKSPACE/examples/Azure_IoT_Central_ESP32/Azure_IoT_Central_ESP32.ino

- name: Build Azure_IoT_Adu_ESP32
run: arduino --verify --board esp32:esp32:esp32 -v --preserve-temp-files $GITHUB_WORKSPACE/examples/Azure_IoT_Adu_ESP32/Azure_IoT_Adu_ESP32.ino
49 changes: 49 additions & 0 deletions examples/Azure_IoT_Adu_ESP32/AzIoTCryptoMbedTLS.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License. */

#include "AzIoTCryptoMbedTLS.h"

/* mbed TLS includes. */
#include "mbedtls/md.h"
#include "mbedtls/threading.h"

/*-----------------------------------------------------------*/

int AzIoTCryptoMbedTLS::HMAC256(
const uint8_t* pucKey,
int ulKeyLength,
const uint8_t* pucData,
int ulDataLength,
uint8_t* pucOutput,
int ulOutputLength,
int* pulBytesCopied)
{
int ulRet;
mbedtls_md_context_t xCtx;
mbedtls_md_type_t xMDType = MBEDTLS_MD_SHA256;

if (ulOutputLength < 32)
{
return 1;
}

mbedtls_md_init(&xCtx);

if (mbedtls_md_setup(&xCtx, mbedtls_md_info_from_type(xMDType), 1)
|| mbedtls_md_hmac_starts(&xCtx, pucKey, ulKeyLength)
|| mbedtls_md_hmac_update(&xCtx, pucData, ulDataLength)
|| mbedtls_md_hmac_finish(&xCtx, pucOutput))
{
ulRet = 1;
}
else
{
ulRet = 0;
*pulBytesCopied = 32;
}

mbedtls_md_free(&xCtx);

return ulRet;
}
/*-----------------------------------------------------------*/
33 changes: 33 additions & 0 deletions examples/Azure_IoT_Adu_ESP32/AzIoTCryptoMbedTLS.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License. */

#ifndef AZIOTCRYPTOMBEDTLS_H
#define AZIOTCRYPTOMBEDTLS_H

#include <stdint.h>

namespace AzIoTCryptoMbedTLS
{
/**
* @brief Compute HMAC SHA256
*
* @param[in] pucKey Pointer to key.
* @param[in] ulKeyLength Length of Key.
* @param[in] pucData Pointer to data for HMAC
* @param[in] ulDataLength Length of data.
* @param[in,out] pucOutput Buffer to place computed HMAC.
* @param[out] ulOutputLength Length of output buffer.
* @param[in] pulBytesCopied Number of bytes copied to out buffer.
* @return An #uint32_t with result of operation.
*/
int HMAC256(
const uint8_t* pucKey,
danewalton marked this conversation as resolved.
Show resolved Hide resolved
int ulKeyLength,
const uint8_t* pucData,
int ulDataLength,
uint8_t* pucOutput,
int ulOutputLength,
int* pulBytesCopied);
}; // namespace AzIoTCryptoMbedTLS

#endif // AZIOTCRYPTOMBEDTLS_H
288 changes: 288 additions & 0 deletions examples/Azure_IoT_Adu_ESP32/AzIoTSasToken.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT

#include "AzIoTSasToken.h"
#include "SerialLogger.h"
#include <az_result.h>
#include <mbedtls/base64.h>
#include <mbedtls/md.h>
#include <mbedtls/sha256.h>
#include <stdlib.h>
#include <time.h>

#define INDEFINITE_TIME ((time_t)-1)

#define az_span_is_content_equal(x, AZ_SPAN_EMPTY) \
(az_span_size(x) == az_span_size(AZ_SPAN_EMPTY) && az_span_ptr(x) == az_span_ptr(AZ_SPAN_EMPTY))

static uint32_t getSasTokenExpiration(const char* sasToken)
{
const char SE[] = { '&', 's', 'e', '=' };
uint32_t se_as_unix_time = 0;

int i, j;
for (i = 0, j = 0; sasToken[i] != '\0'; i++)
{
if (sasToken[i] == SE[j])
{
j++;
if (j == sizeof(SE))
{
// i is still at the '=' position. We must advance it by 1.
i++;
break;
}
}
else
{
j = 0;
}
}

if (j != sizeof(SE))
{
Logger.Error("Failed finding `se` field in SAS token");
}
else
{
int k = i;
while (sasToken[k] != '\0' && sasToken[k] != '&')
{
k++;
}

if (az_result_failed(
az_span_atou32(az_span_create((uint8_t*)sasToken + i, k - i), &se_as_unix_time)))
{
Logger.Error("Failed parsing SAS token expiration timestamp");
}
}

return se_as_unix_time;
}

static void mbedtls_hmac_sha256(az_span key, az_span payload, az_span signed_payload)
{
mbedtls_md_context_t ctx;
mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;

mbedtls_md_init(&ctx);
mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1);
mbedtls_md_hmac_starts(&ctx, (const unsigned char*)az_span_ptr(key), az_span_size(key));
mbedtls_md_hmac_update(&ctx, (const unsigned char*)az_span_ptr(payload), az_span_size(payload));
mbedtls_md_hmac_finish(&ctx, (byte*)az_span_ptr(signed_payload));
mbedtls_md_free(&ctx);
}

static void hmac_sha256_sign_signature(
az_span decoded_key,
az_span signature,
az_span signed_signature,
az_span* out_signed_signature)
{
mbedtls_hmac_sha256(decoded_key, signature, signed_signature);
*out_signed_signature = az_span_slice(signed_signature, 0, 32);
}

static void base64_encode_bytes(
az_span decoded_bytes,
az_span base64_encoded_bytes,
az_span* out_base64_encoded_bytes)
{
size_t len;
if (mbedtls_base64_encode(
az_span_ptr(base64_encoded_bytes),
(size_t)az_span_size(base64_encoded_bytes),
&len,
az_span_ptr(decoded_bytes),
(size_t)az_span_size(decoded_bytes))
!= 0)
{
Logger.Error("mbedtls_base64_encode fail");
}

*out_base64_encoded_bytes = az_span_create(az_span_ptr(base64_encoded_bytes), (int32_t)len);
}

static int decode_base64_bytes(
az_span base64_encoded_bytes,
az_span decoded_bytes,
az_span* out_decoded_bytes)
{
memset(az_span_ptr(decoded_bytes), 0, (size_t)az_span_size(decoded_bytes));

size_t len;
if (mbedtls_base64_decode(
az_span_ptr(decoded_bytes),
(size_t)az_span_size(decoded_bytes),
&len,
az_span_ptr(base64_encoded_bytes),
(size_t)az_span_size(base64_encoded_bytes))
!= 0)
{
Logger.Error("mbedtls_base64_decode fail");
return 1;
}
else
{
*out_decoded_bytes = az_span_create(az_span_ptr(decoded_bytes), (int32_t)len);
return 0;
}
}

static int iot_sample_generate_sas_base64_encoded_signed_signature(
az_span sas_base64_encoded_key,
az_span sas_signature,
az_span sas_base64_encoded_signed_signature,
az_span* out_sas_base64_encoded_signed_signature)
{
// Decode the sas base64 encoded key to use for HMAC signing.
char sas_decoded_key_buffer[32];
az_span sas_decoded_key = AZ_SPAN_FROM_BUFFER(sas_decoded_key_buffer);

if (decode_base64_bytes(sas_base64_encoded_key, sas_decoded_key, &sas_decoded_key) != 0)
{
Logger.Error("Failed generating encoded signed signature");
return 1;
}

// HMAC-SHA256 sign the signature with the decoded key.
char sas_hmac256_signed_signature_buffer[32];
az_span sas_hmac256_signed_signature = AZ_SPAN_FROM_BUFFER(sas_hmac256_signed_signature_buffer);
hmac_sha256_sign_signature(
sas_decoded_key, sas_signature, sas_hmac256_signed_signature, &sas_hmac256_signed_signature);

// Base64 encode the result of the HMAC signing.
base64_encode_bytes(
sas_hmac256_signed_signature,
sas_base64_encoded_signed_signature,
out_sas_base64_encoded_signed_signature);

return 0;
}

int64_t iot_sample_get_epoch_expiration_time_from_minutes(uint32_t minutes)
{
time_t now = time(NULL);
return (int64_t)(now + minutes * 60);
}

az_span generate_sas_token(
az_iot_hub_client* hub_client,
az_span device_key,
az_span sas_signature,
unsigned int expiryTimeInMinutes,
az_span sas_token)
{
az_result rc;
// Create the POSIX expiration time from input minutes.
uint64_t sas_duration = iot_sample_get_epoch_expiration_time_from_minutes(expiryTimeInMinutes);

// Get the signature that will later be signed with the decoded key.
// az_span sas_signature = AZ_SPAN_FROM_BUFFER(signature);
rc = az_iot_hub_client_sas_get_signature(hub_client, sas_duration, sas_signature, &sas_signature);
if (az_result_failed(rc))
{
Logger.Error("Could not get the signature for SAS key: az_result return code " + rc);
return AZ_SPAN_EMPTY;
}

// Generate the encoded, signed signature (b64 encoded, HMAC-SHA256 signing).
char b64enc_hmacsha256_signature[64];
az_span sas_base64_encoded_signed_signature = AZ_SPAN_FROM_BUFFER(b64enc_hmacsha256_signature);

if (iot_sample_generate_sas_base64_encoded_signed_signature(
device_key,
sas_signature,
sas_base64_encoded_signed_signature,
&sas_base64_encoded_signed_signature)
!= 0)
{
Logger.Error("Failed generating SAS token signed signature");
return AZ_SPAN_EMPTY;
}

// Get the resulting MQTT password, passing the base64 encoded, HMAC signed
// bytes.
size_t mqtt_password_length;
rc = az_iot_hub_client_sas_get_password(
hub_client,
sas_duration,
sas_base64_encoded_signed_signature,
AZ_SPAN_EMPTY,
(char*)az_span_ptr(sas_token),
az_span_size(sas_token),
&mqtt_password_length);

if (az_result_failed(rc))
{
Logger.Error("Could not get the password: az_result return code " + rc);
return AZ_SPAN_EMPTY;
}
else
{
return az_span_slice(sas_token, 0, mqtt_password_length);
}
}

AzIoTSasToken::AzIoTSasToken(
az_iot_hub_client* client,
az_span deviceKey,
az_span signatureBuffer,
az_span sasTokenBuffer)
{
this->client = client;
this->deviceKey = deviceKey;
this->signatureBuffer = signatureBuffer;
this->sasTokenBuffer = sasTokenBuffer;
this->expirationUnixTime = 0;
this->sasToken = AZ_SPAN_EMPTY;
}

int AzIoTSasToken::Generate(unsigned int expiryTimeInMinutes)
{
this->sasToken = generate_sas_token(
this->client,
this->deviceKey,
this->signatureBuffer,
expiryTimeInMinutes,
this->sasTokenBuffer);

if (az_span_is_content_equal(this->sasToken, AZ_SPAN_EMPTY))
{
Logger.Error("Failed generating SAS token");
return 1;
}
else
{
this->expirationUnixTime = getSasTokenExpiration((const char*)az_span_ptr(this->sasToken));

if (this->expirationUnixTime == 0)
{
Logger.Error("Failed getting the SAS token expiration time");
this->sasToken = AZ_SPAN_EMPTY;
return 1;
}
else
{
return 0;
}
}
}

bool AzIoTSasToken::IsExpired()
{
time_t now = time(NULL);

if (now == INDEFINITE_TIME)
{
Logger.Error("Failed getting current time");
return true;
}
else
{
return (now >= this->expirationUnixTime);
}
}

az_span AzIoTSasToken::Get() { return this->sasToken; }
Loading