Skip to content

Commit

Permalink
scripts/bootloader: Add ed25519/sha512 to scripts
Browse files Browse the repository at this point in the history
Python scripts implementing ed25519 and sha512 support needed
for nsib image signing.

Signed-off-by: Lukasz Fundakowski <lukasz.fundakowski@nordicsemi.no>
  • Loading branch information
fundakol committed Nov 21, 2024
1 parent d839e3c commit adc2886
Show file tree
Hide file tree
Showing 8 changed files with 533 additions and 105 deletions.
33 changes: 26 additions & 7 deletions scripts/bootloader/asn1parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause


from subprocess import check_output
import argparse
import re
import sys
import argparse
from subprocess import check_output

from cryptography.hazmat.primitives.asymmetric import ed25519

def get_ecdsa_signature(der, clength):

def get_ecdsa_signature(der: bytes, clength: int) -> bytes:
# The der consists of a SEQUENCE with 2 INTEGERS (r and s)
# The expected byte format of the file is
# 0: type(SEQ), len(SEQ)
Expand All @@ -24,7 +26,7 @@ def get_ecdsa_signature(der, clength):
# leading 0 byte.
# clength is the expected length of r and s.
# The following code parses the output of openssl asnparse which prints
# the values in hex, together with human-readble metadata.
# the values in hex, together with human-readable metadata.

# Disable pylint error as 'input' keyword has specific handling in 'check_output'
# pylint: disable=unexpected-keyword-arg
Expand All @@ -36,13 +38,19 @@ def get_ecdsa_signature(der, clength):
return sig


def get_ed25519_signature(der: bytes) -> bytes:
private_key = ed25519.Ed25519PrivateKey.generate()
signature = private_key.sign(der)
return signature


def parse_args():
parser = argparse.ArgumentParser(
description='Decode DER format using OpenSSL.',
formatter_class=argparse.RawDescriptionHelpFormatter,
allow_abbrev=False)

parser.add_argument('-a', '--alg', required=True, choices=['rsa', 'ecdsa'],
parser.add_argument('-a', '--alg', required=True, choices=['rsa', 'ecdsa', 'ed25519'],
help='Expected algorithm')
parser.add_argument('-c', '--contents', required=True, choices=['signature'],
help='Expected contents')
Expand All @@ -55,9 +63,20 @@ def parse_args():
return args


if __name__ == '__main__':
def main() -> int:
args = parse_args()
assert args.alg == 'ecdsa' # Only ecdsa is currently supported.

if args.alg == 'ecdsa':
if args.contents == 'signature':
sys.stdout.buffer.write(get_ecdsa_signature(args.infile.read(), 32))
elif args.alg == 'ed25519':
if args.contents == 'signature':
sys.stdout.buffer.write(get_ed25519_signature(args.infile.read()))
else:
sys.exit(f'Algorythm not supported {args.alg}')

return 0


if __name__ == '__main__':
sys.exit(main())
51 changes: 39 additions & 12 deletions scripts/bootloader/do_sign.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,21 @@
# Copyright (c) 2018 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause


import sys
import argparse
import hashlib
from ecdsa import SigningKey
import io
import sys

from cryptography.hazmat.primitives.serialization import load_pem_private_key
from ecdsa.keys import SigningKey # type: ignore[import-untyped]

def parse_args():

def parse_args(argv=None):
parser = argparse.ArgumentParser(
description='Sign data from stdin or file.',
formatter_class=argparse.RawDescriptionHelpFormatter,
allow_abbrev=False)
allow_abbrev=False
)

parser.add_argument('-k', '--private-key', required=True, type=argparse.FileType('rb'),
help='Private key to use.')
Expand All @@ -25,15 +27,40 @@ def parse_args():
parser.add_argument('-o', '--out', '-out', required=False, dest='outfile',
type=argparse.FileType('wb'), default=sys.stdout.buffer,
help='Write the signature to the specified file instead of stdout.')
parser.add_argument(
'--algorithm', '-a', dest='algorithm', help='Signing algorithm (default: %(default)s)',
action='store', choices=['ecdsa', 'ed25519'], default='ecdsa',
)

args = parser.parse_args()
args = parser.parse_args(argv)

return args


if __name__ == '__main__':
args = parse_args()
private_key = SigningKey.from_pem(args.private_key.read())
data = args.infile.read()
def sign_with_ecdsa(private_key_file: io.BytesIO, input_file: io.BytesIO, output_file: io.BytesIO) -> int:
private_key = SigningKey.from_pem(private_key_file.read())
data = input_file.read()
signature = private_key.sign(data, hashfunc=hashlib.sha256)
args.outfile.write(signature)
output_file.write(signature)
return 0


def sign_with_ed25519(private_key_file: io.BytesIO, input_file: io.BytesIO, output_file: io.BytesIO) -> int:
private_key = load_pem_private_key(private_key_file.read(), password=None)
data = input_file.read()
signature = private_key.sign(data)
output_file.write(signature)
return 0


def main(argv=None) -> int:
args = parse_args(argv)
if args.algorithm == 'ecdsa':
return sign_with_ecdsa(args.private_key, args.infile, args.outfile)
if args.algorithm == 'ed25519':
return sign_with_ed25519(args.private_key, args.infile, args.outfile)
return 1


if __name__ == '__main__':
sys.exit(main())
35 changes: 29 additions & 6 deletions scripts/bootloader/hash.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,43 @@
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause

"""
Hash content of a file.
"""

import hashlib
import sys
import argparse
from intelhex import IntelHex


HASH_FUNCTION_FACTORY = {
'sha256': hashlib.sha256,
'sha512': hashlib.sha512,
}


def parse_args():
parser = argparse.ArgumentParser(
description='Hash data from file.',
formatter_class=argparse.RawDescriptionHelpFormatter,
allow_abbrev=False)

parser.add_argument('--infile', '-i', '--in', '-in', required=True,
help='Hash the contents of the specified file. If a *.hex file is given, the contents will '
'first be converted to binary, with all non-specified area being set to 0xff. '
'For all other file types, no conversion is done.')
parser.add_argument(
'--infile', '-i', '--in', '-in', required=True,
help='Hash the contents of the specified file. If a *.hex file is given, the contents will '
'first be converted to binary, with all non-specified area being set to 0xff. '
'For all other file types, no conversion is done.'
)
parser.add_argument(
'--type', '-t', dest='hash_function', help='Hash function (default: %(default)s)',
action='store', choices=HASH_FUNCTION_FACTORY.keys(), default='sha256'
)

return parser.parse_args()


if __name__ == '__main__':
def main():
args = parse_args()

if args.infile.endswith('.hex'):
Expand All @@ -33,4 +49,11 @@ def parse_args():
to_hash = ih.tobinstr()
else:
to_hash = open(args.infile, 'rb').read()
sys.stdout.buffer.write(hashlib.sha256(to_hash).digest())

hash_function = HASH_FUNCTION_FACTORY[args.hash_function]
sys.stdout.buffer.write(hash_function(to_hash).digest())
return 0


if __name__ == '__main__':
sys.exit(main())
Loading

0 comments on commit adc2886

Please sign in to comment.