Skip to content

Commit

Permalink
fitimagesign: add support for generic fitimage signing
Browse files Browse the repository at this point in the history
* Add signing class for generic FIT images using U-Boot tools for signing
* Add a documentation page
  • Loading branch information
iceaway authored Jan 12, 2025
1 parent 57ebb84 commit 946e14e
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 1 deletion.
3 changes: 2 additions & 1 deletion digsigserver/keyfiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ class KeyFiles:
'rkopteesign',
'uefisign',
'ueficapsulesign',
'ekbsign'
'ekbsign',
'fitimagesign'
]

def __init__(self, app: Sanic, signtype: str, machine_or_distro: str):
Expand Down
30 changes: 30 additions & 0 deletions digsigserver/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from digsigserver.signers.uefisign import UefiSigner
from digsigserver.signers.ueficapsulesign import UefiCapsuleSigner
from digsigserver.signers.ekbsign import EKBSigner
from digsigserver.signers.fitimagesign import FitImageSigner
from . import utils

# Signing can take a loooong time, so set a more reasonable
Expand Down Expand Up @@ -192,6 +193,35 @@ async def sign_handler_imx(req: request):
response = text("Signing error", status=500)
return response

@app.post("/sign/fitimage")
async def sign_handler_fitimage(req: request):
f = validate_upload(req, "artifact")
if not f:
return text("Invalid artifact", status=400)
with tempfile.TemporaryDirectory() as workdir:
try:
s = FitImageSigner(app, workdir)
except ValueError:
return text("Invalid parameters", status=400)

with open(os.path.join(workdir, "artifact"), "wb") as artifact:
artifact.write(f.body)

outfile = tempfile.NamedTemporaryFile(delete=False)
outfile.close()
if await asyncio.get_running_loop().run_in_executor(None, s.sign,
artifact.name,
None,
req.form.get("external_data_offset"),
req.form.get("mark_required"),
req.form.get("algo"),
req.form.get("keyname")):
await return_file(req, artifact.name, "artifact.signed")
response = None
else:
response = text("Signing error", status=500)
return response

@app.post("/sign/modules")
async def sign_handler_modules(req: request):
f = validate_upload(req, "artifact")
Expand Down
44 changes: 44 additions & 0 deletions digsigserver/signers/fitimagesign.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import os
import copy
from typing import Optional
from digsigserver.signers import Signer

from sanic import Sanic
from sanic.log import logger

class FitImageSigner (Signer):

keytag = 'fitimagesign'

def __init__(self, app: Sanic, workdir: str):
super().__init__(app, workdir, "imx")

def _prepare_path(self) -> dict:
env = dict(copy.deepcopy(os.environ))
curpath = env.get('PATH')
if curpath:
env['PATH'] += ':' + curpath
return env

def sign(self, fitimage: str,
dtb: Optional[str],
external_data_offset: Optional[str],
mark_required: Optional[bool],
algo: Optional[str],
keyname: str = "dev.key") -> bool:
private_key = self.keys.get("{}.key".format(keyname))
env = self._prepare_path()
cmd = [ 'mkimage', '-F', '-k', os.path.dirname(private_key) ]
if external_data_offset:
cmd += [ '-p', external_data_offset ]
if mark_required:
cmd += [ '-r' ]
if dtb:
cmd += [ '-K', dtb ]
if algo:
cmd +=[ '-o', algo ]

cmd += [ fitimage ]
result = self.run_command(cmd, env=env)
self.keys.cleanup()
return result
40 changes: 40 additions & 0 deletions doc/fitimage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Signing fitimages

## Prerequisites
The only tool required is `mkimage` from `u-boot-tools`.

## Keyfile storage layout
The private key used for signing the fitImage is expected in the following location:

${DIGSIGSERVER_KEYFILE_URI}/imx/dev.key

The name of the key can be customized with a REST API parameter, otherwise `dev` is default.

## REST API endpoint

Request type: `POST`

Endpoint: `/sign/fitimage`

Expected parameters:
* `artifact=<body>` - binary to be signed

Optional parameters:
* `external_data_offset=<offset>` - external data offset to be used during FIT signing
* `mark_required=<any value>` - if this parameter exists the key will be marked as required
* `algo=<signing algorithm>` - customize the signing algorithm
* `keyname=<name of the key to use>` - specify a keyname other than `dev`

Response: signed binary

Example usage:
curl --connect-timeout 30 --max-time 1800 --retry 1 --fail -X POST \
-F external_data_offset=2000 -F "artifact=@fitImage" \
-F mark_required=true -F keyname=devkey \
--output fitImage.signed http://$DIGSIG_SERVER_IP:$DIGSIG_SERVER_PORT/sign/fitimage


## Future improvements
* Enable including a device tree blob in which the public key is injected.
* Change the `imx` "machine" to something more logical, this is not machine dependent

0 comments on commit 946e14e

Please sign in to comment.