Kelvin Zhang | 059bf6e | 2022-08-12 14:03:41 -0700 | [diff] [blame^] | 1 | #!/usr/bin/env python3 |
| 2 | # |
| 3 | # Copyright (C) 2022 The Android Open Source Project |
| 4 | # |
| 5 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | # you may not use this file except in compliance with the License. |
| 7 | # You may obtain a copy of the License at |
| 8 | # |
| 9 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | # |
| 11 | # Unless required by applicable law or agreed to in writing, software |
| 12 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | # See the License for the specific language governing permissions and |
| 15 | # limitations under the License. |
| 16 | |
| 17 | import common |
| 18 | import logging |
| 19 | from common import OPTIONS |
| 20 | |
| 21 | logger = logging.getLogger(__name__) |
| 22 | |
| 23 | |
| 24 | class PayloadSigner(object): |
| 25 | """A class that wraps the payload signing works. |
| 26 | |
| 27 | When generating a Payload, hashes of the payload and metadata files will be |
| 28 | signed with the device key, either by calling an external payload signer or |
| 29 | by calling openssl with the package key. This class provides a unified |
| 30 | interface, so that callers can just call PayloadSigner.Sign(). |
| 31 | |
| 32 | If an external payload signer has been specified (OPTIONS.payload_signer), it |
| 33 | calls the signer with the provided args (OPTIONS.payload_signer_args). Note |
| 34 | that the signing key should be provided as part of the payload_signer_args. |
| 35 | Otherwise without an external signer, it uses the package key |
| 36 | (OPTIONS.package_key) and calls openssl for the signing works. |
| 37 | """ |
| 38 | |
| 39 | def __init__(self, package_key=None, private_key_suffix=None, pw=None, payload_signer=None): |
| 40 | if package_key is None: |
| 41 | package_key = OPTIONS.package_key |
| 42 | if private_key_suffix is None: |
| 43 | private_key_suffix = OPTIONS.private_key_suffix |
| 44 | |
| 45 | if payload_signer is None: |
| 46 | # Prepare the payload signing key. |
| 47 | private_key = package_key + private_key_suffix |
| 48 | |
| 49 | cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"] |
| 50 | cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"]) |
| 51 | signing_key = common.MakeTempFile(prefix="key-", suffix=".key") |
| 52 | cmd.extend(["-out", signing_key]) |
| 53 | common.RunAndCheckOutput(cmd, verbose=True) |
| 54 | |
| 55 | self.signer = "openssl" |
| 56 | self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key, |
| 57 | "-pkeyopt", "digest:sha256"] |
| 58 | self.maximum_signature_size = self._GetMaximumSignatureSizeInBytes( |
| 59 | signing_key) |
| 60 | else: |
| 61 | self.signer = payload_signer |
| 62 | self.signer_args = OPTIONS.payload_signer_args |
| 63 | if OPTIONS.payload_signer_maximum_signature_size: |
| 64 | self.maximum_signature_size = int( |
| 65 | OPTIONS.payload_signer_maximum_signature_size) |
| 66 | else: |
| 67 | # The legacy config uses RSA2048 keys. |
| 68 | logger.warning("The maximum signature size for payload signer is not" |
| 69 | " set, default to 256 bytes.") |
| 70 | self.maximum_signature_size = 256 |
| 71 | |
| 72 | @staticmethod |
| 73 | def _GetMaximumSignatureSizeInBytes(signing_key): |
| 74 | out_signature_size_file = common.MakeTempFile("signature_size") |
| 75 | cmd = ["delta_generator", "--out_maximum_signature_size_file={}".format( |
| 76 | out_signature_size_file), "--private_key={}".format(signing_key)] |
| 77 | common.RunAndCheckOutput(cmd, verbose=True) |
| 78 | with open(out_signature_size_file) as f: |
| 79 | signature_size = f.read().rstrip() |
| 80 | logger.info("%s outputs the maximum signature size: %s", cmd[0], |
| 81 | signature_size) |
| 82 | return int(signature_size) |
| 83 | |
| 84 | def Sign(self, in_file): |
| 85 | """Signs the given input file. Returns the output filename.""" |
| 86 | out_file = common.MakeTempFile(prefix="signed-", suffix=".bin") |
| 87 | cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file] |
| 88 | common.RunAndCheckOutput(cmd) |
| 89 | return out_file |