Skip to content

Commit 572c087

Browse files
committed
wip
1 parent 05afb47 commit 572c087

8 files changed

+355
-1
lines changed

GCP-Mqtt/GCP-Mqtt.ino

+199
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
/*
2+
GCP (Google Cloud Platform) IoT Core WiFi
3+
This sketch securely connects to GCP IoT Core using MQTT over WiFi.
4+
It uses a private key stored in the ATECC508A and a JSON Web Token (JWT) with
5+
a JSON Web Signature (JWS).
6+
It publishes a message every 5 seconds to "/devices/{deviceId}/state" topic
7+
and subscribes to messages on the "/devices/{deviceId}/config" and
8+
"/devices/{deviceId}/commands/#" topics.
9+
The circuit:
10+
- Arduino MKR WiFi 1010 or MKR1000
11+
This example code is in the public domain.
12+
*/
13+
14+
#include <ArduinoECCX08.h>
15+
#include <utility/ECCX08JWS.h>
16+
#include <ArduinoMqttClient.h>
17+
#include <Arduino_JSON.h>
18+
#include <WiFi101.h> // for MKR1000
19+
20+
#include "arduino_secrets.h"
21+
22+
/////// Enter your sensitive data in arduino_secrets.h
23+
const char ssid[] = SECRET_SSID;
24+
const char pass[] = SECRET_PASS;
25+
26+
const char projectId[] = SECRET_PROJECT_ID;
27+
const char cloudRegion[] = SECRET_CLOUD_REGION;
28+
const char registryId[] = SECRET_REGISTRY_ID;
29+
const String deviceId = SECRET_DEVICE_ID;
30+
31+
const char broker[] = "mqtt.googleapis.com";
32+
33+
WiFiSSLClient wifiSslClient;
34+
MqttClient mqttClient(wifiSslClient);
35+
36+
unsigned long lastMillis = 0;
37+
38+
void setup() {
39+
Serial.begin(9600);
40+
while (!Serial);
41+
42+
if (!ECCX08.begin()) {
43+
Serial.println("No ECCX08 present!");
44+
while (1);
45+
}
46+
47+
// Calculate and set the client id used for MQTT
48+
String clientId = calculateClientId();
49+
50+
mqttClient.setId(clientId);
51+
52+
// Set the message callback, this function is
53+
// called when the MQTTClient receives a message
54+
mqttClient.onMessage(onMessageReceived);
55+
}
56+
57+
void loop() {
58+
if (WiFi.status() != WL_CONNECTED) {
59+
connectWiFi();
60+
}
61+
62+
Serial.println("Before the connectMQTT()");
63+
Serial.println("Delay to get terminal up");
64+
delay(10000);
65+
Serial.println("Go!");
66+
67+
68+
if (!mqttClient.connected()) {
69+
// MQTT client is disconnected, connect
70+
connectMQTT();
71+
}
72+
73+
// poll for new MQTT messages and send keep alives
74+
mqttClient.poll();
75+
76+
// publish a message roughly every 5 seconds.
77+
if (millis() - lastMillis > 5000) {
78+
lastMillis = millis();
79+
80+
publishMessage();
81+
}
82+
}
83+
84+
unsigned long getTime() {
85+
// get the current time from the WiFi module
86+
return WiFi.getTime();
87+
}
88+
89+
void connectWiFi() {
90+
Serial.print("Attempting to connect to SSID: ");
91+
Serial.print(ssid);
92+
Serial.print(" ");
93+
94+
while (WiFi.begin(ssid, pass) != WL_CONNECTED) {
95+
// failed, retry
96+
Serial.print(".");
97+
delay(5000);
98+
}
99+
Serial.println();
100+
101+
Serial.println("You're connected to the network");
102+
Serial.println();
103+
}
104+
105+
void connectMQTT() {
106+
Serial.print("Attempting to connect to MQTT broker: ");
107+
Serial.print(broker);
108+
Serial.println(" ");
109+
110+
while (!mqttClient.connected()) {
111+
// Calculate the JWT and assign it as the password
112+
String jwt = calculateJWT();
113+
114+
mqttClient.setUsernamePassword("", jwt);
115+
116+
if (!mqttClient.connect(broker, 8883)) {
117+
Serial.print("mqttClient.connectError(): ");
118+
Serial.println(mqttClient.connectError());
119+
// failed, retry
120+
Serial.print("Halt");
121+
while(true);
122+
//Serial.print(".");
123+
delay(5000);
124+
}
125+
}
126+
Serial.println();
127+
128+
Serial.println("You're connected to the MQTT broker");
129+
Serial.println();
130+
131+
// subscribe to topics
132+
mqttClient.subscribe("/devices/" + deviceId + "/config", 1);
133+
mqttClient.subscribe("/devices/" + deviceId + "/commands/#");
134+
}
135+
136+
String calculateClientId() {
137+
String clientId;
138+
139+
// Format:
140+
//
141+
// projects/{project-id}/locations/{cloud-region}/registries/{registry-id}/devices/{device-id}
142+
//
143+
144+
clientId += "projects/";
145+
clientId += projectId;
146+
clientId += "/locations/";
147+
clientId += cloudRegion;
148+
clientId += "/registries/";
149+
clientId += registryId;
150+
clientId += "/devices/";
151+
clientId += deviceId;
152+
153+
return clientId;
154+
}
155+
156+
String calculateJWT() {
157+
unsigned long now = getTime();
158+
159+
// calculate the JWT, based on:
160+
// https://cloud.google.com/iot/docs/how-tos/credentials/jwts
161+
JSONVar jwtHeader;
162+
JSONVar jwtClaim;
163+
164+
jwtHeader["alg"] = "ES256";
165+
jwtHeader["typ"] = "JWT";
166+
167+
jwtClaim["aud"] = projectId;
168+
jwtClaim["iat"] = now;
169+
jwtClaim["exp"] = now + (24L * 60L * 60L); // expires in 24 hours
170+
171+
return ECCX08JWS.sign(0, JSON.stringify(jwtHeader), JSON.stringify(jwtClaim));
172+
}
173+
174+
void publishMessage() {
175+
Serial.println("Publishing message");
176+
177+
// send message, the Print interface can be used to set the message contents
178+
mqttClient.beginMessage("/devices/" + deviceId + "/state");
179+
mqttClient.print("hello ");
180+
mqttClient.print(millis());
181+
mqttClient.endMessage();
182+
}
183+
184+
void onMessageReceived(int messageSize) {
185+
// we received a message, print out the topic and contents
186+
Serial.print("Received a message with topic '");
187+
Serial.print(mqttClient.messageTopic());
188+
Serial.print("', length ");
189+
Serial.print(messageSize);
190+
Serial.println(" bytes:");
191+
192+
// use the Stream interface to print the contents
193+
while (mqttClient.available()) {
194+
Serial.print((char)mqttClient.read());
195+
}
196+
Serial.println();
197+
198+
Serial.println();
199+
}

GCP-Mqtt/Makefile

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
.PHONY: compile upload terminal
3+
.DEFAULT: compile
4+
5+
6+
compile:
7+
arduino-cli compile \
8+
--fqbn arduino:samd:mkr1000 GCP-Mqtt
9+
10+
11+
upload:
12+
arduino-cli upload -p /dev/ttyACM0 --fqbn arduino:samd:mkr1000 GCP-Mqtt
13+
14+
terminal:
15+
echo type ctrl-a x
16+
screen /dev/ttyACM0 115200

README.md

+140-1
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ as 192.168.0.160. The capture shows it as 192.168.1.128.
319319

320320
![img](imgs/ss21.png)
321321

322-
So with that said, I am using a capture filter as opposed to a display filter in wireshark. The capture file is in the [capfiles](./capfiles/) directory.
322+
So with that said, I am using a capture filter `host 192.168.1.128` as opposed to a display filter in wireshark. The capture file is in the [capfiles](./capfiles/) directory.
323323

324324
So, now that we know how to configure wireshark, lets see what happens
325325
when we attempt to connect with HTTPs and no domain cert loaded
@@ -345,6 +345,145 @@ contents of a webpage. It also generates a lot of traffic in wireshark. These
345345

346346
![img](imgs/ss25.png)
347347

348+
##
349+
## MQTT to GCP on Arduino
350+
351+
Running [this](https://github.com/arduino/ArduinoCloudProviderExamples/tree/master/examples/Google%20Cloud%20Platform%20IoT%20Core/GCP_IoT_Core_WiFi) code with one change. Since I am using MKR1000 I switch the
352+
header as wonderfully commented.
353+
354+
Before I show the capture file and output, let me specify my
355+
current "ssl" setup.
356+
357+
I have the google.com:443 domain loaded. I read something about
358+
where people said a fix was to include the cert used to sign
359+
the other cert. I'm still unclear on the terms or process.
360+
But, I'm thinking what they are intending is that the
361+
crypto chip needs the firmware to have the correct cert
362+
loaded before it generates a key. So with that said
363+
let me add the mqtt cert in case I need that.
364+
365+
![img](imgs/ss30.png)
366+
367+
```
368+
openssl s_client -connect mqtt.googleapis.com:8883
369+
370+
$ openssl s_client -connect mqtt.googleapis.com:8883
371+
CONNECTED(00000003)
372+
depth=2 OU = GlobalSign Root CA - R2, O = GlobalSign, CN = GlobalSign
373+
verify return:1
374+
depth=1 C = US, O = Google Trust Services, CN = GTS CA 1O1
375+
verify return:1
376+
depth=0 C = US, ST = California, L = Mountain View, O = Google LLC, CN = mqtt.googleapis.com
377+
verify return:1
378+
379+
JFD Just a comment, the lines above do not show up in a stdout capture
380+
with the exception of the CONNECTED line. In that case its more
381+
readable, but I want to show all the output.
382+
---
383+
Certificate chain
384+
0 s:C = US, ST = California, L = Mountain View, O = Google LLC, CN = mqtt.googleapis.com
385+
i:C = US, O = Google Trust Services, CN = GTS CA 1O1
386+
1 s:C = US, O = Google Trust Services, CN = GTS CA 1O1
387+
i:OU = GlobalSign Root CA - R2, O = GlobalSign, CN = GlobalSign
388+
389+
---
390+
Server certificate
391+
-----BEGIN CERTIFICATE-----
392+
MIIFtzCCBJ+gAwIBAgIQZYgPVab72N0FAAAAAIgIBDANBgkqhkiG9w0BAQsFADBC
393+
... stuffed deleted
394+
-----END CERTIFICATE-----
395+
subject=C = US, ST = California, L = Mountain View, O = Google LLC, CN = mqtt.googleapis.com
396+
397+
issuer=C = US, O = Google Trust Services, CN = GTS CA 1O1
398+
399+
---
400+
No client certificate CA names sent
401+
Peer signing digest: SHA256
402+
Peer signature type: RSA-PSS
403+
Server Temp Key: X25519, 253 bits
404+
---
405+
SSL handshake has read 3064 bytes and written 391 bytes
406+
Verification: OK
407+
---
408+
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
409+
Server public key is 2048 bit
410+
Secure Renegotiation IS NOT supported
411+
Compression: NONE
412+
Expansion: NONE
413+
No ALPN negotiated
414+
Early data was not sent
415+
Verify return code: 0 (ok)
416+
---
417+
418+
```
419+
I should note the diff to the .ino file. Earlier I mentioned
420+
I only changed the include. I've since done non logic flow mods
421+
to print status. With that said after the connect error and before
422+
the halt message which stops the loop, I made a change not in the
423+
screenshot which is
424+
425+
mqttClient.connectError(): -1
426+
427+
![img](imgs/ss31.png)
428+
429+
I haven't had time to look at the capture file in detail, but here are some observations:
430+
431+
* I don't see any red packets
432+
* It appears to be looping on the client connecting. I don't think
433+
this is a wait and it will happen thing.
434+
* I don't know enough about this, but frame 13 is the first frame
435+
to the mqtt host. Its brown in the screenshot. In the TCP flags
436+
portion of the frame it has Syn=0. I believe that is the signal
437+
of this is the start of our conversation in tcp. Likewise
438+
the mqtt host does the same. Later the first frame in brown after
439+
the Client sending some application data the mqtt host sends
440+
a frame with Fin=1 set. I think this is programtically interpeted
441+
as i'm closing the socket.
442+
443+
444+
![img](imgs/ss32.png)
445+
446+
So let me try to change the crypto chip. Possibly now that I have
447+
both certs in play in firmware when I run the firmware to generate
448+
the public key to upload to gcp iot core it will work.
449+
450+
### Updating the crypto chip
451+
452+
There are two domains loaded via firmware
453+
454+
* google.com:443
455+
* mqtt.gogapis.somehtingsomeont:8883
456+
457+
There are two key tools which might work for me in the crypto
458+
chip examples.
459+
460+
* ECCX08SelfSignedCert
461+
* ECCX08JWSPublicKey
462+
463+
I'll try the JWS public key one. I know this code uses JWT.
464+
What is a single letter between friends?
465+
466+
Running this code the console says in order to generate a PEM
467+
public key for your board, I need to pick a slot 0-4. I chose
468+
0. I wonder if this corresponds to the order in the firmware
469+
for the domain cert? FWIW, I also said I would like to generate
470+
a new private key in response.
471+
472+
I took the public key and added it to the other keys in gcp for
473+
this device.
474+
475+
-----BEGIN PUBLIC KEY-----
476+
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENp1a1bFLljzL1D6Dc2jLzd/9Hr3pwi0y4ELGCkBr
477+
/+PuTBkQm0TWu9oQB25Hw6ZbIRaN1ZvOMB+pM4D2Jj0gBQ==
478+
-----END PUBLIC KEY-----
479+
480+
This also fails with similar console message. Error = -1
481+
482+
Here is the associated capture.
483+
484+
485+
![img](imgs/ss33.png)
486+
348487

349488
## Archive capture file
350489
This is a 202108 or 07 capture file. After this one, I modifed the firmware and crypto.
Binary file not shown.
Binary file not shown.

imgs/ss30.png

54.3 KB
Loading

imgs/ss31.png

48.3 KB
Loading

imgs/ss32.png

225 KB
Loading

0 commit comments

Comments
 (0)