fix(#3813): seal with an RSA-OAEP key, the only mechanism the TPM has
`bao operator init` fails at the seal with CKR_MECHANISM_INVALID. The pkcs11 seal asks for AES-GCM by default and the TPM does not offer it: a TPM 2.0's symmetric modes are CBC/CFB/CTR/OFB/ECB, and openbao accepts only AEAD mechanisms — AES-GCM or RSA-OAEP — so RSA-OAEP is the single mechanism both sides implement. Measured on the deployed token (`pkcs11-tool --list-mechanisms`, #3860): no AES-GCM, and `RSA-PKCS-OAEP, keySize={1024,2048}, hw, encrypt, decrypt` present. CBC is not a fallback — openbao's `MechanismFromString` rejects `CKM_AES_CBC_PAD` as deprecated and its encrypt path implements exactly the two AEAD mechanisms. The key gets a new label so a store provisioned by the earlier module keeps its unusable AES key without the two resolving to one label, and the addkey step is now keyed on the label rather than on the store not existing — otherwise an existing deployment never gains the RSA key.
This commit is contained in:
parent
c044a33be9
commit
fe81dcaf59
2 changed files with 46 additions and 8 deletions
|
|
@ -95,11 +95,19 @@ let
|
|||
# name alone resolves to two different groups. See `deploy.bao.tpmGid`.
|
||||
tpmGroup = "swarm-bao-tpm";
|
||||
|
||||
# RSA rather than AES, and the label says so because a store may still hold
|
||||
# the AES key an earlier version of this module created: openbao's seal
|
||||
# accepts only AEAD mechanisms — AES-GCM or RSA-OAEP — and a TPM 2.0 offers
|
||||
# no GCM, so RSA-OAEP is the only one both sides have. A label resolving to
|
||||
# both keys at once is an error, hence a distinct one.
|
||||
sealKeyLabel = "swarm-bao-seal-rsa";
|
||||
|
||||
sealSettings = lib.optionalAttrs (baoDeploy.seal == "pkcs11") {
|
||||
seal.pkcs11 = {
|
||||
lib = "${pkgs.tpm2-pkcs11}/lib/libtpm2_pkcs11.so";
|
||||
token_label = "swarm-bao";
|
||||
key_label = "swarm-bao-seal";
|
||||
key_label = sealKeyLabel;
|
||||
mechanism = "CKM_RSA_PKCS_OAEP";
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -795,15 +803,28 @@ in
|
|||
--label=swarm-bao \
|
||||
--sopin="$(cat ${tokenStoreDir}/so-pin)" \
|
||||
--userpin="$(cat ${tokenStoreDir}/user-pin)"
|
||||
# AES rather than RSA on purpose: openbao discussion 1826
|
||||
# reports an RSA keypair here yielding duplicate labels, so
|
||||
# `bao operator init` fails with "got more than 1 key for the
|
||||
# label" and then CKR_GENERAL_ERROR. The seal supports AES-GCM.
|
||||
tpm2_ptool addkey --path ${tokenStoreDir} --label=swarm-bao \
|
||||
--userpin="$(cat ${tokenStoreDir}/user-pin)" \
|
||||
--algorithm=aes256 --key-label=swarm-bao-seal
|
||||
fi
|
||||
|
||||
# Keyed on the label, not on the store's existence: a store
|
||||
# provisioned by an earlier version of this module has a token
|
||||
# and an unusable AES key, and has to gain this one without
|
||||
# being destroyed. See `sealKeyLabel` for why it is RSA.
|
||||
#
|
||||
# Substitution rather than a `grep -q` pipeline: `grep -q`
|
||||
# exits at the first match and SIGPIPEs the lister, which
|
||||
# `pipefail` reports as failure — the one wrong answer here
|
||||
# adds a second key under the label and breaks the seal.
|
||||
objects=$(tpm2_ptool listobjects --path ${tokenStoreDir} \
|
||||
--label=swarm-bao || true)
|
||||
case "$objects" in
|
||||
*${sealKeyLabel}*) ;;
|
||||
*)
|
||||
tpm2_ptool addkey --path ${tokenStoreDir} --label=swarm-bao \
|
||||
--userpin="$(cat ${tokenStoreDir}/user-pin)" \
|
||||
--algorithm=rsa2048 --key-label=${sealKeyLabel}
|
||||
;;
|
||||
esac
|
||||
|
||||
# Outside the branch above on purpose: a store provisioned by an
|
||||
# earlier version of this module is root-owned and unreadable to
|
||||
# the seal, and only the pins stay private to root.
|
||||
|
|
|
|||
|
|
@ -226,6 +226,23 @@ let
|
|||
in
|
||||
(c.bindMounts ? "/dev/tpmrm0") && builtins.any (d: d.node == "/dev/tpmrm0") c.allowedDevices;
|
||||
}
|
||||
{
|
||||
# The stanza's label and the label the unit creates are two literals that
|
||||
# have to name one object, and the mechanism is asserted with them
|
||||
# because it is valid only for an RSA key: openbao takes AES-GCM or
|
||||
# RSA-OAEP and a TPM 2.0 has neither GCM nor an opinion about which the
|
||||
# seal asked for. Every wrong combination renders and deploys, and
|
||||
# surfaces as a pkcs11 error at `operator init`.
|
||||
name = "the seal asks for the RSA key the provisioning unit creates";
|
||||
ok =
|
||||
let
|
||||
p = (baoSettings baoPkcs11).seal.pkcs11;
|
||||
in
|
||||
(p.mechanism or "") == "CKM_RSA_PKCS_OAEP"
|
||||
&& lib.hasInfix "--algorithm=rsa2048 --key-label=${p.key_label or ""}" (
|
||||
(baoUnits baoPkcs11).swarm-bao-token.script or ""
|
||||
);
|
||||
}
|
||||
{
|
||||
# `DynamicUser` implies `ProtectSystem=strict`, so the token directory is
|
||||
# read-only to the seal however it is owned, and the group is the only
|
||||
|
|
|
|||
Loading…
Reference in a new issue