Skip to content

Commit ba2c774

Browse files
amclainfhunleth
authored andcommitted
Add documentation for connecting to GCP IoT Core.
1 parent dd7688d commit ba2c774

File tree

1 file changed

+49
-0
lines changed

1 file changed

+49
-0
lines changed

README.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,55 @@ hopefully the following code fragment will help:
141141
})
142142
```
143143

144+
### Connecting to Google Cloud Platform IoT Core
145+
146+
While the prior `Tortoise` code snippet works well with services like AWS, Google Cloud Platform (GCP) requires that a device must [create a JWT token](https://cloud.google.com/iot/docs/how-tos/credentials/jwts) in order to [connect to the broker](https://cloud.google.com/iot/docs/how-tos/mqtt-bridge#configuring_mqtt_clients). NervesKey provides the `NervesKey.sign_digest` function to assist with the [JWT signature](https://cloud.google.com/iot/docs/how-tos/credentials/jwts#jwt_signature) portion of Google's documentation. The following code fragment can help with creating the JWT that is required as the MQTT password:
147+
148+
```elixir
149+
@doc """
150+
Generate a JSON Web Token used to sign into the Google Cloud Platform
151+
IoT Core MQTT broker.
152+
"""
153+
@spec generate_jwt() :: String.t()
154+
def generate_jwt do
155+
jwt_expiration_in_hours = 4
156+
iat = System.os_time(:second)
157+
exp = iat + jwt_expiration_in_hours * 60 * 60
158+
aud = Application.get_env(:__my_project__, :gcp_project_id)
159+
160+
header = %{"alg" => "ES256", "typ" => "JWT"}
161+
payload = %{"iat" => iat, "exp" => exp, "aud" => aud}
162+
163+
sign(header, payload)
164+
end
165+
166+
@doc """
167+
Sign a JSON Web Token.
168+
"""
169+
@spec sign(header :: map, payload :: map) :: String.t()
170+
def sign(header, payload) do
171+
{:ok, transport} = ATECC508A.Transport.I2C.init([])
172+
173+
encoded_header =
174+
header
175+
|> Jason.encode!()
176+
|> Base.url_encode64(padding: false)
177+
178+
encoded_payload =
179+
payload
180+
|> Jason.encode!()
181+
|> Base.url_encode64(padding: false)
182+
183+
digest = :crypto.hash(:sha256, "#{encoded_header}.#{encoded_payload}")
184+
{:ok, signature} = NervesKey.sign_digest(transport, digest)
185+
encoded_signature = Base.url_encode64(signature, padding: false)
186+
187+
"#{encoded_header}.#{encoded_payload}.#{encoded_signature}"
188+
end
189+
```
190+
191+
This fragment can allow for the dependency injection of `sign` if the firmware needs to run both with and without the ATECC crypto chip (i.e. on the target and on the host). [`JOSE.JWT.sign`](https://hexdocs.pm/jose/JOSE.JWT.html#sign/3) can be used for a host implementation.
192+
144193
## Preparing for provisioning
145194

146195
The ATECC508A/608A in the NervesKey needs to be provisioned before it can be

0 commit comments

Comments
 (0)