diff --git a/Documentation/devel/building.rst b/Documentation/devel/building.rst index 261d090be5..b82529f859 100644 --- a/Documentation/devel/building.rst +++ b/Documentation/devel/building.rst @@ -187,15 +187,14 @@ The ``-Dsgx_driver`` parameter controls which SGX driver to use: * ``upstream`` (default) for upstreamed in-kernel driver (mainline Linux kernel 5.11+), -* ``dcap1.6`` for Intel DCAP version 1.6 or higher, but below 1.10, -* ``dcap1.10`` for Intel DCAP version 1.10 or higher, * ``oot`` for non-DCAP, out-of-tree version of the driver. The ``-Dsgx_driver_include_path`` parameter must point to the absolute path where the SGX driver was downloaded or installed in the previous step. For -example, for the DCAP version 1.41 of the SGX driver, you must specify -``-Dsgx_driver_include_path="/usr/src/sgx-1.41/include/"``. If this parameter is -omitted, Gramine's build system will try to determine the right path. +example, for the OOT driver installed at the default path, you can specify +``-Dsgx_driver_include_path="/opt/intel/linux-sgx-driver"``. If this parameter +is omitted, Gramine's build system will try to determine the right path, so, +it's usually not needed. .. note:: @@ -296,17 +295,32 @@ Additional build options .. _FSGSBASE: -Prepare a signing key ---------------------- +Signing key +----------- -Only for SGX enclave development, and if you haven't already, run the following -command:: +SGX enclaves need to build and signed using a 3072-bit RSA key. During development, +and if you haven't already, you can run the following command:: gramine-sgx-gen-private-key This command generates an |~| RSA 3072 key suitable for signing SGX enclaves and stores it in :file:`{HOME}/.config/gramine/enclave-key.pem`. This key needs to -be protected and should not be disclosed to anyone. +be protected and must not be disclosed to anyone. This is the default signing +method, but since this key is available in the clear, it is **not suitable** +for production deployments. + +For production deployments, you must use a key secured in a HSM. Gramine +supports signing the enclave using a key from Azure Key Vault Managed HSM. You +must have Azure Subscription with access to Azure Key Vault's Managed HSM, and +have a 3072-bit RSA private key with a public exponent 3 created in it. (Please +note that only Managed HSM supports setting public exponent), and must have +given access permissions to yourself or the one who will use the key created (as +'Managed HSM Crypto User' using `az keyvault role assignment`). You must also be +logged in using Azure CLI before signing the enclave. Please see `create_rsa_key +`__ +for creating the key in Azure Key Vault Managed HSM, and `gramine-sgx-sign +`__ for +the options to sign with Azure Key Vault. After signing the application's manifest, users may ship the application and Gramine binaries, along with an SGX-specific manifest (``.manifest.sgx`` @@ -320,7 +334,7 @@ Advanced: installing Linux kernel with FSGSBASE patches FSGSBASE patchset was merged in Linux kernel version 5.9. For older kernels it is available as `separate patches `__. -(Note that Gramine was prevously called *Graphene* and was hosted under a +(Note that Gramine was previously called *Graphene* and was hosted under a different organization, hence the name of the linked repository.) The following instructions to patch and compile a Linux kernel with FSGSBASE diff --git a/Documentation/manpages/gramine-sgx-sign.rst b/Documentation/manpages/gramine-sgx-sign.rst index aa0c166f58..7efca1481d 100644 --- a/Documentation/manpages/gramine-sgx-sign.rst +++ b/Documentation/manpages/gramine-sgx-sign.rst @@ -24,9 +24,18 @@ Command line arguments Path to the output manifest file (with Trusted Files expanded). -.. option:: --key key_file, -k key_file +.. option:: --keytype key_type, -t key_type - Path to the private key used for signing. + Type of signing key: `pem` for a local private key in .pem format or + `akv` for Azure Key Vault Managed HSM. + +.. option:: --key key_path, -k key_path + + Path to .pem file for a local private key or + `vault_url:key_name` for Azure Key Vault Managed HSM. For example, if + we have a key named sgx_sign_key available in the Managed HSM with + URL https://myakv-mhsm.managedhsm.azure.net, the key path will be + `https://myakv-mhsm.managedhsm.azure.net:sgx_sign_key` .. option:: --manifest manifest_file, -m manifest_file diff --git a/python/gramine-sgx-sign b/python/gramine-sgx-sign index fb14f156a7..7551b3b636 100755 --- a/python/gramine-sgx-sign +++ b/python/gramine-sgx-sign @@ -9,7 +9,7 @@ import os import click from graminelibos import ( - Manifest, get_tbssigstruct, sign_with_local_key, SGX_LIBPAL, SGX_RSA_KEY_PATH, + Manifest, get_tbssigstruct, sign_with_akv, sign_with_local_key, SGX_LIBPAL, SGX_RSA_KEY_PATH, ) @click.command() @@ -17,16 +17,20 @@ from graminelibos import ( help='Output .manifest.sgx file (manifest augmented with autogenerated fields)') @click.option('--libpal', '-l', type=click.Path(exists=True, dir_okay=False), default=SGX_LIBPAL, help='Input libpal file') -@click.option('--key', '-k', type=click.Path(exists=True, dir_okay=False), - default=os.fspath(SGX_RSA_KEY_PATH), - help='specify signing key (.pem) file') +@click.option('--keytype', '-t', type=click.STRING, default='pem', + help='Type of signing key: "pem" for a local private key in .pem format, ' + '"akv" for Azure Key Vault Managed HSM') +@click.option('--key', '-k', type=click.STRING, + default=SGX_RSA_KEY_PATH, + help='Signing key: path to .pem file for a local private key, ' + 'vault_url:key_name for Azure Key Vault Managed HSM') @click.option('--manifest', '-m', 'manifest_file', type=click.File('r', encoding='utf-8'), required=True, help='Input .manifest file') @click.option('--sigfile', '-s', help='Output .sig file') @click.option('--depfile', type=click.File('w'), help='Generate dependencies for .manifest.sgx ' 'and .sig files') @click.option('--verbose/--quiet', '-v/-q', default=True, help='Display details (on by default)') -def main(output, libpal, key, manifest_file, sigfile, depfile, verbose): +def main(output, libpal, keytype, key, manifest_file, sigfile, depfile, verbose): # pylint: disable=too-many-arguments manifest = Manifest.load(manifest_file) @@ -45,7 +49,24 @@ def main(output, libpal, key, manifest_file, sigfile, depfile, verbose): today = datetime.date.today() sigstruct = get_tbssigstruct(output, today, libpal, verbose=verbose) - sigstruct.sign(sign_with_local_key, key) + + if keytype == 'akv': + sign_fn = sign_with_akv + if verbose == True: + key_details = key.rsplit(':', 1) + vault_url = key_details[0] + key_name = key_details[1] + print('Signing with key', key_name, 'from Azure Key Vault Managed HSM:', vault_url) + elif keytype == 'pem': + key = os.fspath(key) + sign_fn = sign_with_local_key + if verbose == True: + print('Signing with local private key:', key) + else: + print(f"Invalid keytype specified: `{keytype}`!") + exit(1) + + sigstruct.sign(sign_fn, key) with open(sigfile, 'wb') as f: f.write(sigstruct.to_bytes()) diff --git a/python/graminelibos/__init__.py b/python/graminelibos/__init__.py index f9a3b7e66a..bc33b31b2b 100644 --- a/python/graminelibos/__init__.py +++ b/python/graminelibos/__init__.py @@ -22,5 +22,5 @@ from .manifest import Manifest, ManifestError if _CONFIG_SGX_ENABLED: from .sgx_get_token import get_token - from .sgx_sign import get_tbssigstruct, sign_with_local_key, SGX_LIBPAL, SGX_RSA_KEY_PATH + from .sgx_sign import get_tbssigstruct, sign_with_akv, sign_with_local_key, SGX_LIBPAL, SGX_RSA_KEY_PATH from .sigstruct import Sigstruct diff --git a/python/graminelibos/sgx_sign.py b/python/graminelibos/sgx_sign.py index aeda598760..2b1a2a4246 100644 --- a/python/graminelibos/sgx_sign.py +++ b/python/graminelibos/sgx_sign.py @@ -537,8 +537,58 @@ def get_tbssigstruct(manifest_path, date, libpal=SGX_LIBPAL, verbose=False): return sig +def sign_with_akv(data, key): + """Signs *data* using *key* from Azure Key Vault's Managed HSM. + + Function used to generate an RSA signature over provided data using a 3072-bit private key with + the public exponent of 3 (hard Intel SGX requirement on the key size and the exponent). + Suitable to be used as a callback to :py:func:`graminelibos.Sigstruct.sign()`. This function + requires that the user has an active subscription to Azure and Azure Key Vault's Managed HSM + (MHSM), a 3072-bit RSA key created in the MHSM enabled for signing and the user is logged into + Azure CLI. This function qualifies for production signing. + + Args: + data (bytes): Data to calculate the signature over. + key (str): Details of RSA private key in the AKV's Managed HSM in vault_url:key_name format. + + Returns: + (int, int, int): Tuple of exponent, modulus and signature respectively. + """ + + from azure.identity import DefaultAzureCredential + from azure.keyvault.keys import KeyClient + from azure.keyvault.keys.crypto import CryptographyClient, SignatureAlgorithm + + key_details = key.rsplit(':', 1) + vault_url = key_details[0] + key_name = key_details[1] + + credential = DefaultAzureCredential(exclude_managed_identity_credential=True, + exclude_visual_studio_code_credential=True, + exclude_environment_credential=True, + exclude_shared_token_cache_credential=True, + exclude_powershell_credential=True) + + key_client = KeyClient(vault_url=vault_url, credential=credential) + + rsa_key = key_client.get_key(key_name) + + crypto_client = CryptographyClient(rsa_key, credential=credential) + + digest = hashlib.sha256(data).digest() + + result = crypto_client.sign(SignatureAlgorithm.rs256, digest) + signature = result.signature + + exponent_int = int.from_bytes(rsa_key.key.e, byteorder='big') + modulus_int = int.from_bytes(rsa_key.key.n, byteorder='big') + signature_int = int.from_bytes(signature, byteorder='big') + + return exponent_int, modulus_int, signature_int + + def sign_with_local_key(data, key): - """Signs *data* using *key*. + """Signs *data* using local *key* in .pem format. Function used to generate an RSA signature over provided data using a 3072-bit private key with the public exponent of 3 (hard Intel SGX requirement on the key size and the exponent). @@ -551,6 +601,7 @@ def sign_with_local_key(data, key): Returns: (int, int, int): Tuple of exponent, modulus and signature respectively. """ + proc = subprocess.Popen( ['openssl', 'rsa', '-modulus', '-in', key, '-noout'], stdout=subprocess.PIPE)