Porting Arduino library to Particle Photon


#1

I know this question has been raised a couple times, but I thought I would take a chance.

I am trying to port the thinger Arduino Yun library to a Particle using the Yun example program.

First, I created a program with the Arduino Yun example. I confirmed that the BridgeSSLClient was essentially the same as the TlsTcpClient all methods in the BridgeSSLClient existed in the TlsTcpClient class.

I then brought in each thinger library file (thinger.h, thinger_io, thingerclient, …) and made sure the #include statements were correct.

I then changed “ThingerYun” to “ThingerParticle”. In ThingerParticle, I have this definition:

class ThingerParticle : public ThingerClient {
public:
ThingerParticle(const char* user, const char* device, const char* device_credential) :
ThingerClient(client_, user, device, device_credential)
{}
~ThingerParticle(){}
private:
#ifndef DISABLE_TLS
TlsTcpClient client_;
#else
HttpClient client_;
#endif
};

However, when I try to compile, I receive this error:

ThingerParticle.h:32:67: no matching function for call to ‘ThingerClient::ThingerClient(TlsTcpClient&, const char*&, const char*&, const char*&)’

ThingerClient is not modified. It appears to contain the correct definition:

class ThingerClient : public thinger::thinger {
public:
ThingerClient(Client& client, const char* user, const char* device, const char* device_credential) :
client_(client), username_(user), device_id_(device), device_password_(device_credential)
{}

Any suggestions?

It would be awesome to get the library working on the Photon!!


#2

Hi,

do you have the source code for the TlsTcpClient ? ThingerClient is expecting a class that inherits from the base class Client.


#3

Thank you for looking at this!

Here’s TlsTcpClient.cpp:

#include “TlsTcpClient.h”

TlsTcpClient::TlsTcpClient() {
connected = false;
}

int TlsTcpClient::send_Tls(void *ctx, const unsigned char *buf, size_t len) {
TlsTcpClient *sock = (TlsTcpClient *)ctx;

if (!sock->client.connected()) {
return -1;
}

int ret = sock->client.write(buf, len);
if (ret == 0) {
return MBEDTLS_ERR_SSL_WANT_WRITE;
}
sock->client.flush();
return ret;
}

int TlsTcpClient::recv_Tls(void *ctx, unsigned char *buf, size_t len) {
TlsTcpClient *sock = (TlsTcpClient *)ctx;
// TODO: need this delay?
delay(10);

if (!sock->client.connected()) {
return -1;
}

if (sock->client.available() == 0) {
return MBEDTLS_ERR_SSL_WANT_READ;
}

int ret = sock->client.read(buf, len);
if (ret == 0) {
return MBEDTLS_ERR_SSL_WANT_READ;
}
return ret;
}

int TlsTcpClient::rng_Tls(void* handle, uint8_t* data, const size_t len_) {
size_t len = len_;
while (len>=4) {
((uint32_t)data) = HAL_RNG_GetRandomNumber();
data += 4;
len -= 4;
}
while (len–>0) {
*data++ = HAL_RNG_GetRandomNumber();
}
return 0;
}

void TlsTcpClient::debug_Tls( void *ctx, int level,
const char *file, int line,
const char *str ) {
((void) level);
debug_tls("%s:%04d: %s", file, line, str);
}

time_t TlsTcpClient::particle_Time(time_t* ttime) {
time_t nowt = Time.now();

*ttime = nowt;
return nowt;
}

int TlsTcpClient::veryfyCert_Tls(void *data, mbedtls_x509_crt *crt, int depth, uint32_t *flags) {
char buf[1024];
((void) data);

debug_tls(“Verify requested for (Depth %d):\n”, depth);
mbedtls_x509_crt_info(buf, sizeof(buf) - 1, “”, crt);
debug_tls("%s", buf);

if((*flags) == 0) {
debug_tls(" This certificate has no flags\n");
} else {
debug_tls(buf, sizeof(buf), " ! “, *flags);
debug_tls(”%s\n", buf);
}
return 0;
}

int TlsTcpClient::init(const char *rootCaPem, const size_t rootCaPemSize) {
return this->init(rootCaPem, rootCaPemSize, NULL, 0, NULL, 0);
}

int TlsTcpClient::init(const char *rootCaPem, const size_t rootCaPemSize,
const char *clientCertPem, const size_t clientCertPemSize,
const char *clientKeyPem, const size_t clientKeyPemSize) {

int ret;
connected = false;
mbedtls_ssl_config_init(&conf);
mbedtls_ssl_init(&ssl);
mbedtls_x509_crt_init(&cacert);
mbedtls_x509_crt_init(&clicert);
mbedtls_pk_init(&pkey);

mbedtls_ssl_conf_dbg(&conf, &TlsTcpClient::debug_Tls, nullptr);
#if defined(MBEDTLS_DEBUG_C)
mbedtls_debug_set_threshold(DEBUG_TLS_CORE_LEVEL);
#endif

if ((ret = mbedtls_x509_crt_parse(&cacert, (const unsigned char *)rootCaPem, rootCaPemSize)) < 0) {
debug_tls(" enableTls mbedtls_x509_crt_parse error : %d\n", ret);
return ret;
}

if (clientCertPem != NULL && clientCertPemSize > 0) {
if ((ret = mbedtls_x509_crt_parse(&clicert, (const unsigned char *)clientCertPem, clientCertPemSize)) < 0) {
debug_tls(" tlsClientKey mbedtls_x509_crt_parse error : %d\n", ret);
return ret;
}
}

if (clientKeyPem != NULL && clientKeyPemSize > 0) {
if ((ret = mbedtls_pk_parse_key(&pkey, (const unsigned char *)clientKeyPem, clientKeyPemSize, NULL, 0)) != 0) {
debug_tls(" tlsClientKey mbedtls_pk_parse_key error : %d\n", ret);
return ret;
}
}

if ((ret = mbedtls_ssl_config_defaults(&conf, MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
return ret;
}
mbedtls_ssl_conf_min_version(&conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3);
mbedtls_ssl_conf_verify(&conf, &TlsTcpClient::veryfyCert_Tls, NULL);

// if server certificates is not valid, connection will success. check certificates on verify() function.
mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
mbedtls_ssl_conf_rng(&conf, &TlsTcpClient::rng_Tls, nullptr);
mbedtls_ssl_conf_ca_chain(&conf, &cacert, nullptr);

if (clientCertPem != NULL && clientKeyPem != NULL) {
mbedtls_ssl_conf_own_cert(&conf, &clicert, &pkey);
}

if((ret = mbedtls_ssl_setup(&ssl, &conf)) != 0) {
return ret;
}

mbedtls_ssl_set_timer_cb(&ssl, &timer, mbedtls_timing_set_delay, mbedtls_timing_get_delay);
mbedtls_ssl_set_bio(&ssl, this, &TlsTcpClient::send_Tls, &TlsTcpClient::recv_Tls, nullptr);
return 0;
}

void TlsTcpClient::close() {
connected = false;
mbedtls_x509_crt_free(&cacert);
mbedtls_x509_crt_free(&clicert);
mbedtls_pk_free(&pkey);
mbedtls_ssl_config_free (&conf);
mbedtls_ssl_free (&ssl);
client.stop();
};

int TlsTcpClient::connect(char* domain, uint16_t port) {
int ret;
if (!client.connect(domain, port)) {
debug_tls(" could not connect to server : %s\n", domain);
return -1;
}

if((ret = mbedtls_ssl_set_hostname(&ssl, domain)) != 0) {
return ret;
}

return this->handShake();
}

int TlsTcpClient::connect(uint8_t *ip, uint16_t port) {
int ret;
char buffer[16];
sprintf(buffer, “%d.%d.%d.%d”, ip[0], ip[1], ip[2], ip[3]);

if (!client.connect(ip, port)) {
return -1;
}

if((ret = mbedtls_ssl_set_hostname(&ssl, buffer)) != 0) {
return ret;
}

return this->handShake();
}

int TlsTcpClient::handShake() {
int ret;
do {
while (ssl.state != MBEDTLS_SSL_HANDSHAKE_OVER) {
ret = mbedtls_ssl_handshake_client_step(&ssl);
if (ret != 0)
break;
}
} while(ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE);

// clean ca cert/crt/pkey
mbedtls_x509_crt_free(&cacert);
mbedtls_x509_crt_free(&clicert);
mbedtls_pk_free(&pkey);

if (ssl.state == MBEDTLS_SSL_HANDSHAKE_OVER) {
connected = true;
return 0;
}
return ret;
}

int TlsTcpClient::write(unsigned char *buff, int length) {
if (connected) {
int ret = mbedtls_ssl_write( &ssl, buff, length );
return ret;
} else
return -1;
}

int TlsTcpClient::read() {
unsigned char buff[1];
int ret = read(buff, 1);
if (ret == 1) return buff[0];
else return ret;
}

int TlsTcpClient::read(unsigned char *buff, int length) {
if (connected) {
int ret = mbedtls_ssl_read(&ssl, buff, length);
if (ret < 0) {
switch (ret) {
case MBEDTLS_ERR_SSL_WANT_READ:
break;
case MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE:
ret = 0;
break;
case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY:
default:
close();
return -1;
}
}
return ret;
} else
return -1;
}

int TlsTcpClient::available() {
return client.available();
}

bool TlsTcpClient::isConnected() {
if (client.connected())
return connected;
return false;
}

bool TlsTcpClient::verify() {
int ret;
if ((ret = mbedtls_ssl_get_verify_result(&ssl)) != 0 ) {
char vrfy_buf[512];
mbedtls_x509_crt_verify_info( vrfy_buf, sizeof( vrfy_buf ), " ! “, ret );
debug_tls(”%s\n", vrfy_buf);
return false;
}
return true;
}


#4

Actually, the header file is probably all we need. Here’s TlsTcpClient.h:

#ifndef MBED_TLS_TCP_CLIENT_h
#define MBED_TLS_TCP_CLIENT_h

#include “application.h”
#include “mbedtls/check_config.h”

#include “mbedtls/net.h”
#include “mbedtls/debug.h”
#include “mbedtls/ssl.h”
#include “mbedtls/entropy.h”
#include “mbedtls/ctr_drbg.h”
#include “mbedtls/error.h”
#include “mbedtls/timing.h”
#include “mbedtls/ssl_internal.h”
#include “timer_hal.h”

// for debugging.
#define DEBUG_TLS 0
#if defined(MBEDTLS_DEBUG_C)
#define DEBUG_TLS_CORE_LEVEL 3
#define debug_tls( fmt, … )
Serial.printf(fmt, ##VA_ARGS)
#else /* !DEBUG_TLS /
#define debug_tls( fmt, … ) ((void)0)
#endif /
DEBUG_TLS */

class TlsTcpClient {

private:
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_ssl_context ssl;
mbedtls_ssl_config conf;
mbedtls_x509_crt cacert;
mbedtls_x509_crt clicert;
mbedtls_pk_context pkey;
mbedtls_timing_delay_context timer;

TCPClient client;
bool connected;

static int send_Tls(void *ctx, const unsigned char *buf, size_t len);
static int recv_Tls(void *ctx, unsigned char *buf, size_t len);
static int rng_Tls(void* handle, uint8_t* data, const size_t len_);
static int veryfyCert_Tls(void *data, mbedtls_x509_crt *crt, int depth, uint32_t *flags);
int handShake();

public:
TlsTcpClient();
~TlsTcpClient(){ close(); };
void close();

int init(const char *rootCaPem, const size_t rootCaPemSize);
int init(const char *rootCaPem, const size_t rootCaPemSize,
         const char *clientCertPem, const size_t clientCertPemSize,
         const char *clientKeyPem, const size_t clientKeyPemSize);
int connect(uint8_t *ip, uint16_t port);
int connect(char* domain, uint16_t port);
int write(unsigned char *buff, int length);
int read(unsigned char *buff, int length);
int read();
void stop() {close();};

int available();
bool isConnected();
bool verify();

static void debug_Tls( void *ctx, int level,
                      const char *file, int line,
                      const char *str );
static time_t particle_Time(time_t* ttime);

};

#endif