Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Middleware SGX admin tooling #199

Merged
merged 6 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions middleware/adm_ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from argparse import ArgumentParser
import logging
from ledger.hsm2dongle import HSM2DongleError
from comm.platform import Platform
from admin.misc import not_implemented, info, AdminError
from admin.unlock import do_unlock
from admin.onboard import do_onboard
Expand All @@ -33,8 +34,6 @@
from admin.verify_attestation import do_verify_attestation
from admin.authorize_signer import do_authorize_signer

DEFAULT_PIN_FILE = "pin.txt"
DEFAULT_PIN_CHANGE_FILE = "changePIN"
DEFAULT_ATT_UD_SOURCE = "https://public-node.rsk.co"


Expand Down Expand Up @@ -144,6 +143,7 @@ def main():

try:
options = parser.parse_args()
Platform.set(Platform.LEDGER)
actions.get(options.operation, not_implemented)(options)
sys.exit(0)
except AdminError as e:
Expand Down
128 changes: 128 additions & 0 deletions middleware/adm_sgx.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# 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 sys
from argparse import ArgumentParser
import logging
from ledger.hsm2dongle import HSM2DongleError
from comm.platform import Platform
from admin.misc import not_implemented, info, AdminError
from admin.unlock import do_unlock
from admin.onboard import do_onboard
from admin.pubkeys import do_get_pubkeys
from admin.changepin import do_changepin


def main():
logging.disable(logging.CRITICAL)

actions = {
"unlock": do_unlock,
"onboard": do_onboard,
"pubkeys": do_get_pubkeys,
"changepin": do_changepin,
}

parser = ArgumentParser(description="SGX powHSM Administrative tool")
parser.add_argument("operation", choices=list(actions.keys()))
parser.add_argument(
"-r",
"--port",
dest="sgx_port",
help="SGX powHSM listening port (default 7777)",
type=int,
default=7777,
)
parser.add_argument(
"-s",
"--host",
dest="sgx_host",
help="SGX powHSM host. (default 'localhost')",
default="localhost",
)
parser.add_argument("-p", "--pin", dest="pin", help="PIN.")
parser.add_argument(
"-n",
"--newpin",
dest="new_pin",
help="New PIN (only valid for 'changepin' operation).",
)
parser.add_argument(
"-a",
"--anypin",
dest="any_pin",
action="store_const",
help="Allow any pin (only valid for 'changepin' operation).",
default=False,
const=True,
)
parser.add_argument(
"-o",
"--output",
dest="output_file_path",
help="Output file (only valid for 'onboard' and 'pubkeys' "
"operations).",
)
parser.add_argument(
"-u",
"--nounlock",
dest="no_unlock",
action="store_const",
help="Do not attempt to unlock (only valid for 'changepin' and 'pubkeys' "
"operations).",
default=False,
const=True,
)
parser.add_argument(
"-v",
"--verbose",
dest="verbose",
action="store_const",
help="Enable verbose mode",
default=False,
const=True,
)

try:
options = parser.parse_args()
Platform.set(Platform.SGX, {
"sgx_host": options.sgx_host,
"sgx_port": options.sgx_port,
})
actions.get(options.operation, not_implemented)(options)
sys.exit(0)
except AdminError as e:
info(str(e))
sys.exit(1)
except HSM2DongleError as e:
info(str(e))
sys.exit(2)
except KeyboardInterrupt:
info("Interrupted by user!")
sys.exit(3)
except Exception as e:
info(str(e))
sys.exit(4)


if __name__ == "__main__":
main()
14 changes: 9 additions & 5 deletions middleware/admin/changepin.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
AdminError,
)
from .unlock import do_unlock
from comm.platform import Platform


def do_changepin(options):
Expand Down Expand Up @@ -62,10 +63,10 @@ def do_changepin(options):
mode = hsm.get_current_mode()
info(f"Mode: {mode.name.capitalize()}")

# We can only change the pin while in bootloader mode
if mode != HSM2Dongle.MODE.BOOTLOADER:
raise AdminError("Device not in bootloader mode. Disconnect and re-connect the "
"ledger and try again")
# In Ledger, we can only change the pin while in bootloader mode
if Platform.is_ledger() and mode != HSM2Dongle.MODE.BOOTLOADER:
raise AdminError("Device not in bootloader mode. "
f"{Platform.message("restart").capitalize()} and try again")

# Ask the user for a new pin if one has not been given
if new_pin is None:
Expand All @@ -76,6 +77,9 @@ def do_changepin(options):
info("Changing pin... ", options.verbose)
if not hsm.new_pin(new_pin):
raise AdminError("Failed to change pin")
info("Pin changed. Please disconnect and re-connect the ledger.")
info("Pin changed.", nl=Platform.is_sgx())
# We require a restart in Ledger only
if Platform.is_ledger():
info(f" Please {Platform.message("restart")}.")

dispose_hsm(hsm)
10 changes: 9 additions & 1 deletion middleware/admin/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@
import time
from getpass import getpass
from ledger.hsm2dongle import HSM2Dongle
from sgx.hsm2dongle import HSM2DongleSGX
from ledger.pin import BasePin
from .dongle_admin import DongleAdmin
from .dongle_eth import DongleEth
from comm.platform import Platform

PIN_ERROR_MESSAGE = ("Invalid pin given. It must be exactly 8 alphanumeric "
"characters with at least one alphabetic character.")
Expand Down Expand Up @@ -68,7 +70,13 @@ def not_implemented(options):

def get_hsm(debug):
info("Connecting to HSM... ", False)
hsm = HSM2Dongle(debug)
if Platform.is_ledger():
hsm = HSM2Dongle(debug)
elif Platform.is_sgx():
hsm = HSM2DongleSGX(Platform.options("sgx_host"),
Platform.options("sgx_port"), debug)
else:
raise AdminError("Platform not set or unknown platform")
hsm.connect()
info("OK")
return hsm
Expand Down
17 changes: 13 additions & 4 deletions middleware/admin/onboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
from .dongle_admin import DongleAdmin
from .unlock import do_unlock
from .certificate import HSMCertificate, HSMCertificateElement
from comm.platform import Platform

# TODO: this could perhaps be done with a different value.
# Currently unused but necessary for the attestation setup process.
Expand All @@ -50,8 +51,8 @@ def do_onboard(options):
head("### -> Onboarding and attestation setup", fill="#")
hsm = None

# Require an output file
if options.output_file_path is None:
# Ledger-only: require an output file
if Platform.is_ledger() and options.output_file_path is None:
raise AdminError("No output file path given")

# Validate pin (if given)
Expand All @@ -71,8 +72,8 @@ def do_onboard(options):

# Require bootloader mode for onboarding
if mode != HSM2Dongle.MODE.BOOTLOADER:
raise AdminError("Device not in bootloader mode. Disconnect and re-connect the "
"ledger and try again")
raise AdminError("Device not in bootloader mode. "
f"{Platform.message("restart").capitalize()} and try again")

# Echo check
info("Sending echo... ", options.verbose)
Expand Down Expand Up @@ -120,6 +121,14 @@ def do_onboard(options):

dispose_hsm(hsm)

if Platform.is_sgx():
head(["Onboarding done"])
return

if not Platform.is_ledger():
raise AdminError("Unsupported platform")

# **** Attestation setup is Ledger only (for now) ****
head(
[
"Onboarding done",
Expand Down
3 changes: 2 additions & 1 deletion middleware/admin/pubkeys.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from .misc import info, get_hsm, dispose_hsm, AdminError, wait_for_reconnection
from .unlock import do_unlock
from comm.bip32 import BIP32Path
from comm.platform import Platform

SIGNER_WAIT_TIME = 1 # second

Expand Down Expand Up @@ -65,7 +66,7 @@ def do_get_pubkeys(options):
# Modes for which we can't get the public keys
if mode in [HSM2Dongle.MODE.UNKNOWN, HSM2Dongle.MODE.BOOTLOADER]:
raise AdminError(
"Device not in app mode. Disconnect and re-connect the ledger and try again")
f"Device not in app mode. {Platform.message("restart").capitalize()}")

# Gather public keys
pubkeys = {}
Expand Down
8 changes: 5 additions & 3 deletions middleware/admin/unlock.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
AdminError,
ask_for_pin,
)
from comm.platform import Platform


def do_unlock(options, exit=True, no_exec=False, label=True):
Expand Down Expand Up @@ -65,8 +66,8 @@ def do_unlock(options, exit=True, no_exec=False, label=True):

# Modes for which we can't unlock
if mode == HSM2Dongle.MODE.UNKNOWN:
raise AdminError("Device mode unknown. Already unlocked? Otherwise disconnect "
"and re-connect the ledger and try again")
raise AdminError("Device mode unknown. Already unlocked? Otherwise "
f"{Platform.message("restart")} and try again")
if mode == HSM2Dongle.MODE.SIGNER or mode == HSM2Dongle.MODE.UI_HEARTBEAT:
raise AdminError("Device already unlocked")

Expand All @@ -87,9 +88,10 @@ def do_unlock(options, exit=True, no_exec=False, label=True):
raise AdminError("Unable to unlock: PIN mismatch")
info("PIN accepted")

# **** Ledger only ****
# Exit the bootloader, go into menu (or, if app is properly signed, into
# the app)
if exit:
if Platform.is_ledger() and exit:
autoexec = not (options.no_exec or no_exec)
info(f"Exiting to menu/app (execute signer: {bls(autoexec)})... ",
options.verbose)
Expand Down
2 changes: 2 additions & 0 deletions middleware/build/adm_sgx
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/bash
source $(dirname $0)/bld-docker adm_sgx
2 changes: 2 additions & 0 deletions middleware/build/all
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ BINDIR=$(realpath $BUILDDIR/../bin/)
echo "Building all..."

QUIET=1 $BUILDDIR/manager_ledger && \
QUIET=1 $BUILDDIR/manager_sgx && \
QUIET=1 $BUILDDIR/manager_tcp && \
QUIET=1 $BUILDDIR/adm_ledger && \
QUIET=1 $BUILDDIR/adm_sgx && \
QUIET=1 $BUILDDIR/lbutils && \
QUIET=1 $BUILDDIR/signapp && \
echo "" && sha256sum $BINDIR/*.tgz
65 changes: 65 additions & 0 deletions middleware/comm/platform.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# 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.

class Platform:
LEDGER = "Ledger"
SGX = "SGX"
X86 = "X86"
VALID_PLATFORMS = [LEDGER, SGX, X86]

MESSAGES = {
LEDGER: {
"restart": "disconnect and re-connect the ledger nano",
},
SGX: {
"restart": "restart the SGX powHSM",
},
X86: {
"restart": "restart the TCPSigner",
}
}

_platform = None
_options = None

@classmethod
def set(klass, plf, options={}):
if plf not in klass.VALID_PLATFORMS:
raise RuntimeError("Invalid platform given")
klass._platform = plf
klass._options = options

@classmethod
def is_ledger(klass):
return klass._platform == Platform.LEDGER

@classmethod
def is_sgx(klass):
return klass._platform == Platform.SGX

@classmethod
def options(klass, key):
return klass._options[key]

@classmethod
def message(klass, key):
return klass.MESSAGES[klass._platform][key]
Loading
Loading