Skip to content

Commit

Permalink
Merge pull request #8 from JeremyTubongbanua/umass
Browse files Browse the repository at this point in the history
feat: UMass 2022 Pico W Template
  • Loading branch information
JeremyTubongbanua authored Nov 13, 2022
2 parents 1f5d9d2 + af27330 commit f448bce
Show file tree
Hide file tree
Showing 19 changed files with 730 additions and 263 deletions.
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
project.pico-go
project.pico-go
.vscode/
.picowgo
blink.py
.DS_STORE
219 changes: 203 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,211 @@
<img width=250px src="https://atsign.dev/assets/img/atPlatform_logo_gray.svg?sanitize=true">


# at_pico_w
Communicate with the atPlatform using a Raspberry Pi Pico W.

Developed with [MicroPython](https://micropython.org/).
For UMass 2022 IoT Projects.

# Table of Contents

- [Prerequisites](#prerequisites)
- [Instructions](#instructions)
* [Forking the Project](#forking-the-project)
* [Setting up Micropython on your Pico W](#setting-up-micropython-on-your-pico-w)
* [Getting Started - Blinking the LED](#getting-started---blinking-the-led)
* [Connecting to the atPlatform](#connecting-to-the-atplatform)
* [Libraries](#libraries)

# Prerequisites

- [VSCode](https://code.visualstudio.com/Download) with the [Pico-W-Go extension](https://marketplace.visualstudio.com/items?itemName=paulober.pico-w-go)
- [Git](https://git-scm.com/) and your own [GitHub](https://github.com) account
- A [Raspberry Pi Pico W](https://www.canakit.com/raspberry-pi-pico-w.html) and [a micro-USB to USB-A cable](https://m.media-amazon.com/images/I/61kT7kpt2hL._AC_SY450_.jpg) (to connect your Pico to your Computer)
- [FileZilla](https://filezilla-project.org/) or any other FTP software.
- Two [atSigns](https://my.atsign.com/go) and their [.atKeys files](https://www.youtube.com/watch?v=2Uy-sLQdQcA&ab_channel=Atsign)

# Instructions

## 0. Introduction

Hi UMass students! I've wrote some code for you to get your Pico Ws setup with the atPlatform. Big shoutout to @realvarx on GitHub for developing AES CTR and RSA-2048 private key signing on the Pico W.

Let me know if you have any questions on Discord (Jeremy#7970) or by email ([email protected]) or just on our [discord](https://discord.atsign.com).

Be sure to get the first 4 prerequisites under [Prerequisites](#prerequisites) before you start. The last 2 prerequisites (FTP software and 2 atSigns) can be done later.

## 1. Getting the right Micropython Firmware for your Pico W

1. Go to [atsign-foundation/micropython/releases](https://github.com/atsign-foundation/micropython/releases) and download the `.uf2` file. This `.uf2` file is specially built firmware to allow a certain encryption that Atsign uses (AES-256 CTR mode) to work on the Pico W. Big shoutout to @realvarx for developing all the Atsign encryption for the Pico W.

2. Unplug your Pico W from your computer. Hold down the BOOTSEL button on the Pico W and then plug back the Pico W into your computer (all while holding down the BOOTSEL button). Once the Pico W is plugged in, you can let go of the BOOTSEL button. Your Pico W should now be in bootloader mode.

You should see the Pico on your computer as a USB drive (see image below).

<img src="https://i.imgur.com/msoBukM.png" />

3. Drag and drop the `firmware.uf2` file into the Pico W. This will flash the Pico W with the new firmware. Now the Pico W should automatically restart so no need to unplug/replug it.

## 2. Getting Started - Blinking the LED

Now let's create our first project.

1. Open VSCode and open an empty folder somewhere on your computer. This is where your project will be.

2. Get the [Pico-W-Go extension](https://marketplace.visualstudio.com/items?itemName=paulober.pico-w-go)

3. Create a file named `blink.py` and write the following code:

```py
# blink.py

import machine
import time

led = machine.Pin("LED", machine.Pin.OUT) # "LED" is the on board LED

# blink ten times
for i in range(10):
print('Blinking... %s' %str(i+1))
led.toggle()
time.sleep(0.5)
```

Note: You could also name it `main.py`. However, `main.py` is the default file that the Pico W will run when it starts up. But with the [Pico-W-Go extension](https://marketplace.visualstudio.com/items?itemName=paulober.pico-w-go), you can choose *when* to run a file (regardless of name) which is how we will be running our files from here on out. I recommend not having a main.py at all since it tends to bug out the Pico W trying to run your experimental code as soon as it boots which can get annoying.


4. Open the command pallette via `Ctrl + Shift + P` (or `Cmd + Shift + P` if you are on a Mac) and type `Configure Project` and press Enter. The extension should setup your current folder as a Pico W project by initializing a `.vscode/` hidden folder and a `.picowgo` hidden file. Now, any Pico-W-Go commands should work. You can also access Pico-W-Go commands by clicking on "All Commands" at the bottom of VSCode.

VSCode bottom toolbar with Pico-W-Go commands:

<img width=500px src="https://i.ibb.co/g3jm53c/image.png">

6. Now try connecting to your Pico W and see if it works. You can do this by clicking on the "Connect" button.

7. Make sure you have your `blink.py` python file open in the editor and Run the "Run current file" command. You should see the onboard LED blink 10 times.

## 3. Forking the Project

Now we know your Pico W is working swell, let's get into some atPlatform.

First, let's create a fork of this repository branch on your own GitHub account. This gives you a copy of the code that you can edit on your own system.

1. Fork this repository by clicking "Fork"
2. Go into your terminal where you like to keep your code projects and do the following:
- `mkdir at_pico_w` (make an at_pico_w folder)
- `cd at_pico_w` (change directories)
- `git clone <https://github.com/<YOUR_GITHUB_NAME>/at_pico_w.git> .` (clone your fork into the folder you've created)
- `git checkout -b umass2022` (create a new branch called umass2022 and go into it)
- `git reset --hard origin/umass2022` (reset the branch to the origin)

Now you should have all of the code in your folder. Your folder should look something like this:

<img width=250px src="https://i.imgur.com/CC7bEFI.png">

## 4. Connecting to WiFi

1. Edit the `settings.json` by adding your WiFi and Password, leave the atSign blank for now.

settings.json

```json
{
"ssid": "********",
"password": "&&&&",
"atSign": ""
}
```


2. You should see a file called `test_1_wifi.py` in your project (since you forked the repository). Open this file in VSCode and run the Pico-W-Go command "Run this current file".

3. The code is:
```py
def main():
from lib.at_client import io_util
from lib import wifi

# Add your SSID an Password in `settings.json`
ssid, password, atSign = io_util.read_settings()
del atSign # atSign not needed in memory right now

print('\nConnecting to %s (Ctrl+C to stop)...' % ssid)
wlan = wifi.init_wlan(ssid, password)

if not wlan == None:
print('Connected to WiFi %s: %s' %(ssid, str(wlan.isconnected())))
else:
print('Failed to connect to \'%s\'... :(' %ssid)

if __name__ == '__main__':
main()
```

4. Your output should be similar to:

```
Connecting to Soup (Ctrl+C to stop)...
Connected to WiFi Soup: True
```

## 5. Connecting to your device atSign's atServer.

1. If you do not have all of the prerequisites, it is time to get them, especially: [FileZilla](https://filezilla-project.org/) or any other FTP software and two [atSigns](https://my.atsign.com/go) and their [.atKeys files](https://www.youtube.com/watch?v=2Uy-sLQdQcA&ab_channel=Atsign). Continue reading to find out how to get your .atKeys files.

2. Add the atSign you wish your device's atSign to be in the `settings.json`. This is an atSign you own and got from [my.atsign.com/go](https://my.atsign.com/go).

settings.json

```
{
"ssid": "******",
"password": "***",
"atSign": "@alice"
}
```

3. To get an .atKeys file belonging to an atSign, go to 0:54 of this [video](https://youtu.be/2Uy-sLQdQcA?t=54). The easiest way is to download [atmospherePro](https://atsign.com/apps/atmospherepro/) on your computer (via Microsoft Store on Windows or the Appstore if you're on Mac) and onboarding your atSign via the onboarding widget in the app. There are other ways to generate the encryption .atKeys file for an atSign (such as the [at_onboarding_cli on Dart](), [RegisterCLI on Java](), the [sshnp_register_tool in at_tools](), [OnboardingCLI in Java]()) but generating it through [atmospherePro](https://atsign.com/apps/atmospherepro/) is the easiest if you don't want to deal with any code. If you have an .atKeys file (like '@bob232.atKeys'), then move onto the next step. We are going to put our atKeys on the Pico W.

4. Run the "Start FTP server" command to start the FTP server on your Pico W Go. You should be given an address in the terminal (similar to the image below):

<img width=500px src="https://i.ibb.co/89mpzBh/image.png">

5. Open [FileZilla](https://filezilla-project.org/) and connect to this address. If it asks for a password, the password should be `pico`. If you've uploaded and files to the Pico before, you should see them once you've connected. Similar to below:

<image src="https://i.imgur.com/UaLQVdu.png" />

6. Create a `keys/` directory. This is where you will drag and drop the .atKeys file. Your `/keys/` directory should look like this:

<image src="https://i.imgur.com/wl2rIk8.png" />

7. Close the FTP server by pressing "Stop" on the bottom toolbar on VSCode.

Stop button:

<image src="https://i.imgur.com/RcxeSV5.png" />

Yay our FTP server is closed:

<image src="https://i.imgur.com/3FJg4Lc.png" />

8. Now we have to put our keys in the Pico W, it's time to run `test_3_pkam_authenticate.py`.

Output should be similar to:

<image src="https://i.imgur.com/01zt1BN.png">

If you don't see this, ensure your settings.json looks like this (with an atSign you own):

## Usage
- Install the latest `firmware.uf2` onto your Pico W from [atsign-foundation/micropython Releases](https://github.com/atsign-foundation/micropython/releases), as this is patched to enable AES CTR, which is used by atSigns.
- Fill all the fields of the `settings.json` file (ssid/passw of your Wi-Fi network and atSign).
- Download [Thonny IDE](https://thonny.org/) and place all the files of this repository in the Pico W file system.
- Place your `.atKeys` file in the `~/keys/` directory (if the folder doesn't exist, create it manually)
- Run `main.py` and select option `3` in the REPL ("Get privateKey for @[yourAtSign]")
- Re-launch the REPL (run `main.py` again)
- Now you can select option `2` in the REPL to automatically get authenticated in your DESS
- Enjoy! :)
```json
{
"ssid": "****",
"password": "****",
"atSign": "@fascinatingsnow"
}
```

(If you get an error when attempting to find the secondary or when trying to connect to it, run again the REPL)
and that you have the .atKeys file in the `/keys/` directory.

(You can uncomment a few commented lines in the `send_verb` method to see the llookup verb answer content decrypted. Warning: some stored keys are not encrypted, if you try to see the decrypted content of one of those stored keys, you will get an error. This exception will be handled in the future)
<image src="https://i.imgur.com/wl2rIk8.png" />

## Libraries
- [iot-core-micropython](https://github.com/GoogleCloudPlatform/iot-core-micropython)
- [uasn1](https://github.com/mkomon/uasn1)
## 6. Sending end-to-end encrypted data
18 changes: 0 additions & 18 deletions aes.py

This file was deleted.

33 changes: 33 additions & 0 deletions lib/aes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import ubinascii
import ucryptolib

def aes_decrypt(encryptedText: str, aes256Base64Key: str) -> str:
key = ubinascii.a2b_base64(aes256Base64Key)
iv = hex_str_to_bytes("00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00")
aes = ucryptolib.aes(key, 6, iv)
decrypted = aes.decrypt(bytearray(ubinascii.a2b_base64(encryptedText)))
return decrypted.decode('utf-8').rstrip("\x10")

def aes_encrypt(plain_text: str, aes256Base64Key: str) -> str:
key = ubinascii.a2b_base64(aes256Base64Key)
iv = hex_str_to_bytes("00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00")
aes = ucryptolib.aes(key, 6, iv)
encrypted = aes.encrypt(bytearray(ubinascii.a2b_base64(plain_text)))
return encrypted.decode('utf-8').rstrip("\x10")

def hex_str_to_bytes(hex_str):
parts = hex_str.split(' ')
together = "".join(parts)
return bytes.fromhex(together)

def str_to_bytes(s: str) -> bytes:
return ubinascii.a2b_base64(s)

def str_to_bytearray(s: str) -> bytearray:
return bytearray(str_to_bytes(s))

def bytearray_to_str(b: bytearray) -> str:
return str(b)

def bytes_to_str(b: bytes) -> str:
return str(ubinascii.b2a_base64(b))
23 changes: 23 additions & 0 deletions lib/at_client/at_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import ubinascii

def without_prefix(atSign: str):
if atSign.startswith('@'):
return atSign[1:]
return atSign

def format_atSign(atSign: str):
if not atSign.startswith('@'):
return '@' + atSign
return atSign

def str_to_bytes(s: str) -> bytes:
return ubinascii.a2b_base64(s)

def str_to_bytearray(s: str) -> bytearray:
return bytearray(str_to_bytes(s))

def bytearray_to_str(b: bytearray) -> str:
return str(b)

def bytes_to_str(b: bytes) -> str:
return str(ubinascii.b2a_base64(b))
14 changes: 14 additions & 0 deletions lib/at_client/io_util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import ujson
from lib.at_client import at_utils

def read_settings():
with open('settings.json') as f:
info = ujson.loads(f.read())
return info['ssid'], info['password'], info['atSign'].replace('@', '')

def read_key(atSign: str):
atSign = at_utils.without_prefix(atSign)
path = '/keys/@' + atSign + '_key.atKeys'
with open(path) as f:
info = ujson.loads(f.read())
return info['aesEncryptPrivateKey'], info['aesEncryptPublicKey'], info['aesPkamPrivateKey'], info['aesPkamPublicKey'], info['selfEncryptionKey']
43 changes: 43 additions & 0 deletions lib/at_client/keys_util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from lib.at_client.io_util import read_key
from lib.aes import aes_decrypt
from lib.at_client.at_utils import without_prefix
from lib.pem_service import get_pem_parameters, get_pem_key
import os

# initializes /keys/@alice/ with RSA keys in their pem form
# e.g. /keys/@alice/aesEncryptPrivateKey_pem.json
def initialize_keys(atSign: str) -> None:
aesEncryptPrivateKey, aesEncryptPublicKey, aesPkamPrivateKey, aesPkamPublicKey, selfEncryptionKey = read_key(atSign)

try:
os.mkdir('/keys/@%s' % without_prefix(atSign))
except:
pass

# aesEncryptPrivateKey_pem_parameters = get_pem_parameters(get_pem_key(aes_decrypt(aesEncryptPrivateKey, selfEncryptionKey), 'private'), 'private')
# with open('/keys/@%s/aesEncryptPrivateKey_pem.json' % without_prefix(atSign), 'w') as w:
# w.write("{\n\"aesEncryptPrivateKey\": [\n" + str(aesEncryptPrivateKey_pem_parameters[0]) + ",\n" + str(aesEncryptPrivateKey_pem_parameters[1]) + ",\n" + str(aesEncryptPrivateKey_pem_parameters[2]) + ",\n" + str(aesEncryptPrivateKey_pem_parameters[3]) + ",\n" + str(aesEncryptPrivateKey_pem_parameters[4]) + "\n]\n}")
# del aesEncryptPrivateKey_pem_parameters, aesEncryptPrivateKey

# aesEncryptPublicKey_pem_parameters = get_pem_parameters(get_pem_key(aes_decrypt(aesEncryptPublicKey, selfEncryptionKey), 'public'), 'public')
# with open('/keys/@%s/aesEncryptPublicKey_pem.json' % without_prefix(atSign), 'w') as w:
# w.write("{\n\"aesEncryptPublicKey\": [\n" + str(aesEncryptPublicKey_pem_parameters[0]) + ",\n" + str(aesEncryptPublicKey_pem_parameters[1]) + ",\n" + str(aesEncryptPublicKey_pem_parameters[2]) + ",\n" + str(aesEncryptPublicKey_pem_parameters[3]) + ",\n" + str(aesEncryptPublicKey_pem_parameters[4]) + "\n]\n}")
# del aesEncryptPublicKey_pem_parameters, aesEncryptPublicKey

aesPkamPrivateKey_pem_parameters = get_pem_parameters(get_pem_key(aes_decrypt(aesPkamPrivateKey, selfEncryptionKey), 'private'), 'private')
with open('/keys/@%s/aesPkamPrivateKey_pem.json' % without_prefix(atSign), 'w') as w:
w.write("{\n\"aesPkamPrivateKey\": [\n" + str(aesPkamPrivateKey_pem_parameters[0]) + ",\n" + str(aesPkamPrivateKey_pem_parameters[1]) + ",\n" + str(aesPkamPrivateKey_pem_parameters[2]) + ",\n" + str(aesPkamPrivateKey_pem_parameters[3]) + ",\n" + str(aesPkamPrivateKey_pem_parameters[4]) + "\n]\n}")
del aesPkamPrivateKey_pem_parameters, aesPkamPrivateKey

# aesPkamPublicKey_pem_parameters = get_pem_parameters(get_pem_key(aes_decrypt(aesPkamPublicKey, selfEncryptionKey)))
# with open('/keys/@%s/aesPkamPublicKey_pem.json' % without_prefix(atSign), 'w') as w:
# w.write("{\n\"aesPkamPublicKey\": [\n" + str(aesPkamPublicKey_pem_parameters[0]) + ",\n" + str(aesPkamPublicKey_pem_parameters[1]) + ",\n" + str(aesPkamPublicKey_pem_parameters[2]) + ",\n" + str(aesPkamPublicKey_pem_parameters[3]) + ",\n" + str(aesPkamPublicKey_pem_parameters[4]) + "\n]\n}")
# del aesPkamPublicKey_pem_parameters, aesPkamPublicKey

del selfEncryptionKey

def get_pem_pkam_private_key_from_file(atSign: str):
import ujson
with open('/keys/@%s/aesPkamPrivateKey_pem.json' %without_prefix(atSign), 'r') as f:
info = ujson.loads(f.read())
return info["aesPkamPrivateKey"]
Loading

0 comments on commit f448bce

Please sign in to comment.