Skip to content

Commit

Permalink
SGX middleware manager (#198)
Browse files Browse the repository at this point in the history
- Renamed manager and adm to manager_ledger and adm_ledger
- Renamed manager-tcp to manager_tcp for consistency (python doesn't like hyphens in module names)
- Renamed build scripts
- Protocol now accepts configurable "restart the powHSM" messages
- Updated tcpsigner bundle artifacts
- Updated unit tests
- Updated documentation
- Updated github workflows
- Updated .gitignore(s)
- Incidentally removed unnecessary file
- Added password changing capabilities to the SGX powHSM implementation
- Added main SGX manager script
- Added build script for the SGX manager
- Added password change and password retries getter commands to the HSM2DongleSGX
- Updated user options to make names more generic
- Added unit tests
- Updated middleware docker do script to expose the host to the container (allowing for running tests against e.g. an SGX powHSM simulator)
  • Loading branch information
amendelzon committed Oct 22, 2024
1 parent 0cd44a7 commit fe93c34
Show file tree
Hide file tree
Showing 36 changed files with 322 additions and 115 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:
run: |
docker/mware/build
docker/packer/build
middleware/build/manager-tcp
middleware/build/manager_tcp
firmware/build/build-tcpsigner
- name: Checkout hsm-integration-test repo
Expand All @@ -69,9 +69,9 @@ jobs:

- name: Copy required files
run: |
mkdir hsm-integration-test/docker/manager/manager-tcp
tar -xzf rsk-powhsm/middleware/bin/manager-tcp.tgz \
-C hsm-integration-test/docker/manager/manager-tcp
mkdir hsm-integration-test/docker/manager/manager_tcp
tar -xzf rsk-powhsm/middleware/bin/manager_tcp.tgz \
-C hsm-integration-test/docker/manager/manager_tcp
cp rsk-powhsm/firmware/src/tcpsigner/tcpsigner \
hsm-integration-test/docker/tcpsigner/
Expand Down
6 changes: 3 additions & 3 deletions build-dist → build-dist-ledger
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ rm -f $DEST_DIR/attestation.json $DEST_DIR/device_attestation.json

echo
echo -e "\e[33mBuilding middleware...\e[0m"
$ROOT_DIR/middleware/build/dist
cp $ROOT_DIR/middleware/bin/adm.tgz $BIN_DIR
$ROOT_DIR/middleware/build/dist_ledger
cp $ROOT_DIR/middleware/bin/adm_ledger.tgz $BIN_DIR
cp $ROOT_DIR/middleware/bin/lbutils.tgz $BIN_DIR
cp $ROOT_DIR/middleware/bin/manager.tgz $BIN_DIR
cp $ROOT_DIR/middleware/bin/manager_ledger.tgz $BIN_DIR
cp $ROOT_DIR/middleware/bin/signapp.tgz $BIN_DIR
echo

Expand Down
4 changes: 2 additions & 2 deletions dist/scripts/setup
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ TARGET_ID="$(cat $ROOT_DIR/scripts/target.id)"
LBUTILS_DIR="$ROOT_DIR/bin/lbutils"
LBUTILS_BUNDLE="$LBUTILS_DIR.tgz"
LBUTILS="$LBUTILS_DIR/lbutils"
ADMIN_DIR="$ROOT_DIR/bin/adm"
ADMIN_DIR="$ROOT_DIR/bin/adm_ledger"
ADMIN_BUNDLE="$ADMIN_DIR.tgz"
ADMIN="$ADMIN_DIR/adm"
ADMIN="$ADMIN_DIR/adm_ledger"

function cleanBinaries() {
rm -rf $LBUTILS_DIR
Expand Down
4 changes: 2 additions & 2 deletions dist/scripts/upgrade-existing
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ TARGET_ID="$(cat $ROOT_DIR/scripts/target.id)"
LBUTILS_DIR="$ROOT_DIR/bin/lbutils"
LBUTILS_BUNDLE="$LBUTILS_DIR.tgz"
LBUTILS="$LBUTILS_DIR/lbutils"
ADMIN_DIR="$ROOT_DIR/bin/adm"
ADMIN_DIR="$ROOT_DIR/bin/adm_ledger"
ADMIN_BUNDLE="$ADMIN_DIR.tgz"
ADMIN="$ADMIN_DIR/adm"
ADMIN="$ADMIN_DIR/adm_ledger"

function cleanBinaries() {
rm -rf $LBUTILS_DIR
Expand Down
2 changes: 1 addition & 1 deletion docker/mware/do
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ WORKDIR=$1; shift
BINARY=$1; shift
ARGS=$@

docker run -ti --rm --name hsm-mware -v $HSM_ROOT:/hsm2 -v /dev/bus/usb:/dev/bus/usb --privileged -p$PORT:$PORT -w $WORKDIR $DOCKER_IMAGE $BINARY $ARGS
docker run -ti --rm --name hsm-mware -v $HSM_ROOT:/hsm2 -v /dev/bus/usb:/dev/bus/usb --privileged --add-host=host.docker.internal:host-gateway -p$PORT:$PORT -w $WORKDIR $DOCKER_IMAGE $BINARY $ARGS
2 changes: 1 addition & 1 deletion docs/attestation.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ The validation process _for each of the targets_ is fairly straightforward, and
For completion's sake, a validation tool is provided within the administration toolset. So, for example, if the file depicted above was at `/a/path/to/the/attestation.json`, the JSON-formatted public keys generated at onboarding time were at `/a/path/to/the/public-keys.json` and we knew the issuer public key was `0490f5c9d15a0134bb019d2afd0bf297149738459706e7ac5be4abc350a1f818057224fce12ec9a65de18ec34d6e8c24db927835ea1692b14c32e9836a75dad609` (the actual ledger issuer public key, found in [ledger's endorsement setup tooling](https://github.com/LedgerHQ/blue-loader-python/blob/0.1.31/ledgerblue/endorsementSetup.py#L138)), we could issue:

```bash
middleware/term> python adm.py verify_attestation -t /a/path/to/the/attestation.json -r 0490f5c9d15a0134bb019d2afd0bf297149738459706e7ac5be4abc350a1f818057224fce12ec9a65de18ec34d6e8c24db927835ea1692b14c32e9836a75dad609 -b /a/path/to/the/public-keys.json
middleware/term> python adm_ledger.py verify_attestation -t /a/path/to/the/attestation.json -r 0490f5c9d15a0134bb019d2afd0bf297149738459706e7ac5be4abc350a1f818057224fce12ec9a65de18ec34d6e8c24db927835ea1692b14c32e9836a75dad609 -b /a/path/to/the/public-keys.json
```

to then obtain the following sample output:
Expand Down
2 changes: 1 addition & 1 deletion docs/protocol-v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## About

This document describes the legacy protocol used in version 1 of the HSM. The purpose is to provide a reference for the usage of the legacy mode in the manager and TCP manager (with modifier `--version-one`).
This document describes the legacy protocol used in version 1 of the HSM. The purpose is to provide a reference for the usage of the legacy mode in the Ledger manager and TCP manager (with modifier `--version-one`).

## Definitions

Expand Down
1 change: 1 addition & 0 deletions firmware/src/powhsm/src/err.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ typedef enum {
ERR_DEVICE_ONBOARDED = 0x6BEF,
ERR_ONBOARDING = 0x6BF0,
ERR_DEVICE_LOCKED = 0x6BF1,
ERR_PASSWORD_CHANGE = 0x6BF2,
} err_code_signer_t;

#endif // __ERR_H
1 change: 1 addition & 0 deletions firmware/src/powhsm/src/instructions.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ typedef enum {
SGX_RETRIES = 0xA2,
SGX_UNLOCK = 0xA3,
SGX_ECHO = 0xA4,
SGX_CHANGE_PASSWORD = 0xA5,
} apdu_instruction_t;

#endif // __INSTRUCTIONS_H
23 changes: 23 additions & 0 deletions firmware/src/sgx/src/trusted/system.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,24 @@ static unsigned int do_onboard(unsigned int rx) {
return TX_NO_DATA();
}

static unsigned int do_change_password(unsigned int rx) {
// Require a nonblank password
if (APDU_DATA_SIZE(rx) < 1) {
THROW(ERR_INVALID_DATA_SIZE);
}

// Password change
uint8_t tmp_buffer[apdu_buffer_size];
size_t password_length = APDU_DATA_SIZE(rx);
memcpy(tmp_buffer, APDU_DATA_PTR, password_length);
if (!access_set_password((char*)tmp_buffer, password_length)) {
THROW(ERR_PASSWORD_CHANGE);
}

SET_APDU_OP(1);
return TX_NO_DATA();
}

static unsigned int do_unlock(unsigned int rx) {
if (!access_is_locked()) {
SET_APDU_OP(1);
Expand Down Expand Up @@ -124,6 +142,11 @@ static external_processor_result_t system_do_process_apdu(unsigned int rx) {
case SGX_ECHO:
result.tx = do_echo(rx);
break;
case SGX_CHANGE_PASSWORD:
REQUIRE_ONBOARDED();
REQUIRE_UNLOCKED();
result.tx = do_change_password(rx);
break;
default:
result.handled = false;
}
Expand Down
16 changes: 8 additions & 8 deletions middleware/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,36 +40,36 @@ Throughout the rest of the document, we will refer to a middleware development e

## Middleware breakdown

### Manager
### Ledger Manager

The manager is the main middleware component. Its role is to provide a high-level abstraction layer over the low-level powHSM dongle USB interface. It does this by starting a TCP service in a certain interface and port and implementing the [protocol](../docs/protocol.md) on top by means of interactions with the connected powHSM dongle. The entrypoint to the powHSM manager is the `manager.py` script. In order to start it, issue:
The Ledger manager is the main middleware component for the Ledger powHSM implementation. Its role is to provide a high-level abstraction layer over the low-level powHSM dongle USB interface. It does this by starting a TCP service in a certain interface and port and implementing the [protocol](../docs/protocol.md) on top by means of interactions with the connected powHSM dongle. The entrypoint to the powHSM manager is the `manager_ledger.py` script. In order to start it, issue:

```
(mware)> python manager.py
(mware)> python manager_ledger.py
```

Hit CTRL-C at any time to stop it.

### TCP Manager

This is an implementation of the Manager that connects to a dongle via a TCP/IP connection. Its main use is along the TCPSigner (an x86 implementation of the Signer component) for integration tests and the like. It's important to mention that Manager and TCP Manager share most of the code, and that the main difference lies in the dongle proxy used and available user options. The entrypoint to the TCP manager is the `manager-tcp.py` script. In order to start it, issue:
This is an implementation of the Manager that connects to a dongle via a TCP/IP connection. Its main use is along the TCPSigner (an x86 implementation of the Signer component) for integration tests and the like. It's important to mention that Manager and TCP Manager share most of the code, and that the main difference lies in the dongle proxy used and available user options. The entrypoint to the TCP manager is the `manager_tcp.py` script. In order to start it, issue:

```
(mware)> python manager-tcp.py
(mware)> python manager_tcp.py
```

Hit CTRL-C at any time to stop it.

### Administrative utilities

Aside from the main `manager.py` and `manager-tcp.py` scripts, there are other three scripts to consider:
Aside from the main `manager_ledger.py` and `manager_tcp.py` scripts, there are other three scripts to consider:

- `adm.py`: administrative utility for a powHSM dongle. It provides common utilities that can be performed on a powHSM dongle.
- `adm_ledger.py`: administrative utility for a Ledger powHSM dongle. It provides common utilities that can be performed on a powHSM dongle.
- `lbutils.py`: common frontend to some of the `ledgerblue` modules. In particular, it ultimately serves the purpose of being able to build a binary for these utilities.
- `signapp.py`: signer authorization generator. Serves the purpose of generating authorization files for Signer versions (see [the signer authorization documentation](../docs/signer-authorization.md) for details). It can be used to add externally generated signatures, or to sign with a manually input key (intended for testing purposes only). It can also be used to calculate the message to be signed to authorize a specific signer version (so that then the signature can be generated on a third-party application, e.g., MetaMask). Last, it has an option to calculate and output a Ledger app's hash.
- `signonetime.py`: ledger app signer. Serves the purpose of signing Ledger Nano S firmware builds with a securely generated random one-time key. It is used in the distribution building process targeting the initial device setup process.

The remaining `client.py` is a shorthand client utility for manually testing communication with a running manager or TCP manager.
The remaining `client.py` is a shorthand client utility for manually testing communication with a running Ledger manager or TCP manager.

## Unit tests

Expand Down
2 changes: 1 addition & 1 deletion middleware/adm.py → middleware/adm_ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def main():
"authorize_signer": do_authorize_signer,
}

parser = ArgumentParser(description="powHSM Administrative tool")
parser = ArgumentParser(description="Ledger powHSM Administrative tool")
parser.add_argument("operation", choices=list(actions.keys()))
parser.add_argument("-p", "--pin", dest="pin", help="PIN.")
parser.add_argument(
Expand Down
2 changes: 0 additions & 2 deletions middleware/build/adm

This file was deleted.

2 changes: 2 additions & 0 deletions middleware/build/adm_ledger
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/bash
source $(dirname $0)/bld-docker adm_ledger
6 changes: 3 additions & 3 deletions middleware/build/all
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ BINDIR=$(realpath $BUILDDIR/../bin/)

echo "Building all..."

QUIET=1 $BUILDDIR/manager && \
QUIET=1 $BUILDDIR/manager-tcp && \
QUIET=1 $BUILDDIR/adm && \
QUIET=1 $BUILDDIR/manager_ledger && \
QUIET=1 $BUILDDIR/manager_tcp && \
QUIET=1 $BUILDDIR/adm_ledger && \
QUIET=1 $BUILDDIR/lbutils && \
QUIET=1 $BUILDDIR/signapp && \
echo "" && sha256sum $BINDIR/*.tgz
10 changes: 5 additions & 5 deletions middleware/build/dist → middleware/build/dist_ledger
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
BUILDDIR=$(dirname $0)
BINDIR=$(realpath $BUILDDIR/../bin/)

echo "Building distribution binaries..."
echo "Building Ledger distribution binaries..."

QUIET=1 $BUILDDIR/manager && \
QUIET=1 $BUILDDIR/adm && \
QUIET=1 $BUILDDIR/manager_ledger && \
QUIET=1 $BUILDDIR/adm_ledger && \
QUIET=1 $BUILDDIR/lbutils && \
QUIET=1 $BUILDDIR/signapp && \
echo "" && \
sha256sum $BINDIR/manager.tgz && \
sha256sum $BINDIR/adm.tgz && \
sha256sum $BINDIR/manager_ledger.tgz && \
sha256sum $BINDIR/adm_ledger.tgz && \
sha256sum $BINDIR/lbutils.tgz
sha256sum $BINDIR/signapp.tgz
2 changes: 0 additions & 2 deletions middleware/build/manager

This file was deleted.

2 changes: 0 additions & 2 deletions middleware/build/manager-tcp

This file was deleted.

2 changes: 2 additions & 0 deletions middleware/build/manager_ledger
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/bash
source $(dirname $0)/bld-docker manager_ledger
2 changes: 2 additions & 0 deletions middleware/build/manager_sgx
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/bash
source $(dirname $0)/bld-docker manager_sgx
2 changes: 2 additions & 0 deletions middleware/build/manager_tcp
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/bash
source $(dirname $0)/bld-docker manager_tcp
14 changes: 9 additions & 5 deletions middleware/ledger/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ class HSM2ProtocolLedger(HSM2Protocol):
# Required minimum number of pin retries available to proceed with unlocking
MIN_AVAILABLE_RETRIES = 2

# Default user messages
MESSAGES = {
"restart": "restart the powHSM"
}

def __init__(self, pin, dongle):
super().__init__()
self.hsm2dongle = dongle
Expand Down Expand Up @@ -73,7 +78,7 @@ def initialize_device(self):
self.logger.info(
"Could not determine onboarded status. If unlocked, "
+ "please enter the signing app and rerun the manager. Otherwise,"
+ "disconnect and reconnect the ledger nano and try again"
+ f"{self.MESSAGES["restart"]} and try again"
)
raise HSM2ProtocolInterrupt()

Expand Down Expand Up @@ -196,14 +201,13 @@ def _handle_bootloader(self):
raise Exception("Dongle reported fail to change pin. Pin invalid?")
self.pin.commit_change()
self.logger.info(
"PIN changed. Please disconnect and reconnect the ledger nano"
f"PIN changed. Please {self.MESSAGES["restart"]}"
)
except Exception as e:
self.pin.abort_change()
self.logger.error(
"Error changing PIN: %s. Please disconnect and "
"reconnect the ledger nano and try again",
format(e),
f"Error changing PIN: %s. Please {self.MESSAGES["restart"]} "
"and try again", format(e),
)
finally:
raise HSM2ProtocolInterrupt()
Expand Down
12 changes: 9 additions & 3 deletions middleware/manager.py → middleware/manager_ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,18 @@ def load_pin(user_options):
return pin


def configure_protocol_messages(protocol):
protocol.MESSAGES = {
"restart": "disconnect and reconnect the ledger nano",
}


if __name__ == "__main__":
user_options = UserOptionParser("Start the powHSM manager",
user_options = UserOptionParser("Start the powHSM manager for Ledger",
with_pin=True).parse()

runner = ManagerRunner("powHSM manager",
lambda options: HSM2Dongle(options.dongle_debug),
load_pin)
lambda options: HSM2Dongle(options.io_debug),
load_pin, configure_protocol_messages)

runner.run(user_options)
61 changes: 61 additions & 0 deletions middleware/manager_sgx.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# The MIT License (MIT)
#
# Copyright (c) 2021 RSK Labs Ltd
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
# of the Software, and to permit persons to whom the Software is furnished to do
# so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

import os
from sgx.hsm2dongle import HSM2DongleSGX
from mgr.runner import ManagerRunner
from ledger.pin import FileBasedPin
from user.options import UserOptionParser


def load_pin(user_options):
env_pin = os.environ.get("PIN", None)
if env_pin is not None:
env_pin = env_pin.encode()
pin = FileBasedPin(
user_options.pin_file,
default_pin=env_pin,
force_change=user_options.force_pin_change,
)
return pin


def configure_protocol_messages(protocol):
protocol.MESSAGES = {
"restart": "restart the SGX powHSM",
}


if __name__ == "__main__":
user_options = UserOptionParser("Start the powHSM manager for SGX",
with_pin=True,
with_tcpconn=True,
host_name="SGX",
default_tcpconn_port=7777).parse()

runner = ManagerRunner("powHSM manager for SGX",
lambda options: HSM2DongleSGX(options.tcpconn_host,
options.tcpconn_port,
options.io_debug),
load_pin, configure_protocol_messages)

runner.run(user_options)
19 changes: 14 additions & 5 deletions middleware/manager-tcp.py → middleware/manager_tcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,24 @@
from mgr.runner import ManagerRunner
from user.options import UserOptionParser


def configure_protocol_messages(protocol):
protocol.MESSAGES = {
"restart": "restart the TCPSigner",
}


if __name__ == "__main__":
user_options = UserOptionParser("Start the powHSM manager for TCPSigner",
with_pin=False,
with_tcpsigner=True).parse()
with_tcpconn=True,
host_name="TCPSigner").parse()

runner = ManagerRunner("powHSM manager for TCPSigner",
lambda options: HSM2DongleTCP(options.tcpsigner_host,
options.tcpsigner_port,
options.dongle_debug),
load_pin=lambda options: None)
lambda options: HSM2DongleTCP(options.tcpconn_host,
options.tcpconn_port,
options.io_debug),
load_pin=lambda options: None,
configure_protocol=configure_protocol_messages)

runner.run(user_options)
Loading

0 comments on commit fe93c34

Please sign in to comment.