SignatureServer

From SEGGER Wiki
Jump to: navigation, search

The SEGGER Signature Server is a network-based signing server that allows creating digital signatures for data while keeping the signing key secret.

The Signature Server is intended for use where many signatures need to be generated by a number of different users. For example, in a production environment where firmware files need to be signed for every release, the signature server allows all connected production systems to sign data while maintaining privacy of the signing key. This way, all release files and releases can be signed with a single production-key, while none of the users need to have access to the private key. For verification, only a single public key needs to be distributed.

The Server allows signing data with the RSA asymmetric cryptography algorithm. Data is signed by sending the data's hash to the server. An according signature is generated and returned to the requester. Anyone can then use the public key to verify that the data was signed by someone having access to the matching private key.

The SEGGER Signature Server is based on SEGGER's well-established emSecure library of cryptographic signing tools and primitives. It uses the industry-standard signature scheme RSA-PSS. The default configuration uses 2048bit RSA keys and SHA1 for signing. However, for PSS, a whole set of modern digest algorithms are available, and RSA keys between 1024 and 8192 bits are supported, keys of at least 2048 bits being recommended.

Signing with larger keys can easily be sped up by having multiple servers with the same key. Signing tasks can thus be balanced between the different servers.

The SEGGER Signature Server is an embedded firmware solution for single-chip microcontroller systems. It runs on the off-the-shelf, low cost ST NUCLEO-H743ZI2 and NUCLEO-H723ZG hardware platform. The firmware uses SEGGER emPower OS components. Due to it's modular design and hardware abstraction the project is easily portable to many other platforms, in case the standard hardware is unsuitable or unavailable.

Use Cases

Signing Software Releases

The Signature Server can be used by a software development team that needs to sign all firmware and application images that are part of a release.

None of the developers should have access to a signing key that represents the company. But in a traditional setup, each developer would be required to have a copy of the private key to sign a release file. With the Signature Server, no developer is required to have direct access to the private key. Even better, none of them will be able to obtain the key, even if they intended to. They can only sign their release images while they are in the same network as the Signature Server.

signatureserver usecase software releases.png

Another option is to only give a build-server access to the Signature Server. When configured appropriately, only official, traceable builds from the build-server are automatically signed by the build-server as part of the build- and release-process. Then they can be included in the generated release packages as part of the build-server process.

signatureserver usecase buildserver.png

Signing Hardware Units

The Signature Server can also be used in hardware production to make sure that device units and firmware cannot be cloned.

A manufacturer can sign specific, unchangeable hardware properties, such as a unique hardware ID of the target processor. The signature can then be flashed onto the device together with a production firmware. To improve device security, the firmware can then verify the signature against the hardware during each power-up, making sure it is running on signed authentic hardware.

Like in the case of developers, no production machine should have access to the hardware signing key. With the Signature Server, each production machine can read the properties of the unit to be produced and request a signature from the server. Production of authentic units is only possible with access to the Signature Server. The private key required for signature creation cannot be extracted.

Signing Device Keys

Similarly to signing hardware units, the Signature Server can be used as a trusted authority to sign further keys, such as per-device keys.

A signing/certificate scheme could be developed where a hardware device creates its own key pair. It transmits its public key through the manufacturer to the Signature Server to receive a signature of the public key. Now the firmware can authenticate itself at the manufacturer or to another device with its signed public key to establish a fully encrypted communication channel.

Sources

The SignatureServer release contains the sources for the SEGGER Signature Server and a client-side implementation. It consists of the following components:

  • embOS Cortex-M ES
  • emCrypt PRO
  • emSecure RSA
  • emNet IPv4 & IPv6 PRO
  • emNet Synopsys QOS ethernet driver
  • Business logic for Signature Server
  • Configuration files
  • Client implementation in python
  • Simplified C client API
  • Additional example programs using emSecure and emCrypt

Hardware Setup

To use the SEGGER Signature Server, the server firmware needs to be built and flashed to an embedded device.

To build the server, you need

  • SEGGER Embedded Studio >= 5.60
  • Server hardware, one of:
    • ST NUCLEO-H743ZI
    • ST NUCLEO-H743ZI2
    • ST NUCLEO-H723ZG

Build the board-specific release configuration of the contained SignatureServer.emProject using SEGGER Embedded Studio. Make sure to use the release configuration; the debug configuration does not protect the device against read-out. Anyone with hardware access could then read out the private keys.

Alternatively, you can use the emBuild tool of Embedded Studio to directly build the release configuration.

~> emBuild -config NUCLEO-H743ZI-2-Release SignatureServer.emProject

Once the firmware is built, it can be flashed to the corresponding board. You can use Embedded Studio or any of the other standard tools to do that. (e.g. JLinkExe or JFlashExe).

If the board was used before, or existing keys need to be removed from a Signature Server, erase the full flash of the target ("Target" => "Erase All" in Embedded Studio), then download the application to it.

The device will be operational after a power-cycle.

NOTE: When read-out protection is enabled (the default), the on-board ST-Link must be disconnected after the firmware has been flashed, as otherwise the ST-Link would try to access the MCUs flash, resulting in a system halt. This can be done by supplying power such that the ST-Link remains unpowered (5V_EXT or VIN). For further instructions, see the boards User Manual.

Build-time configuration

All relevant configuration options of the firmware are set in the Config/APP_Conf.h header. See there for more information on specific options.

Changing the Ethernet configuration

The MAC address of the device is configured with the APP_CONF_HW_ADDR macro. The macro must be a six-character string that is directly used as the 6-byte MAC address. Consider that each MAC address can only be used by a single device. It needs to be set to an address that is unique within at least the local ethernet.

In the default configuration, the MAC is hard-coded to 5E:33:E7:00:00:01. This is a locally administered address as per IEEE 802.2011 .

Changing the IP network configuration

In the default configuration, the signature server will try to configure its IP address dynamically via DHCP. If you don't use a DHCP server, or the IP address should not be configured dynamically, edit the following macros:

  • Define APP_CONF_USE_DHCP to 0.
  • Define APP_CONF_IP_ADDR, APP_CONF_SUBNET_MASK, APP_CONF_GW_ADDR and APP_CONF_DNS_ADDR as seen in the examples in that file.

Setting Gateway and DNS Server to 0.0.0.0 will make the server unreachable for clients outside of the local network. However, for additional security, any incoming traffic to the server should then be filtered by your network administrators.

The TCP port is set to 33030 in the default configuration. To change it, change the APP_CONF_LISTENPORT macro as described.

Using The Server

In the subdirectory signatureclient/ the python3 scripts client.py and cryptostore.py can be found. Additionally, a simplified C API can be found in SIGAPI.*.

To use the python client implementation, you need:

  • Python 3
  • pyOpenSSL

client.py can be used to send commands and requests to an existing server, or to verify an existing signature with a public key. (Note that client.py depends on cryptostore.py)

cryptostore.py can also used to create and translate local files with cryptographic material like keys and signatures. It can translate between PEM and emSecure formats and also sign and verify data with local public and private keys.

Below you find specific use cases for these tools. To see all supported commands, execute them with the --help parameter.

To use the C SIGAPI, you also need the SEGGER_SYS module that corresponds to your operating system, which can be found in SEGGER/SYS/. (Or you need to replace the socket interfacing code with your own implementation -- see SEGGER_SYS_IP_* in SIGAPI.*.) For an example use, check signatureclient/tests/test_c_SIGAPI.c and its Makefile.

Listing existing keys

Use client.py -s <server-address> listkeys to list all keys on the server:

	~> ./client.py -s 192.168.11.41 listkeys
	Has RSA Private Key 'tests/private.2048'
	Has RSA Private Key 'tests/private.4096'
	Has RSA Private Key 'ProductionKey'

Creating or uploading keys

signatureserver cli keygen.gif

You can upload existing keys with the keyload command of client.py, or generate and upload new keys with the keygen command:

	~> openssl genrsa 2048 > key.private

	~> openssl rsa -in key.private -out key.public -pubout -outform PEM

	# upload an existing private key:
	~> ./client.py -s 192.168.11.41 listkeys

	~> ./client.py -s 192.168.11.41 keyload -n test123 key.private

	~> ./client.py -s 192.168.11.41 listkeys
	Has RSA Private Key 'test123'

Note that here the input key was in OpenSSL PEM format. keyload supports keys in both emSecure and PEM format.

	# generate and upload a new key:
	~> ./client.py -s 192.168.11.41 keygen -n test456 myKey
	Generating key 'test456'

	~> ./client.py -s 192.168.11.41 listkeys
	Has RSA Private Key 'test456'

	~> ls myKey*
	myKey.priv  myKey.pub

Here an RSA key with 2048 bits was generated and uploaded. The private and public keys were also saved as myKey.priv and myKey.pub in emSecure format. To change the parameters of the key (e.g. the bits), see additional parameters of the keygen command. You can also save the key in PEM format by adding --openssl.

The local copy of the public key needs to be published.

The local copies of the private key should be either stored in a safe location or destroyed (e.g. on a USB stick or printed on paper). These can NOT be recovered once destroyed!

Note that the private key was - in both cases - created on your local computer and then uploaded to the server. This has the problem that some private parts of the key remain in the main memory for an undetermined amount of time and may thus be recoverable. Best practice for key generation is to use a live CD or USB stick to boot from, generate the key in a RAM disk, create a physical backup and put it in a safe, and then turn the computer off afterwards, so the main memory contents are destroyed. Leave it off for a few minutes.

Signing data

signatureserver cli sign verify.gif

To sign a local file, use the client.py tool:

	~> ./client.py -s 192.168.11.41 sign -n test456 datafile
	Signing file 'datafile' with key 'test456'

	~> ls datafile*
	datafile datafile.sig

Additional parameters may be given to the sign command. E.g. the signature digest can be chosen with --digest <...>. The default digest is sha1, which was chosen for backwards compatibility, but is easily broken these days. The following digests are supported: sha1 sha224 sha256 sha384 sha512 sha512_224 sha512_256 sha3-224 sha3-256 sha3-384 sha3-512.

Best practice is to choose one of sha256, sha512, sha3_256 and sha3_512.

Verifying a signature

Use the verify command of client.py to verify a signature:

	~> ./client.py verify -i keyfile.pub datafile.sig datafile
	Verifying signature 'datafile.sig' for 'datafile' with key 'keyfile.pub'.
	Signature valid.

Note that if a digest was specified while signing, the same digest must be specified when verifying the signature, or verification will fail:

	~> ./client.py verify -d sha256 -i keyfile.pub datafile.sig datafile
	Verifying signature 'datafile.sig' for 'datafile' with key 'keyfile.pub'.
	Verification failed!

Advanced features

cryptostore.py provides some advanced features with respect to keys. It is all about the cryptographic materials used to encrypt, sign or verify data. You can execute ./cryptostore.py -h to see all available commands.

It can be used to generate local keys, sign and verify data with local public and private keys, and it can be used to translate keys between emSecure and OpenSSL PEM format, thus making it possible to verify server-side generated signatures with the standard openssl tool.

Generating local key pairs

Generating a local keypair works just the same as with client.py, only that the key is not automatically uploaded.

	~> ./cryptostore.py myKey keygen
	Generating keypair with 2048 bits in 'myKey'*

	~> ls myKey*
	myKey.priv  myKey.pub

Using local keys

To sign or verify, simply use the sign and verify commands of cryptostore.py. However, note that to sign, the private key must be accessible. Signing via a signature server is not supported - use client.py to do that.

	~> ./cryptostore.py myKey.priv sign data data.sig
	Signing 'data' with key 'myKey.priv' to signature 'data.sig'*.

	~> ls data.sig*
	data.sig

	~> ./cryptostore.py myKey.pub verify data.sig data
	Verifying signature 'data.sig' for 'data' with key 'myKey.pub'.
	Signature valid.

As with client.py, the digest can be selected with --digest. Generated signatures are compatible with client.py.

Translating between emSecure and OpenSSL PEM formats

Public and private keys as well as signatures can be translated between the emSecure and OpenSSL compatible formats.

	~> ls myKey.pub*
	myKey.pub

	~> ./cryptostore.py myKey.pub translate myKey.pub -o
	Translating 'myKey.pub' to 'myKey.pub'* in emSecure and SSL format

	~> ls myKey.pub*
	myKey.pub myKey.pub myKey.pub.ssl


	# Note that to translate a signature from openssl compatible format,
	# you must give the `-s` parameter. That is because openssl-style
	# signatures cannot be identified by its contents.
	~> ./cryptostore.py data.sig translate data.sig -s -o
	Translating 'data.sig' to 'data.sig'* in emSecure format

	~> ls data.sig*
	data.sig

Compatibility with OpenSSL

A standard signature scheme is used for signing of data. So when public key and signature were translated into an OpenSSL compatible format, they can be verified by OpenSSL:

	~> openssl dgst -verify myKey.pub.ssl -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-2 -sigopt rsa_mgf1_md:sha1 -sha1 -signature data.sig.ssl data
	Verified OK

Note that again the same digest must be passed to OpenSSL as when signing the data. If no digest was given while signing, the default of SHA1 was used, as in the example.

Additional Examples

Additional example programs using emSecure/emCrypt can be found in Windows/SECURE/. Provided are copies of programs from emSecure that can be used to generate and print keys, and to sign data and verify signatures. They can be used via the command line interface of Windows, Linux or Mac OS X. CMake can be used to build them:

	mkdir Output
	cd Output
	cmake ..
	make

The resulting executables can then be found in Output/bin/. However, most of their functionality replicates that of the signatureclient.

Testing

Server- and client-side implementations can be tested with the Makefile in signatureclient/tests/. It assumes to find a new, running server instance at the IP address set in the Makefile. (See "Hardware Setup" above.)

For the python client and the server, key uploading, signing, verification and signature compatibility to OpenSSL are part of that test suite. A number of different digests are tested for a number of different key sizes.

For the C SIGAPI implementation, only basic signing is tested, as the C API does not support more than that.

Safely installing keys with emKeyGenRSA

The additional example tools include the emKeyGenRSA program, which allows deterministically generating keys from passwords. I.e. instead of saving a backup of a private key, you can pick and save a password in a secure location, and use this password to generate an RSA key.

This section will help you create a linux live system to safely install keys to a signature server. Keys will be generated on the live system, but they will only reside in RAM, so after a power-down all key material will be safely removed from that computer.

Download a Linux live system. Here we will use KALI Linux, as it allows using additional data partitions and has all the needed tools.

Install the live system to a USB stick: (here /dev/sdb is the USB stick device)

	cat kali-linux-2021.3a-live-amd64.iso > /dev/sdb

Add a data partition with a filesystem to the setup. For KALI Linux, see https://www.kali.org/docs/usb/usb-persistence/

Mount the new partition and copy the python client tools and the keygen executable to it. Here we assume this partition to be /dev/sdb3 and that you manually need to mount it. If you followed the example for KALI, your partition will be mounted automatically over the linux root once you boot it.

	mount /dev/sdb3 /mnt
	mkdir /mnt/signature-tools
	cp signatureclient/* emKeyGenRSA /mnt/signature-tools
	umount /mnt

Boot the USB stick and log in. (For KALI, you need to boot Live USB Persistence mode.)

Configure and setup the network such that it can reach the running signature server. Test the connection (e.g. with ping). It is best practice to only have these two devices in a network, with nothing else. I.e. directly connect the computer to the signature server, with no other network devices connected. Otherwise some device may be able to capture the private key data that is transmitted over network.

Open a terminal with a root shell and mount the data partition with the tools. Then go to /dev/shm, so that the generated key will only reside in a RAM disk and is never stored to a persistent medium. (/dev/shm usually is a ramdisk-style tmpfs).

Make sure no SWAP is used, or disable it with swapoff.

Run the emKeyGenRSA with appropriate command line to generate the wanted key, the use the python client tools to upload it to the server, and verify with listkeys. Also try signing some data to make sure the key is ok.

	sudo -s
	mount /dev/sdb3 /mnt
	cd /dev/shm
	/mnt/signature-tools/emKeyGenRSA -k <keyname> -pw <password>
	/mnt/signature-tools/client.py -s <keyserver> keyload -n <keyname> <keyname>.prv
	/mnt/signature-tools/client.py -s <keyserver> listkeys
	/mnt/signature-tools/client.py -s <keyserver> sign -n <keyname> <some file>
	umount /mnt

Power down the computer. Optionally, wipe or destroy the USB stick. That is not really needed, as all was done in a RAM disk, but if you want to be extra secure, sure!

Your signature server is ready for use now. Deploy it in your network.