Signature Provider Plugin#
This notebook describes how to setup a Signature Provider plugin
When signing the data with SPSDK, there are two options:
Using the local private key (not recommended)
Using the remote signing service(HSM)
Let’s look at the second option and setup Signature Provider
%run ../../init_notebook.ipynb
import pprint
pp = pprint.PrettyPrinter(indent=4)
PLUGINS_DIR = "plugins/" # change this to path to your workspace
VERBOSITY = "-v" # verbosity of commands, might be -v or -vv for debug or blank for no additional info
env: JUPYTER_SPSDK=1
Created `%!` as an alias for `%execute`.
HSM setup#
First, we need to start the custom HSM. In order to do that, open this notebook and follow the instructions there. Once you are done, come back and continue here.
Now the HSM should be up and running. In order to test the functionality of HSM from previous step, run a simple test:
import requests
#rsa2048 sign
response = requests.get("http://127.0.0.1:5000/signer/rsa2048/0?data=b'ABC'")
print(f"RSA2048: {response.json()}")
response = requests.get("http://127.0.0.1:5000/signer/secp384r1/0?data=b'ABC'")
print(f"SECP384R1: {response.json()}")
RSA2048: {'signature': 'QWjBWnbG7QtninaD6R9dQZqGiMZZskdVLCV1peXZEp43SJx3PATOoXTIQhvLhOZ5Q0f1683dtGAkEzb1aHKY05fIw2iPAGNHsL7IAe5nH0t3dOaCvemlodzAbb8GDpdahUHBURpnJOsgqYccZZOR6E3GSuIwD8qKBlZ7sGomtwzrBGuNHU5AG8U0J+8hLhExpEttd953mtnyMnC5aq3W30SbwU+7lZDAc2jIJn1PltVUetdHOVyGSPi4yAGZIlnzgYD8vpse2xlPP+3Ifdfuu3ckkNSZ0xzmK8adehKGTqD5hlpnP9iWPd7lio+82SovjmQ552RwwtRGbFmqC2qEkg=='}
SECP384R1: {'signature': 'b63Hysi7FGgy86+iI0Fdl2orzhKqK6UTUj5s309z7/iIUD5a+bqK6zGtKlHjzNi+6cFtetTc/yofbdZx0Au0KNdA69Zx3oIkwK69RuKlQtKnPUFYnyt6MDNiOOl5INk6'}
Signature Provider plugin#
Plugins extend the existing SPSDK functionality with additional features.
In order to use remote signing, a Signature Provider plugin used for communication with HSM must be implemented.
Explore the file plugins\sasp.py
. It will be used later on.
import os
plugins_dir = 'plugins/'
# The content of plugin will be printed here
SASP_PLUGIN = os.path.join(plugins_dir, 'sasp.py')
with open(SASP_PLUGIN, 'r') as f:
print(f.read())
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#
# Copyright 2020-2023 NXP
#
# SPDX-License-Identifier: BSD-3-Clause
"""Customer-specific Signature Provider."""
import base64
import requests
from spsdk.crypto.signature_provider import SignatureProvider
class SuperAwesomeSP(SignatureProvider):
"""Signature Provider based on a remote signing service."""
# identifier of this signature provider; used in yaml configuration file
sp_type = "sasp"
def __init__(self, key_number: int, key_type: str) -> None:
"""Initialize the Super Awesome SignatureProvider.
:param key_number: index of the key to use (rot_id from yaml config)
"""
self.url = "http://127.0.0.1:5000"
self.key_number = key_number
self.key_type = key_type
def sign(self, data: bytes) -> bytes:
"""Perform the signing.
:param data: Data to sign
:return: Signature
"""
endpoint = f"{self.url}/signer/{self.key_type}/{self.key_number}"
params = {"data": base64.b64encode(data)}
response = requests.get(endpoint, params=params)
self.check_response(response)
signature = response.json()["signature"]
data = base64.b64decode(signature)
return data
def verify_public_key(self, public_key: bytes) -> bool:
"""Verify if given public key matches private key.
:param data: Public key to verify
:return: True if public_key is matching private_key, False otherwise
"""
endpoint = f"{self.url}/verifier/{self.key_type}/{self.key_number}"
params = {"public_key": base64.b64encode(public_key)}
response = requests.get(endpoint, params=params)
self.check_response(response)
is_matching = response.json()["is_matching"]
return is_matching
@property
def signature_length(self) -> int:
"""Return length of the signature."""
return {"rsa2048": 256, "secp256r1": 64, "secp384r1": 96, "secp521r1": 132}[self.key_type]
@staticmethod
def check_response(response: requests.Response) -> None:
"""Raise if response is not 2xx."""
try:
response.raise_for_status()
except requests.HTTPError as e:
if response.text:
raise requests.HTTPError(
f"{str(e)}; Error Message: {response.text}",
request=e.request,
response=e.response,
)
else:
raise e
The only plugin requirement is that it contains a class derived from spsdk.crypto.SignatureProvider
base class.
The derived class has to implement:
sp_type: str
: class attribute that identifies the concrete implementation of SignatureProvidersign(bytes) -> bytes
: method which performs the actual signingsignature_length -> str
: property which returns a length of a signature
The derived class can also optionally implement:
info() -> str
: method which returns information about the signature provider (for debugging purposes). The default implementation returns a class name as a stringverify_public_key(bytes) -> bool
: method which verifies if a given public key matches a private key.
Omitting the implementation of optional methods such as
info()
does not break the functionality of application.
Signature Formats#
The signature must meet following formats:
RSA: Standard format
ECC: Extracted public numbers r+s stored in big endian or DER-formatted signature