Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for setting of a profile for a TPM 2 #716

Merged
merged 11 commits into from
Jul 22, 2024

Conversation

stefanberger
Copy link
Owner

@stefanberger stefanberger commented Jul 15, 2022

This PR adds support for setting of profiles for a TPM 2 that restrict what algorithms are available to the TPM 2. It requires libtpms v0.10 with the profile support.

To setup an instance of swtpm with a specific profile one should use the swtpm_setup command like this:

swtpm_setup --tpm2 --tpmstate /tmp/myvtpm --create-ek-cert --create-platform-cert --profile '{"name":"default-v1"}' 

A profile can only be applied to a swtpm instance the very first time the instance is started or for as long as no state file exists in the tpmstate directory.

The availability of profiles can be queried from a running swtpm instance using swtpm_ioctl:

$ swtpm_ioctl --tcp :2322 --info 0x20 | jq
{
  "AvailableProfiles": [
    {
      "Name": "default-v1",
      "StateFormatLevel": 5,
      "Commands": "0x11f-0x122,0x124-0x12e,0x130-0x140,0x142-0x159,0x15b-0x15e,0x160-0x165,0x167-0x174,0x176-0x178,0x17a-0x193,0x197,0x199-0x19a",
      "Algorithms": "rsa,rsa-min-size=1024,tdes,tdes-min-size=128,sha1,hmac,aes,aes-min-size=128,mgf1,keyedhash,xor,sha256,sha384,sha512,null,rsassa,rsaes,rsapss,oaep,ecdsa,ecdh,ecdaa,sm2,ecschnorr,ecmqv,kdf1-sp800-56a,kdf2,kdf1-sp800-108,ecc,ecc-min-size=192,ecc-nist,ecc-bn,symcipher,camellia,camellia-min-size=128,cmac,ctr,ofb,cbc,cfb,ecb",
      "Description": "This profile enables all libtpms v0.9-supported commands and algorithms."
    },
    {
      "Name": "null",
      "StateFormatLevel": 1,
      "Commands": "0x11f-0x122,0x124-0x12e,0x130-0x140,0x142-0x159,0x15b-0x15e,0x160-0x165,0x167-0x174,0x176-0x178,0x17a-0x193,0x197",
      "Algorithms": "rsa,rsa-min-size=1024,tdes,tdes-min-size=128,sha1,hmac,aes,aes-min-size=128,mgf1,keyedhash,xor,sha256,sha384,sha512,null,rsassa,rsaes,rsapss,oaep,ecdsa,ecdh,ecdaa,sm2,ecschnorr,ecmqv,kdf1-sp800-56a,kdf2,kdf1-sp800-108,ecc,ecc-min-size=192,ecc-nist,ecc-bn,symcipher,camellia,camellia-min-size=128,cmac,ctr,ofb,cbc,cfb,ecb",
      "Description": "The profile enables the commands and algorithms that were enabled in libtpms v0.9. This profile is automatically used when the state does not have a profile, for example when it was created by libtpms v0.9 or before."
    },
    {
      "Name": "custom",
      "StateFormatLevel": 2,
      "Commands": "0x11f-0x122,0x124-0x12e,0x130-0x140,0x142-0x159,0x15b-0x15e,0x160-0x165,0x167-0x174,0x176-0x178,0x17a-0x193,0x197,0x199-0x19a",
      "Algorithms": "rsa,rsa-min-size=1024,tdes,tdes-min-size=128,sha1,hmac,aes,aes-min-size=128,mgf1,keyedhash,xor,sha256,sha384,sha512,null,rsassa,rsaes,rsapss,oaep,ecdsa,ecdh,ecdaa,sm2,ecschnorr,ecmqv,kdf1-sp800-56a,kdf2,kdf1-sp800-108,ecc,ecc-min-size=192,ecc-nist,ecc-bn,symcipher,camellia,camellia-min-size=128,cmac,ctr,ofb,cbc,cfb,ecb",
      "Description": "This profile allows customization of enabled algorithms and commands."
    }
  ]
}

@stefanberger stefanberger marked this pull request as draft July 15, 2022 20:22
@stefanberger stefanberger force-pushed the stefanberger/libtpms_profiles branch 10 times, most recently from 004ac8d to bbee00c Compare July 24, 2022 07:01
@coveralls
Copy link

coveralls commented Jul 24, 2022

Pull Request Test Coverage Report for Build 4265

  • 211 of 351 (60.11%) changed or added relevant lines in 14 files are covered.
  • 5 unchanged lines in 4 files lost coverage.
  • Overall coverage decreased (-0.5%) to 72.823%

Changes Missing Coverage Covered Lines Changed/Added Lines %
src/swtpm/swtpm.c 10 11 90.91%
src/swtpm/cuse_tpm.c 6 9 66.67%
src/swtpm_setup/swtpm_setup.c 29 34 85.29%
src/utils/swtpm_utils.c 23 28 82.14%
src/swtpm/swtpm_chardev.c 5 11 45.45%
src/swtpm/capabilities.c 37 45 82.22%
src/swtpm_setup/profile.c 37 50 74.0%
src/swtpm/fips.c 0 14 0.0%
src/swtpm/options.c 30 44 68.18%
src/swtpm/common.c 19 35 54.29%
Files with Coverage Reduction New Missed Lines %
src/swtpm_setup/swtpm_setup.c 1 76.96%
src/swtpm/swtpm_chardev.c 1 50.2%
src/swtpm/tpmlib.c 1 63.39%
src/swtpm/swtpm.c 2 77.49%
Totals Coverage Status
Change from base Build 4264: -0.5%
Covered Lines: 7157
Relevant Lines: 9828

💛 - Coveralls

@stefanberger stefanberger force-pushed the stefanberger/libtpms_profiles branch 2 times, most recently from cfa8445 to 83aa5c9 Compare July 25, 2022 19:24
@stefanberger stefanberger force-pushed the stefanberger/libtpms_profiles branch 8 times, most recently from 8ccee76 to 75852b1 Compare August 11, 2022 12:43
@stefanberger stefanberger force-pushed the stefanberger/libtpms_profiles branch 3 times, most recently from a793593 to 60c0331 Compare August 16, 2022 13:01
@stefanberger stefanberger changed the title WIP: Add support for setting of a profile for a TPM 2 Add support for setting of a profile for a TPM 2 Aug 16, 2022
@stefanberger stefanberger marked this pull request as ready for review August 16, 2022 13:08
@stefanberger stefanberger force-pushed the stefanberger/libtpms_profiles branch 2 times, most recently from d32aaf9 to d387c72 Compare August 18, 2022 15:03
@stefanberger stefanberger force-pushed the stefanberger/libtpms_profiles branch 4 times, most recently from ad63d13 to ba16bc0 Compare July 18, 2024 06:41
* enabled check whether any of the algorithms that the TPM 2 uses would need
* OpenSSL FIPS mode to be disabled for the TPM 2 to work and then try to disable
* it.
*/

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disabling FIPS mode in openssl made sense for swtpm historically, since the hardcoded algorithm list prevents it from working at all.

Going forward though, I don't think we should keep that behaviour. Disabling FIPS mode in openssl is too big of a hammer. We were doing it to allow the blocked algorithms, but it has the secondary behaviour of turning off FIPS self-tests in all the non-blocked algorithms. IOW, even if the guest only uses the FIPS permitted algorithms, the TPM is not FIPS compliant due to all self-tests being disabled in openssl.

IMHO, now that we have algorithm configuribility, if someone has requested the TPM to have an algorithm set that is incompatible with FIPS mode, then I think we should refuse to start the TPM at all, so that the host admin immediately sees their incorrect configuration request, rather than have the TPM silently run in non-FIPS compliant mode.

Going further than that, I wonder if we have proactively self-test the supported algorithms in openssl in general. Regardless of what crypto-policy is enabled in openssl, we don't want to launch the TPM, if openssl is blocking one of the algorithms that it is configured to expose. At best that will result in the guest seeing the TPM in a failed state at runtime, which is harder to diagnose than if we refuse startup of swtpm entirely.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IOW, even if the guest only uses the FIPS permitted algorithms, the TPM is not FIPS compliant due to all self-tests being disabled in openssl.

This FIPS thing is a problem. There's FIPS mode on the host where I remember running into inconsistencies on Fedora, CentOS etc. when enabling FIPS mode. Sometimes it seemed to not even enforce it or not disable the same algorithms across all machines (camellia, tdes, rsa-es, ecc keys smaller than 224bit). Then there's FIPS for the TPM 2 itself where it's not clear to me what needs to be done considering documents I have seen.

For example, the TPM itself is not FIPS compliant due to the algorithms it implements or how it uses them. There's an old FIPS guidance document here but I am not sure of its significance nor whether it is complete (from what I had seen elsewhere) or what changes it would require to the code: https://trustedcomputinggroup.org/resource/tcg-fips-140-2-guidance-for-tpm-2-0/

IMHO, now that we have algorithm configuribility, if someone has requested the TPM to have an algorithm set that is incompatible with FIPS mode, then I think we should refuse to start the TPM at all, so that the host admin immediately sees their incorrect configuration request, rather than have the TPM silently run in non-FIPS compliant mode.

Well, building a profile and submitting it from the level libvirt will not be easy for users either.
Ideally there would be a FIPS profile offered by the TPM 2, one that at least takes into consideration disabled algorithms on the host. It would of course have to function on all system.

Going further than that, I wonder if we have proactively self-test the supported algorithms in openssl in general.

Among the supported algorithms there must be a subset of algorithms that are disabled by FIPS mode. FIPS mode for 2024 and next few years should at least not mean something different between machines, so it should be possible to write a FIPS profile v1 that works on all current systems. That's why I was going to try at least offer a FIPS-on-host profile via libtpms.

I have a branch for libtpms to support FIPS profiles but I am quite a bit hesitant to merge this: https://github.com/stefanberger/libtpms/tree/stefanberger/fips-140-2%2B

First we would have to support FIPS mode on the host that disables some algorihms during runtime (e.g., signing a SHA1 while allowing signature verification with a sha1, camellia, tdes, rsa-es, etc). Then in the more difficult step there would be FIPS mode of the TPM 2 itself, if it can be done at all, that would build on top of it.

Thanks for looking at this. With FIPS I am not sure how to proceed.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you know, on Fedora / RHEL, everything related to crypto algorithm availability is controlled by the crypto-policies, which are in turn defined by the 'update-crypto-policies' tool. RHEL and Fedora usually have different policies at any given point in time, as RHEL tends to be more aggressive in blocking undesirable algorithms. eg it blocked SHA1 signatures many years before Fedora. Primarily the crypto policies tool generates config files for crypto libraries (openssl, gnutls, gcrypt, etc), but it can also generate config files for other programs. It just needs extension to teach it about the config format.

IOW, we could teach the crypto policies tool how to generate a policy in the libtpms format.

From a libvirt level, I could see us having a "policy" attribute on the '' element that gives a policy name to use for manufacturing. The valid choices could be the various built-in libtpms standard policies, plus a policy that is generated from the crypto-policies tool. Obviously that won't work if admins dynamically change crypto policies on an existing host, but in the case of FIPS, I would expect FIPS mode to be set when a host is first deployed, and never changed afterwards. If we took that approach, libtpms wouldn't have to define a FIPS policy itself.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Obviously that won't work if admins dynamically change crypto policies on an existing host, but in the case of FIPS, I would expect FIPS mode to be set when a host is first deployed, and never changed afterwards. If we took that approach, libtpms wouldn't have to define a FIPS policy itself.

Though with FIPS being a standard I thought that we should be able to define a profile for it that works across all systems in 2024++. We just need to know the maximum set of algorithms we need to disable. Otherwise what would be the meaning of FIPS if it was configured differently on each host? Once a new FIPS version comes out we would have to create a new profile with more algorithms removed and the old one would not work anymore nor would the VM be able to start ever again on that host that would have presumably further restricted the set of algorithms that can be used on a FIPS-enabled host.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FIPS isn't entirely precise. For example, it actually allows SHA1, but only allows use of SHA1 for signing for "legacy applications". RHEL-9's FIPS policy goes beyond this requirement, to fully block SHA1, though the change merged last week overrides that now for swtpm. Basically though, the Fedora/RHEL crypto maintainers would prefer if in the out of the box config, swtpm fully complied with currently configured crypto policy, and required a user opt-in for alternative configs. This is an inherently moving target.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FIPS isn't entirely precise. For example, it actually allows SHA1, but only allows use of SHA1 for signing for "legacy applications".

I thought it would only disallow SHA1 for signing and not sign with keys < 2048bits but still allow signature verification.

We could define a FIPS profile without SHA1, so no legacy application support at all. If someone complains about this they could customize the custom profile to meet their requirements.

RHEL-9's FIPS policy goes beyond this requirement, to fully block SHA1, though the change merged last week overrides that now for swtpm.

This is an inherently moving target.

The problem with this is if it moves against existing profiles it will prevent VMs from starting. A problem is also a distro like Fedora where people just upgrade from one version to another and more algorithms restrictions will come their way and they wouldn't like it if their VMs didn't start anymore...

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought it would only disallow SHA1 for signing and not sign with keys < 2048bits but still allow signature verification.

IIUC the limits on SHA1 get defined in

http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar1.pdf

where it says

  • SHA-1 for digital signature generation:

    SHA-1 may only be used for digital signature generation where specifically allowed by NIST protocol-specific guidance. For all other applications, SHA-1 shall not be used for digital signature generation.

  • SHA-1 for digital signature verification:

    For digital signature verification, SHA-1 is allowed for legacy-use.

  • SHA-1 for non-digital signature applications:

    For all other hash function applications, the use of SHA-1 is acceptable. The other applications include HMAC, Key Derivation Functions (KDFs), Random Bit Generation, and hash-only applications (e.g., hashing passwords and using SHA-1 to compute a checksum, such as the approved integrity technique specified in Section 4.6.1 of [FIPS 140]).

The problem with this is if it moves against existing profiles it will prevent VMs from starting. A problem is also a distro like Fedora where people just upgrade from one version to another and more algorithms restrictions will come their way and they wouldn't like it if their VMs didn't start anymore...

Yes, the upgrade path is a problem. The appropriate tradeoff is probably to strictly honour crypto-policies at time of manufacturing, but then force allow the selected algorithms at swtpm launch. The openssl config file override can likely to used to do the latter.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the interest of making progress on this PR and being able to set a profile with swtpm/swtpm_setup I will remove the patches from it that are related to FIPS...

@stefanberger stefanberger force-pushed the stefanberger/libtpms_profiles branch 8 times, most recently from c396a71 to 34627bf Compare July 19, 2024 17:50
Libtpms v0.10 adds the TPMLIB_SetProfile call that swtpm needs to set a
profile. Check whether the API call is available in the local libtpms
installation.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Allow passing a JSON map as part of an option value in the format of
--foo name={...},... Prior to this patch this would not have worked since
the option values were broken apart around commas, which a map may also
contain. Now, if a '{' is following the '=', the value is attempted to be
parsed as a JSON map and the end of the map is searched considering
possibly embedded maps.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
@stefanberger stefanberger force-pushed the stefanberger/libtpms_profiles branch 3 times, most recently from 20094e3 to d5986b5 Compare July 22, 2024 12:35
Add support for the --profile option for a TPM 2. The 'name=' parameter
allows a user to select a specific profile available in libtpms. The
'profile=' parameter allows a user to pass a JSON profile that must
contain a name field with a profile known to libtpms. It may contain
an algorithm field that has a comma-separated list of verbs with the
names of algorithms that the TPM 2 is supposed to provide.

The --profile option only has an effect the first time a TPM 2 is started
since afterwards whenever the state of the TPM 2 is read, the profile
found in the state is being used.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Display the new capability verb 'cmdarg-profile' indicating that the
--profile option with the name= and profile= parameters is supported.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Print a list of names of profiles implemented by libtpms as part of the
capabilities JSON. The profiles map will only be visible if libtpms v0.10
with the TPMLIB_SetProfile() API is used.

swtpm socket --print-capabilities  --tpm2| jq
{
  "type": "swtpm",
  "features": [
    [...]
  ],
  "profiles": {
    "names": [
      "default-v1",
      "null",
      "custom"
    ],
    ...
  },
  "version": "0.10.0"
}

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Add support for the --profile parameter that allows a user to select
a profile for the TPM 2 instance. The profile parameter must be a
string-formatted JSON map describing the profile to use.

Resolves: stefanberger/libtpms#284
Resolves: #710
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Print a list of names of profiles implemented by libtpms as part of the
capabilities JSON. The profiles map will only be visible if libtpms v0.10
with the TPMLIB_SetProfile() API is used.

swtpm_setup --print-capabilities --tpm2 | jq
{
  "type": "swtpm_setup",
  "features": [
    [...]
  ],
  "profiles": [
    "default-v1",
    "null",
    "custom"
  ],
  "version": "0.10.0"
}

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Implement test cases for swtpm_setup and swtpm exercising the
--profile option.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Add a test case for testing profiles across libtpms versions. For now only
an instance with the NULL profile is being tested with the latest libtpms
version and the state is then attempted to be used by libtpms v0.9.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Add support for --print-profiles option to print all profiles supported
by libtpms.

Usage:

  swtpm socket --tpm2 --print-profiles | jq

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
If the user did not provide the profile on the command line read the
default profile from the swtpm_setup.conf configuration file.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
@stefanberger stefanberger force-pushed the stefanberger/libtpms_profiles branch from d5986b5 to 9e11191 Compare July 22, 2024 12:52
@stefanberger stefanberger merged commit 25eb5f7 into master Jul 22, 2024
5 checks passed
@stefanberger stefanberger deleted the stefanberger/libtpms_profiles branch July 22, 2024 13:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants