Skip to content

Latest commit

 

History

History
258 lines (179 loc) · 6.59 KB

File metadata and controls

258 lines (179 loc) · 6.59 KB

Device Keys

Encrypted signing key management for Roku development. One device, multiple companies, zero crosstalk.


Prerequisites

macOS

brew install age

Windows

scoop install age
# or
winget install FiloSottile.age

Generate your encryption key (one-time)

mkdir -p ~/.age
age-keygen -o ~/.age/key.txt

This creates two things:

  • Public key (age1...) — used to encrypt. Safe to share.
  • Secret key (AGE-SECRET-KEY-...) — used to decrypt. Never share this.

Back up the secret key

macOS (Keychain):

security add-generic-password -a "$USER" -s "age-secret-key" -w "$(grep 'AGE-SECRET-KEY' ~/.age/key.txt)" -U

To retrieve later:

security find-generic-password -s "age-secret-key" -w

Windows (Credential Manager):

# Store
cmdkey /generic:age-secret-key /user:age /pass:"AGE-SECRET-KEY-YOUR-KEY-HERE"

# Retrieve
cmdkey /list:age-secret-key

Or store it in any password manager. This one string is the only thing you need to recover everything in this repo.


Setup

Run the one-shot setup script:

./setup.sh

This installs age, generates your encryption key, backs it up to the system keychain, and adds shell aliases to all detected shells (zsh, bash, PowerShell). If run again, it offers to abort, overwrite, or repair.


Repo Structure

device-keys/
├── devices/
│   ├── default.age                     # Default Roku device (IP + password)
│   └── <name>.age                      # Additional devices
├── keys/
│   └── <company>/
│       ├── <company>-credentials.age   # Encrypted DevID + signing password
│       └── <company>-rekey.pkg.age     # Encrypted .pkg for restoring key to device
├── scripts/
│   ├── common.sh                       # Shared utilities (device resolution, decryption)
│   ├── add-device.sh                   # Register a new Roku device
│   ├── capture-key.sh                  # Full setup for a new company
│   ├── rekey.sh                        # Switch device to a company's key
│   ├── genkey.sh                       # Generate a signing key (standalone)
│   ├── extract-key.sh                  # Extract existing key from device
│   ├── package.sh                      # Build a .pkg for store submission
│   ���── init.sh                         # Generate bsconfig.local.json for a project
├── stub-app/                           # Minimal Roku app used for key capture
└── .gitignore                          # *.dec, *.pkg, *.tmp

All .age files are encrypted with your public key. Without the secret key, they're unreadable.


Commands

All commands support --device <name> to target a specific Roku device. If omitted, the default device is used.

Add a new device

roku-add-device <name> <ip> <password>

Registers a new Roku device so you can target it with --device.

Example:

roku-add-device stick-2 192.168.1.100 mypassword
roku-add-device office 10.0.0.50 devpassword

Then use it:

roku-rekey wvw --device stick-2
roku-capture acme --device office

Add a new company

roku-capture <company-name>

This runs the full setup:

  1. Generates a new signing key on the Roku device (via telnet)
  2. Sideloads the stub app
  3. Packages it to create a signed .pkg
  4. Encrypts and saves both the credentials and .pkg

Example:

roku-capture acme

Creates:

keys/acme/
├── acme-credentials.age
└── acme-rekey.pkg.age

Commit both files after.


Switch between companies

roku-rekey <company-name>

Restores that company's signing key to the device using their saved .pkg. Takes a few seconds.

Example:

roku-rekey wvw       # Device is now keyed to WVW
roku-rekey untamed   # Device is now keyed to Untamed

The script verifies the switch by checking the DevID on the device after rekeying.


Set up a project for development

cd /path/to/roku/project
roku-init

Decrypts the device IP and password, writes bsconfig.local.json into the current directory. Only needed once per project (or when the device IP changes). The file is gitignored.


Package for store submission

cd /path/to/roku/project
roku-package <company-name>

Loads the correct signing key, builds the app, sideloads it, packages it, and downloads the .pkg to out/.

Example:

cd ~/Development/pin/untamed/untamed_roku
roku-package untamed
# → out/untamed-20260410-170000.pkg

Generate a signing key (standalone)

roku-genkey <company-name>

Generates a new key via telnet and saves the encrypted credentials. Does not capture a .pkg — use capture-key.sh instead for the full flow.


Extract an existing key from the device

roku-extract <company-name>

Reads the current DevID from the device and saves it. Useful if you already have a key on the device and want to back it up. You'll be prompted for the keygen password if you have it.


How it works

Signing keys

Each genkey creates a cryptographic signing key in the Roku's hardware and returns:

  • DevID — identifies the key (submitted to the Channel Store)
  • Password — used to sign apps and rekey the device

Only one key exists on the device at a time. Running genkey again destroys the previous key.

Rekeying

To restore a previous key, upload a .pkg that was signed with that key plus the password. The device extracts the key from the .pkg signature and restores it. This is why we capture a .pkg immediately after generating each key.

Encryption

All credentials and .pkg files are encrypted with age before committing. The repo is safe to push to a private GitHub repo — nothing is readable without your secret key.


Recovery (new machine)

  1. Install age
  2. Retrieve your secret key from Keychain / password manager
  3. Save it to ~/.age/key.txt
  4. Clone this repo
  5. Add the shell aliases to your profile
  6. Run roku-init in each project directory

Everything works exactly as before.


Important notes

  • Never commit decrypted files. The .gitignore excludes *.dec, *.pkg (unencrypted), and *.tmp.
  • One device, many companies. Rekey before packaging. Daily sideloading doesn't need rekeying.
  • Back up your age secret key. It's the single point of recovery. Lose it and the encrypted files are gone.
  • genkey destroys the current key. Always capture the existing key before generating a new one.