Hoi,

>
> On Wed, 2024-11-27 at 03:39 +0000, Johannes Schneider via 
> lists.openembedded.org wrote:
> > > On Fri, 2024-11-01 at 13:05 +0100, Johannes Schneider via 
> > > lists.openembedded.org wrote:
> > > > Add handling of ca-chains which can consist of more than one
> > > > certificate in a .pem file, which need to be split off, processed and
> > > > stored separately in the softhsm - as the tool-chain
> > > > signing.bbclass::signing_import_cert* -> softhsm -> 'extract-cert'
> > > > only supports one-per-file, due to using/expecting "plain" x509
> > > > in-/output.
> > > >
> > > > The added signing_import_cert_chain_from_pem function takes a <role>
> > > > basename, and iterates through the input .pem file, creating numbered
> > > > <role>_1, _2, ... roles as needed.
> > >
> > > Why do you want to import certificates without corresponding private
> > > keys into the singing mechanism?
> > >
> > > They can't be used for signing, so don't really fit the role concept as
> > > I intended it. This mismatch then leads to workarounds such as the _N
> > > suffix and the problem below.
> >
> > During a (yocto) build, there can be places where the key is used to sign
> > something, and other places where the corresponding certificate is 
> > inserted/used
> > to verify the former - so there are two different "consumers" of one role; 
> > e.g.
> > the kernel fitimage is signed with A.key, and at bootloader build time 
> > A.cert is
> > inserted into the bootloader so that it can later verify what it is 
> > supposed to load.
>
> Yes, this split between signed artifact component (e.g. fitimage) and
> authenticating component (e.g. bootloader) was the main motivation for
> the concept of roles: both users of a role can be switched from one
> (development) key to a different one at a single place
> (local/auto.conf).
>
> > Now the bootloader<>kernel uscase is simple, because there is no 
> > verification of
> > the certificate; e.g. there is no need to provide the complete chain.
>
> (for fit images, the certificate isn't even available during
> authentication, only the public key)
>
> > But other usecases sign $something during buildtime, and at runtime this
> > artifact would then be verified against a certificate, which in turn would 
> > need
> > to be verified against it's CA-chain.
>
> Beyond the simple case of self-signed certificates, I find it clearer
> to talk about leaf, intermediate and root certificates. Depending on
> context, "chain" can mean leaf+intermediates, intermediates only, or
> even leaf+intermediates+root.
>
> On the target, only the root certificates need to be stored in a
> trusted location. The intermediate certificates are usually provided
> with the signature to let the authenticating component to build a path
> to a trusted root.

Or stored also in a trusted location?
-> openssl -CApath /usr/share/ca-certificate/youre-company/
(which openssl/libopenssl actually picks up automatically, if the certificates
there have hash-symlinks; and that's what we're using ATM)

>
> The leaf certificate is special in this case mainly because it's the
> only certificate for which we have the corresponding private key.
>
> > By keeping the leaf (key+cert) plus all links (just the cert) up the 
> > certificate
> > chain, the softhsm can be used as "single source of keymaterial". Otherwise
> > there could be a rift between taking the keys from the softhsm during 
> > buildtime,
> > but takeing the certificate (-chains) from $elsewhere.
>
> If we say signing.bbclass instead of SoftHSM, I'd agree.
>

(thumbsup) i was more refering to the "thing storing the keymaterial"
the softhsm just happens to be the default backend of the .bbclass

>
> SoftHSM doesn't actually provide more security for private keys than
> simple .pem files. So why add all this complexity instead of using
> simple OE variable pointing to a private key file? Because it allows us
> to:
> - support hardware tokens
> - decouple key configuration from key usage
>   => simplifies reuse
>   => consistent configuration for many recipes
> - use same code-paths for development and release
>   => better testing
>
> (for more background info, see my OE WS 2023 talk
> https://pretalx.com/openembedded-workshop-2023/talk/3C8MFF/)
>
>
> I definitely agree that signing.bbclass should also have a way to get
> the intermediate and root certs for any given key-pair via the role.
> Introducing additional certificate-only roles for this causes new
> problems, though (see below).

what new problems?
it's already being done for e.g. HABv4, where there are roles
that do import a (leaf) certificat, but don't have a key to import

>
> > > > Afterwards the certificates can be used or extracted one-by-one from
> > > > the softhsm, using the numbered roles; the only precondition - or
> > > > limitation - is that the PKI structure has to be known beforhand;
> > > > e.g. how many certificates are between leaf and root.
> > >
> > > The point of the signing.bbclass is to abstract over different HSMs and
> > > allow flexible key selection at the .conf level.
> > >
> > > With this approach, any recipe using this set of generated
> > > (certificate-only) roles now needs to know how long the chain is and
> > > the PKIs with different layouts are no longer possible.
> >
> > That is the one caveat - the layout has to be the same and known beforehand;
> > should we add more logic=complexity into the class to detect and handle this
> > (... i guess: no ;-)?
>
> The information has to be stored somewhere, but ideally only in one
> place. It can't be the recipes, because we always have (at least) two
> of them (signing and authenticating components). Also, hard-coding
> details of the layout in recipes reduces reusability across layers or
> projects.
>
> I'm not willing to give up on the goal of proper decoupling. As the
> inherent complexity has to live somewhere, I'd prefer the class, so a
> single implementation can be reused and the recipes are less complex.
>
>
> One aspect to consider is that there is not a one-to-one relationship
> between the leaf certificate and the set of CA certificates

Depends on which way you look, there is a 1:1 from leaf->intermediary,
and then intermediary/-ies->root
But you're right, it's not necessarily 1:1 in the other direction.

> (intermediates+root): CAs are used to support multiple interchangeable
> leaf certificates under a single trusted root. Without the need of
> multiple leaf certs, you could just use a self-signed cert and avoid
> all this complexity. :)

:-D - or just do away with the code-signing complexity alltogether, and allow
people to run OSS-software of their choosing on the hardware they paid money for
;-)

>
> For example, one build configuration could have different key-pairs
> (roles) for different components, e.g. kernel modules and an IPE
> policies. Their certificates may still be signed by the same CA, as
> they are authenticated by the same component (the kernel).
>
>
> So, handling (intermediate and root) CA certificates separately from
> the roles would allow us to refer to them indirectly.
>
> > > Could you describe you use-case in detail? I think we should try to
> > > find a design which avoids using roles for certificate chains, while
> > > also allowing different PKI layouts between SoftHSM and actual HSMs.
> >
> > We have a build setup that, depending on a build-configuration switch, uses
> > either development-keymaterial, or production-keymaterial. So the (soft)HSM
> > encapsuled by the signing.bbclass and it's roles becomes the common 
> > interface
> > through which all keymaterial is requested for artifact-signing purposes, or
> > certificate (chains) are gathered to populate e.g. the rootfs with, for use
> > during system runtime.
>
> Yes.
>
> > The intended usecase of the signing.bbclass is certainly to swith around the
> > actual underlying HSMs (e.g. have a softhsm with devkeys, but a "real" HSM 
> > for
> > the production usecase) - but due to limitations with our CI 
> > infrastructure...
> > we only have a secured channel to fetch the productive keymaterial, but 
> > still
> > need/intend to use the softhsm to "secure" the keymaterial on the CI during
> > build-time.
>
> Using SoftHSM doesn't provide any security. If you're OK with that, it
> still is useful as part of the abstraction, so that keys can be
> switched without touching recipes.
>
>
> Note that certificates are not secret, so storing them in a HSM is
> primarily useful because that avoids the need of a different storage
> location. Alternatively, all certs could be stored as files in the
> layer and found by recursively walking the X509v3 Authority Key
> Identifiers (but that would likely need a small tool to be called from
> the class).
>
> > > > Signed-off-by: Johannes Schneider 
> > > > <[email protected]>
> > > > ---
> > > >  meta-oe/classes/signing.bbclass | 30 ++++++++++++++++++++++++++++++
> > > >  1 file changed, 30 insertions(+)
> > > >
> > > > diff --git a/meta-oe/classes/signing.bbclass 
> > > > b/meta-oe/classes/signing.bbclass
> > > > index 3e662ff73..8af7bbf8e 100644
> > > > --- a/meta-oe/classes/signing.bbclass
> > > > +++ b/meta-oe/classes/signing.bbclass
> > > > @@ -134,6 +134,36 @@ signing_import_cert_from_der() {
> > > >      signing_pkcs11_tool --type cert --write-object "${der}" --label 
> > > > "${role}"
> > > >  }
> > > >
> > > > +# signing_import_cert_chain_from_pem <role> <pem>
> > > > +#
> > > > +
> > > > +# Import a certificate *chain* from a PEM file to a role.
> > > > +# (e.g. multiple ones concatenated in one file)
> > > > +#
> > > > +# Due to limitations in the toolchain:
> > > > +#   signing class -> softhsm -> 'extract-cert'
> > > > +# the input certificate is split into a sequentially numbered list of 
> > > > roles,
> > > > +# starting at <role>_1
> > > > +#
> > > > +# (The limitations are the conversion step from x509 to a plain .der, 
> > > > and
> > > > +# extract-cert expecting a x509 and then producing only plain .der 
> > > > again)
> > > > +signing_import_cert_chain_from_pem() {
> > > > +    local role="${1}"
> > > > +    local pem="${2}"
> > > > +    local i=1
> > > > +
> > > > +    cat "${pem}" | \
> > > > +        while openssl x509 -inform pem -outform der -out 
> > > > ${B}/temp_${i}.der; do
> > > > +            signing_import_define_role "${role}_${i}"
> > >
> > > Calling signing_import_define_role() from an import function breaks the
> > > separation and ordering we currently use (first
> > > signing_import_define_role(), then import certs/keys) and is surprising
> > > compared to the existing signing_import_define_role()
> >
> > That is true... got any advice/ideas on how to handle this?
> >
> > This way the PKI layout = the certificate chain lenght, has not to be known 
> > at
> > the time of the import, but only when using the roles. A separate
> > signing_import_define_N_roles(N) to have them prepared "blindly" ahead of 
> > time?
>
> If we separate certificates from roles, we'd have something like this
> in the provider (where development keys are imported):
>   signing_import_prepare
>
>   signing_import_root_cert kernel ".../kernel-ca.crt"
>
>   signing_import_define_role kernel_modules
>   signing_import_cert_from_pem kernel_modules "${S}/kmod-development.crt"
>   signing_import_key_from_pem kernel_modules "${S}/kmod-development.key"
>   signing_import_set_ca kernel_modules kernel
>

That is an idea! adding a variable to to the SIGNING_ENV_FILE that allows to
preserves the relation between certificates. Having a "signing_import_set_ca" is
probably enough: leafs would point to their intermediary, and that to it's root;
having an signing_import_root_cert is IMO not needed, as it's the same as a
signing_import_cert_from_pem.

>   signing_import_define_role kernel_ipe
>   signing_import_cert_from_pem kernel_ipe "${S}/ipe-development.crt"
>   signing_import_key_from_pem kernel_ipe "${S}/ipe-development.key"
>   signing_import_set_ca kernel_ipe kernel
>
>   signing_import_finish
>
> In the recipes using these roles, you'd need to refer to the CA. For
> the kernel (authenticating component):
>   signing_prepare
>   cp "$(signing_get_root_cert kernel)" "${B}/kernel_ca.pem"
>

if it would be just a simple 'cp'... it's always a "get the pkcs11 uri" + 
extract-cert :-/
It would be neat if the signing.bbclass would support getting to the (public) 
certificate
out of a role directly.

>
> When signing the modules, you'd use:
>   signing_prepare
>   signing_use_role kernel_modules
>   ... use $PKCS11_URI
>
>
> For cases wth intermediate certificates, we could chain them to their
> respective CA:
>   signing_import_prepare
>
>   signing_import_root_cert kernel ".../kernel-ca.crt"
>   signing_import_intermediate_cert kernel_2024 kernel ".../kernel-kmods.crt"
>

and why not:
signing_import_define_role kernel
signing_import_cert_from_pem kernel ".../kernel-ca.crt"
signing_import_define_role kernel_2024
signing_import_cert_from_pem kernel_2024 ".../kernel-kmods.crt"
signing_import_set_ca kernel_2024 kernel

Or did you mean to abstract those steps away in the functions
signing_import_root_cert +  signing_import_intermediate_cert
... seems redudndant to the import_cert_from_pem

>
>   signing_import_define_role kernel_modules
>   signing_import_cert_from_pem kernel_modules "${S}/kmod-development.crt"
>   signing_import_key_from_pem kernel_modules "${S}/kmod-development.key"
>   signing_import_set_ca kernel_modules kernel_2024
>
>   signing_import_define_role kernel_ipe
>   signing_import_cert_from_pem kernel_ipe "${S}/ipe-development.crt"
>   signing_import_key_from_pem kernel_ipe "${S}/ipe-development.key"
>   signing_import_set_ca kernel_ipe kernel_2024
>
>   signing_import_finish
>
> To support this, only the user (e.g. kernel modules, IPE policy) would
> need to conditionally include the intermediate certificates:
>
>   signing_prepare
>   signing_use_role kernel_modules
>   SIGN_CMD="... --key=PKCS11_URI ..."
>   if signing_has_intermediate_certs kernel_modules; then
>     SIGN_CMD="$SIGN_CMD --intermediate=$(signing_get_intermediate_certs 
> kernel_modules)"
>   fi
>
> This recipe-level-code would support all cases:
> - self-signed cert for simple cases
> - leaf + root cert (no intermediates)
> - leaf + intermediate + root cert (intermediates include with the signature)
>
>
> For release builds, in addition to overriding the roles, you'd need to
> override the certificates as well in you .conf:
>   SIGNING_PKCS11_URI[kernel_modules] = 
> "pkcs11:serial=DENK0200554;object=kmod&pin-value=123456"
>   SIGNING_CA_CERT[kernel_modules] = "file:///kod-ca.cert"
>   SIGNING_PKCS11_URI[kernel_ipe] = 
> "pkcs11:serial=DENK0200554;object=ipe&pin-value=123456"
>   SIGNING_CA_CERT[kernel_ipe] = "pkcs11:serial=DENK0200554;object=ipe-ca"
>   SIGNING_CA_CERT[kernel] = "pkcs11:serial=DENK0200554;object=kernel-ca"
> So, you could have the certificates as files or in the HSM.
>
> If you'd need to define a different hierarchy, you'd add:
>   SIGNING_CA[kernel_modules] = "product_a_kmods_2024"
>   SIGNING_CA_CERT[kernel_modules] = "..."
>   SIGNING_CA[kernel_ipe] = "product_a_ipe_2024"
>   SIGNING_CA_CERT[kernel_ipe] = "..."
>   SIGNING_CA[product_a_kmods_2024] = "product_a_2024"
>   SIGNING_CA_CERT[product_a_kmods_2024] = "..."
>   SIGNING_CA[product_a_ipe_2024] = "product_a_2024"
>   SIGNING_CA_CERT[product_a_ipe_2024] = "..."
>   SIGNING_CA[product_a_2024] = "release-ca"
>   SIGNING_CA_CERT[product_a_2024] = "..."
>   SIGNING_CA_CERT[release-ca] = "..."
> And the actual recipes wouldn't need to change, preserving the
> decoupling between recipes and PKI configuration.
>
> I think that should offer enough flexibility for your and other
> scenarios.
>
> Regards
> Jan

seems like this patch was already accepted in <master>  :-)
i'll put togheter another one to add the 'signing_import_set_ca', since it
would supplement everything nicely.


gruß
Johannes
-=-=-=-=-=-=-=-=-=-=-=-
Links: You receive all messages sent to this group.
View/Reply Online (#114126): 
https://lists.openembedded.org/g/openembedded-devel/message/114126
Mute This Topic: https://lists.openembedded.org/mt/109331453/21656
Group Owner: [email protected]
Unsubscribe: https://lists.openembedded.org/g/openembedded-devel/unsub 
[[email protected]]
-=-=-=-=-=-=-=-=-=-=-=-

Reply via email to