Re: Cryptographic ABI

93 views
Skip to first unread message

Mickaël Salaün

unread,
Jan 6, 2021, 1:28:56 PM1/6/21
to Adam Barth, dis...@fuchsia.dev

On 05/01/2021 19:48, Adam Barth wrote:
> Hi Mickaël,
>
> Thanks for your email.  Two process points and one substantive point:
>
> ## Process points
>
> (1) You've cross-posted to many mailing lists.  I can understand why you
> might do that if you're unsure which mailing list makes the most sense
> or if your message touches on many different areas.  However, we would
> prefer less cross-posting, if possible.  There's a general "discuss"
> mailing list which can be used for cross-cutting concerns if there isn't
> a more specific mailing list that is appropriate.

OK, this email is the continuation of the initial thread:
https://groups.google.com/a/fuchsia.dev/g/component-framework-dev/c/UdVBik5G1_E

>
> (2) The way we normally handle proposals like yours through the RFC
> process rather than through mailing lists.  You can find out more about
> Fuchsia RFCs
> at https://fuchsia.dev/fuchsia-src/contribute/governance/rfcs/current_rfc_process
> <https://fuchsia.dev/fuchsia-src/contribute/governance/rfcs/current_rfc_process>
> .  The first step in the process is to socialize your idea with the
> project, which can be done over email.  However, your email has very
> specific technical designs, which is something that usually comes about
> in step 2 or 3.

I have read the RFC process but I though emails would be easier to
discuss ideas before creating an actual RFC. Moreover, I didn't want to
invest time to write a proper RFC if there is no chance of adoption.

The general idea is to encrypt and authenticate everything. :)
Because there is a lot of way to interpret this and to implement this,
after some though (and digestion of Fuchsia principles), I tried to
explain the basis of a Fuchsia-idiomatic and flexible interface based on
my knowledge of other OS use cases.

>
> ## Substantive points
>
> (3) It's likely that we'll want something like what you describe at some
> point.  However, there's a large pile of useful functionality we could
> add to the system.  We need to figure out in what order to add that
> functionality.  Generally, we'll add functionality to the project's
> roadmap based on two constraints: (a) if there is a "ready and willing
> customer" that needs the functionality,

What exactly is a Fuchsia customer? Is there a public list of them? Does
open-source community count as one?

> or (b) if not adding the
> functionality now makes it significantly (e.g., 10x) more difficult to
> add the functionality in the future.
Because a complete component-instance-side encryption and authentication
would be invasive, I think it would be much more easy to make Fuchsia
support this kind of features before a wider adoption rather than
relying on incremental system updates. Multiple services could leverage
these features at their core (e.g. FIDL protocols, filesystem formats,
components management), which would avoid a painful transition. I think
this is an opportunity to embedded a widely and secure use of
cryptographic primitives in all part of Fuchsia, much more than current
systems.

>
> Having a ready-and-willing customer is important to ground the work in
> concrete requirements.  That helps avoid the risk of building
> functionality that can't actually be used by a customer because we
> didn't understand their requirements well enough.

I agree. Because security is one of the principle of Fuchsia, I think it
make sense to have a wider use of confidentiality and authenticity
features. :)

>
> If you look at the project's roadmap...
>
> https://fuchsia.dev/fuchsia-src/contribute/roadmap?hl=en
> <https://fuchsia.dev/fuchsia-src/contribute/roadmap?hl=en>
>
> ... you'll see that many of the things we're working on are complete
> migrations and reducing technical debt.  That's a result of this YAGNI
> [1] approach to the project.

I have read this roadmap, and it helps understand some of the current
priorities, among them, component management and storage enhancements. I
didn't know this funny YAGNI acronym though. This principle make sense
for "usable" features, but maybe less for security features which may
not be directly used by developers nor users. However, I agree that part
of the syscalls I propose shouldn't be a priority (e.g. asymmetric
cryptography), but it helps see the potential of the basic blocks.

>
> Based on what you've written, you seem like a very capable contributor,
> and I do hope that you'll end up contributing to Fuchsia.  I'd be happy
> to talk with you directly and help you find something to work on in the
> project that aligns with your interests.
>
> Adam
>
> [1] https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it
> <https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it>
>
>
> On Sun, Jan 3, 2021 at 9:24 PM Mickaël Salaün <m...@digikod.net
> <mailto:m...@digikod.net>> wrote:
>
> Hi,
>
> Fuchsia is really interesting, especially from a security point of view,
> but some pieces are missing for now. I would like to start a discussion
> with some draft (and probably some bogus) ideas about confidentiality
> and authenticity features mapping to the Fuchsia model. This document
> doesn't dive too much into cryptographic implementation details but it
> focuses on the system call API and ABI.
>
> Encrypting component instances' data (kernel or component instance side)
> is a requirement to enforce confidentiality without trusting (local or
> remote) storage services (or intermediate services acting as proxies).
> Component authentication can be used to only grant access to data to
> their owner, to enforce quota per component instances, and to protect
> against DoS.
>
> The goal of the following proposition is to enable all component
> instances to easily use their own set of cryptographic keys without
> storing nor accessing them, thanks to the capability model and key
> derivations. These keys can be used to encrypt and authenticate data (at
> rest), and to authenticate (local or remote) peers in a secure and
> privacy preserving way.
>
> New kernel objects
> ------------------
>
> One of the main challenges is to not directly tie keys to a component
> instance (e.g. its job) nor its topology (i.e. moniker), which may
> evolve with system updates. Another challenge is to enable multiple
> cooperative component instances to share the same encrypted data without
> accessing the key payload.
>
> Using new dedicated kernel objects may enable any processes to encrypt,
> sign, decrypt and authenticate specific data. This way, key management
> remains independent from component hierarchies (jobs), even if in
> practice they should be close. For instance, a component could still be
> updated to create multiple internal jobs and transfer key handles to its
> child jobs.
>
> To achieve that, we add a set of system calls to manage new kernel
> objects: keyring, msgkey, peerkey, passkey and passid. The kernel may be
> in charge of some cryptographic processing (e.g. private symmetric keys)
> while some other (e.g. private asymmetric keys) can be delegated to a
> secure element (i.e. hardware): the private key payloads don't reach
> user space and are assigned to a specific usage in a specific context.
> This helps protect against key leaks, key misuses and persistent
> compromised key. The kernel can also improve keys protection thanks to
> dedicated memory mappings (cf. Linux's memfd_create/MFD_SECRET).
>
> Keyrings
> --------
>
> This model relies on key derivations and handles. A (privileged) syscall
> is dedicated to get one or multiple root keyrings derived from the
> current secure boot state (e.g. TPM PCRs). This keyring can then be used
> to derive usable keys.
>
> zx_keyring_get_root(uint32_t slot, uint32_t options, zx_handle_t* out);
>
> The zx_keyring_get_root() syscall enables to get some initial keyrings
> tied to the system integrity. Only one call to get the keyring of a
> specific @slot (e.g. a key derived from a PCR) should be allowed, and
> only from a process with the ZX_RIGHT_ROOT privilege. The set of
> cryptographic algorithms tie to the @out keyring (and its derived keys)
> can be chosen at paving-time according to the hardware cryptography
> acceleration features (e.g. AES-NI or ChaCha20). This set of algorithms
> may not be part of Fuchsia ABI. The new @out keyring is derived from the
> integrity of the running system (and hardware-bound), which make it
> useful for local-only data encryption and local-only component instance
> authentication.
>
> Every component instance's cache should be automatically encrypted by
> locally derived keys to get similar guarantees as with a Linux swap
> encryption, but at the component instance level and with authentication.
> This provides continuous authenticated and encrypted component instance
> state for its whole lifetime. This encrypted data cannot be decrypted on
> another running system.
>
> zx_keyring_import(uint32_t cipher_set, const void* private_key, size_t
> private_key_size, uint32_t options, zx_handle_t* out);
>
> The zx_keyring_import() syscall may be used to create a keyring
> independent from the integrity of the running system, which enables to
> share keys between multiple running systems (e.g. to encrypt user
> session data shared between multiple devices). Because of compatibility
> reasons, the set of cryptographic algorithms specified by @cipher_set
> (e.g. ZX_KEYRING_CIPHER_XCHACHA20_POLY1305_ARGON2) must be part of the
> Fuchsia ABI. @private_key should be a high-entropy private key from
> which multiple keys can be derived.
>
> zx_keyring_derive(zx_handle_t parent, const void* context, size_t
> context_size, uint32_t options, zx_handle_t* out);
>
> The zx_keyring_derive() syscall enables to give their own set of keys to
> other component instances. Indeed, deriving the main key of @parent
> enables to create new derived keys thanks to a user-supplied
> deterministic @context and a Key Derivation Function (KDF). @context can
> be a stored (randomly generated) key tied to a component URL and its
> absolute moniker, or a key derived from a user-supplied secret (e.g.
> passwords for user sessions), or (if there is no storage available) a
> hardcoded value. As a safeguard, @context must be unique (at creation
> time) for all objects derived from the same parent.
>
> zx_keyring_mask_rights(zx_handle_t keyring, uint32_t key_types,
> zx_rights_t masked_rights);
>
> The zx_keyring_mask_rights() syscall enables to (temporarily) remove (or
> restore) a set of access rights to all the @keyring derived objects with
> specific @key_types (bitmask). For instance, this may be interesting to
> forbid decryption of data when the system is in a specific state (e.g.
> locked user session). Underneath, cryptographic (public or private) keys
> could be removed from (the kernel) memory to prevent hardware attacks,
> or derived back to regain some abilities.
>
> Data confidentiality and authenticity
> -------------------------------------
>
> Enforced Authenticated Encryption with Associated Data (AEAD) enables to
> securely store data in a secure way, which also enables all component
> instances to have a virtually infinite private storage space.
>
> zx_msgkey_derive(zx_handle_t parent, const void* context, size_t
> context_size, uint32_t options, zx_handle_t* out);
>
> An msgkey object enables to encrypt, sign, decrypt and authenticate
> data. The zx_msgkey_derive() syscall creates a new msgkey by deriving
> the main key of a @parent handle referring to either a keyring or an
> msgkey object. @context must not already be used by another child of
> @parent and it must refer to a specific usage (e.g.
> "storage-cache-data", "storage-cache-filename", etc.).
>
> zx_msgkey_encrypt(zx_handle_t msgkey, uint64_t offset, const void*
> buffer, size_t buffer_size, uint32_t options, void* out, size_t
> out_size, size_t* actual);
>
> The zx_msgkey_encrypt() syscall enables to directly encrypt and sign a
> buffer with a @msgkey key. @offset set the message index used by the
> encryption algorithm.
>
> zx_msgkey_decrypt(zx_handle_t msgkey, uint64_t offset, const void*
> buffer, size_t buffer_size, uint32_t options, void* out, size_t
> out_size, size_t* actual);
>
> The zx_msgkey_decrypt() syscall enables to directly decrypt and
> authenticate a buffer with a @msgkey key.
>
> zx_msgkey_encrypt and zx_msgkey_decrypt may be useful to quickly encrypt
> and decrypt data (e.g. file name) and to further transform them (e.g.
> base64) which may be required by the recipient (e.g. file system
> service). Long-term encryption and decryption can be performed thanks to
> two following syscalls.
>
> zx_msgkey_splice_socket(zx_handle_t msgkey, uint64_t offset, zx_handle_t
> socket, uint32_t options, zx_handle_t* out);
>
> The zx_msgkey_splice_socke() syscall consumes @msgkey and @socket to
> create a new @out socket. This socket can be used to transparently
> encrypt/sign data with zx_socket_write() and decrypt/authenticate data
> with zx_socket_read() to/from the other side of @socket. @offset sets
> the initial message index which will be automatically increased with
> socket reads and writes. Such
> auto-encrypting/signing/decrypting/authenticating socket can be useful
> to transmit confidential data between (remote or local) peers.
>
> zx_msgkey_splice_stream(zx_handle_t msgkey, uint64_t offset, zx_handle_t
> stream, uint32_t options, zx_handle_t* out);
>
> The zx_msgkey_splice_socke() syscall consumes @msgkey and @stream to
typo: zx_msgkey_splice_stream()

> create a new @out stream. This enables to transparently encrypt/decrypt
> data to/from a VMO (e.g. read-only file). @offset sets the initial
> message index which is automatically adjusted according to the
> corresponding VMO offset.
>
> Component instance authenticity
> -------------------------------
>
> Being able to authenticate a component instance may be useful for (local
> or remote) storage services, secure delegated configurations (i.e.
> storage quota tied to the client but data only accessible to the server)
> and trusted human-computer interfaces (e.g. inform users of the
> component instances drawing on screen).
>
> A peerkey object refers to the (unforgeable) identity of a component
> instance. A unique peerkey should be created by the component manager
> and dedicated to each component instance it creates. Such component
> receive their peerkey as an initial handle and can use it as a component
> instance ID and to authenticate another component instance ID.
>
> Using dedicated key handle types (keymsg, peerkey, passkey and passid)
> avoids misuse of transferred keys (e.g. confused deputy attack).
> Nonetheless, each key type should be derived from a different context.
>
> zx_peerkey_derive(zx_handle_t keyring, const void* context, size_t
> context_size, uint32_t options, zx_handle_t* out);
>
> The zx_peerkey_derive() syscall creates a new @out peerkey object
> derived from the main key of @keyring thanks to @context.
>
> zx_passkey_create(zx_handle_t peerkey, const char* usage, size_t
> usage_size, uint32_t options, zx_handle_t* out);
>
> The zx_passkey_create() syscall creates a passkey object which enables
> to authenticate and use a received passid. @usage sets a specific usage
> name (e.g. "StorageProtocol:cache",
> "UserInterfaceProtocol:notification", etc.) for @out which must match
> the one used by the verified party (i.e. a passid). Unlike contexts used
> for derived-type syscalls, a @peerkey can create passkeys with the same
> @usage.
>
> zx_passid_create(zx_handle_t peerkey, const char* usage, size_t
> usage_size, zx_handle_t wrapped_handle, uint32_t options,
> zx_handle_t* out);
>
> The zx_passid_create() syscall creates a pass identity @out from a
> @peerkey. A passid is meant to be transferred to another component
> instance. @usage sets a specific usage name for @out which must match
> the one used by the verifier party (i.e. a passkey). @wrapped_handle is
> an arbitrary optional handle (e.g. ZX_HANDLE_INVALID) to pass with the
> passid and only accessible if authenticated.
>
> The created @out passid doesn't have a ZX_RIGHT_DUPLICATE right. A
> passid shouldn't have a (readable) related_koid, which could be used to
> identify an (ephemeral) identity (i.e. peerkey KOID). Zircon always
> requires a peerkey of the same keyring to use/unwrap a passid handle.
> For instance, this enables to check that a service is in the same user
> session as a client component instance, which may give guarantees that
> shared data stay in a comparable trusted level.
>
> zx_passid_unwrap(zx_handle_t passid, zx_handle_t passkey, uint32_t
> options, zx_handle_t* out);
>
> The zx_passid_unwrap() consumes @passid and return the wrapped handle,
> usually a channel, or ZX_HANDLE_INVALID. @passid and @passkey must have
> the same usage and they must come from the same keyring (but from
> different peerkeys).
>
> zx_passid_compute_hmac(zx_handle_t passid, zx_handle_t passkey, uint32_t
> checksum_type, const void* buffer, size_t buffer_size, uint32_t options,
> void* hmac, size_t hmac_size);
>
> The zx_passid_compute_hmac() syscall generates and write the HMAC of the
> input @buffer with the combination of @passid and @passkey. This enables
> to authenticate a component instance while preserving its privacy (i.e.
> not infering its URL nor its moniker), and avoid confused deputy attacks
> by forwarding a received passid. The HMAC can be used to avoid flooding
> services with an arbitrary number of IDs. @passid and @passkey must have
> the same usage and they must come from the same keyring (but from
> different peerkeys). @checksum_type identifies the used checksum (e.g.
> ZX_KEY_CHECKSUM_SHA256). The @hmac_size must match the size of a
> @checksum_type value.
>
> Component instance encryption
> -----------------------------
>
> The following syscalls enable to encrypt data for a component instance
> even if it is not started yet. For instance, this can be useful for
> encrypting received user messages (e.g. SMS) for the user session
> whereas the component instances related to this session are not
> available because the session is not open nor unlocked.
>
> zx_passid_get_public_key(zx_handle_t passid, zx_handle_t passkey,
> uint32_t options, void* out, size_t out_size, size_t* actual);
>
> The zx_passid_get_signature() syscall write the public key of the
> passkey sibling of @passid to @out. @passkey must come from the same
> peerkey as @passid. This signature enables to encrypt messages even when
typo: from the same *keyring* as @passid (and must have the same usage).

> the creator of @passid isn't available (e.g. user session not yet
> started), thanks to the following syscall.
>
> zx_msgkey_derive_encrypt(zx_handle_t passkey, const void*
> passid_public_key, uint32_t options, zx_handle_t* out);
>
> The zx_msgkey_derive_encrypt() syscall creates a new msgkey @out which
> is only able to encrypt (i.e. write-only right) messages for the passkey
> sibling of the passid which created @passid_public_key (i.e. with the
> same usage and the same parent peerkey).
>
> zx_msgkey_derive_decrypt(zx_handle_t passkey, zx_handle_t passid,
> uint32_t options, zx_handle_t* out);
>
> The zx_msgkey_derive_decrypt() syscall creates a new msgkey @out which
> is only able to decrypt (i.e. read-only right) messages encrypted by the
> passkey sibling of @passid for the signature of passid sibling of
> @passkey. This enable component instances to decrypt messages signed by
> a specific peer.
>
> Component manager
> -----------------
>
> The component manager initially gets a root keyring derived from the
> secure boot state (i.e. system authenticity). The component manager is
> then in charge of deterministically (thanks to component URLs and
> monikers) deriving new keyrings dedicated per component instance, and to
> assign them a peerkey for component authentication and asymmetric
> encryption.
>
> Encrypted and shareable file hierarchies
> ----------------------------------------
>
> Using msgkey objects, a similar approach to EncFS or ext4 encryption
> could be implemented. Some inspiration can also be taken from iOS keys
> (class) management.
>
> At the files level, a process can create an msgkey object for a specific
> file hierarchy. An encrypted (randomly generated) private key can be
> stored in each file and directory (e.g. in the encrypted filename) and
> used to encrypt and sign files' and directories' content. For each child
> node, a new msgkey is derived from the parent msgkey and the node's key,
> which creates a dedicated msgkey for the child hierarchy. When linking
> or moving a file to another hierarchy, re-encrypt (or encrypt) the node
> key with the new hierarchy key (i.e. similar to LUKS).
>
> Thanks to this derivation mechanism, a file hierarchy can be shared with
> a handle to this hierarchy (storage service side) and a handle to the
> related msgkey (data owner side). When sharing a hierarchy read-only,
> the msgkey can be shared without the write right. This approach is
> theoretically independent from the file system but may have limitations
> according to the filesystem limitations (e.g. file name length).
> However, a native Fuchsia filesystem could leverage the fact that a
> directory is also a file (which can then store an encrypted key for its
> hierarchy).
>
> For compatibility reasons, each node content should start with a magic
> value tied to the specific set of cryptographic algorithms used to
> encrypt and authenticate the rest of the content. As for ABI revisions,
> next versions of this magic should not be predictable.
>
> The filesystem FIDL protocols and the corresponding library should take
> into account the bigger (encrypted) file name size (e.g. by grouping
> them) and the per-node keys. The filesystem library (fdio) could use a
> "_cleartext" suffix for all cleartext IO functions, which should rarely
> be used (e.g. write file in cleartext on an USB drive).
>
> Encrypted storage devices
> -------------------------
>
> At the storage device level, zxcrypt drivers could use a dedicated
> keyring to encrypt a high-entropy key for their own use. It may be
> preferable to use a quick symmetric encryption (i.e. without
> authentication) to encrypt all local storage devices but let component
> instances be in charge of their own data authentication (and
> decryption).
>
> To avoid double encryption but still ensure that data at rest are
> correctly encrypted (and signed), an improvement could be to ensure that
> data written by zxcrypt clients to the zxcrypt device is encrypted by a
> system-derived key. This could be enforced by a locked stream handle
> which could only be unlocked by splicing it with an msgkey derived from
> the root of a specific keyring (e.g. local root keyring form a specific
> slot).
>
> Network encryption
> ------------------
>
> Similarly to the zxcrypt optimization, network encryption could be
> enforced with locked socket handles to ensure that data is properly
> encrypted thanks to an msgkey derived from the root of a specific
> keyring (e.g. imported keyring shared between multiple Fuchsia devices).
> This may be interesting for overnet.
>
> Performance impact
> ------------------
>
> The performance overhead should not be negligible, especially because of
> the authentication mechanism, but new hardware features could
> improve it.
>
> I think there is a performance improvement opportunity thanks to the
> vDSO and CPU security features such as BTI, XOM and PAC (with its
> limitations), but the thread capabilities would need to be restricted
> and side channel attacks would need to be addressed. Another opportunity
> would be to leverage the asynchronous syscall mechanism with ring
> buffers, a bit like Linux's io_uring(2).
>
> Regards,
>  Mickaël
>
> --
> All posts must follow the Fuchsia Code of Conduct
> https://fuchsia.dev/fuchsia-src/CODE_OF_CONDUCT
> <https://fuchsia.dev/fuchsia-src/CODE_OF_CONDUCT> or may be removed.
> ---
> To unsubscribe from this group and stop receiving emails from it,
> send an email to component-framewo...@fuchsia.dev
> <mailto:component-framework-dev%2Bunsu...@fuchsia.dev>.
>

Justin Mattson

unread,
Jan 6, 2021, 2:23:44 PM1/6/21
to discuss, m...@digikod.net, dis...@fuchsia.dev, Adam Barth
I hope not to fork this thread. I sense I have not nearly the security background that you do Mickaël, so forgive my naive question, but do you see a way of doing this without a significant expansion of the kernel APIs? What is the minimal kernel interface that could be used to get the vast majority of the functionality into a usermode program? Would "properly" controlled access to encryption-supporting hardware (ie. TrustZone, TPM, etc) be sufficient?

There are a variety of benefits to putting stuff outside the kernel and since I imagine you're conscious of those I'll skip my soap box speech. I also wonder these things must be in the kernel if there is a way to prototype it outside the kernel with no or minimal change to the syscalls.

Cheers,
Justin

Mickaël Salaün

unread,
Jan 6, 2021, 4:16:45 PM1/6/21
to Justin Mattson, discuss, Adam Barth

On 06/01/2021 20:13, Justin Mattson wrote:
> I hope not to fork this thread. I sense I have not nearly the security
> background that you do Mickaël, so forgive my naive question, but do you
> see a way of doing this without a significant expansion of the kernel
> APIs? What is the minimal kernel interface that could be used to get the
> vast majority of the functionality into a usermode program? Would
> "properly" controlled access to encryption-supporting hardware (ie.
> TrustZone, TPM, etc) be sufficient?

This is an important question. Extending the kernel with new syscalls
increase the attack surface of the whole system.

The main idea is to enable every processes to encrypt their own data
*before* transferring them to a storage service (e.g. a filesystem),
thanks to IPC primitives provided by the kernel.

These syscalls are a bit similar, from a trust point of view, to the
zx_cprng_*() syscalls. The kernel is in charge of managing derivation
and this require almost no user space input (except to add entropy if
user space which to): the kernel doesn't need a storage (e.g.
filesystem) to derive and provide random numbers (even if user space can
make this more secure thanks to saved random seeds). This is similar for
the syscalls in this proposal. The new kernel objects don't need to rely
on user space inputs (other than critical drivers maybe).

From a performance point of view, it is much more handy to avoid context
switches to read a random value or to manage encrypted data. Mixing this
with filesystem encryption would make the performances even worse.

From a trust point of view, the kernel is already in the Trusted
Computing Base for every component instances. Delegating the secret key
management to user space processes would increase this TCB (and add
another overhead). Because this derivation model relies (in part) on the
integrity of the system (e.g. secure boot), the kernel is a central
actor there and it can manage these keys in a standalone way.

Another point is that the splice syscalls directly wrap socket and
stream objects with encryption/decryption primitives. This make these
cryptographic mechanism easier to use for user space and is important to
get decent performances.

I also guess there is a need to keep Zircon independent from a
full-feature Fuchsia, for instance to expose some (cryptographic)
primitives to early processes.

>
> There are a variety of benefits to putting stuff outside the kernel and
> since I imagine you're conscious of those I'll skip my soap box speech.
> I also wonder these things must be in the kernel if there is a way to
> prototype it outside the kernel with no or minimal change to the syscalls.

I think a pure user space prototype can be develop, except for the
splice syscalls, but I don't know if it's worth it.

These new syscalls are cryptographic primitives which should be used by
most component for their own use (e.g. secure storage) and leveraged
with their exposed protocols/services. For instance, the
zx_keyring_import() syscall can be use by specific component instances
to share keys between multiple devices connected to the same user
account (e.g. a Google account). The kernel exposes basic primitives to
have a common interface wherever the root keys come from.

Justin Mattson

unread,
Jan 6, 2021, 6:33:43 PM1/6/21
to Mickaël Salaün, discuss, Adam Barth
On Wed, Jan 6, 2021 at 1:14 PM Mickaël Salaün <m...@digikod.net> wrote:

On 06/01/2021 20:13, Justin Mattson wrote:
> I hope not to fork this thread. I sense I have not nearly the security
> background that you do Mickaël, so forgive my naive question, but do you
> see a way of doing this without a significant expansion of the kernel
> APIs? What is the minimal kernel interface that could be used to get the
> vast majority of the functionality into a usermode program? Would
> "properly" controlled access to encryption-supporting hardware (ie.
> TrustZone, TPM, etc) be sufficient?

This is an important question. Extending the kernel with new syscalls
increase the attack surface of the whole system.

The main idea is to enable every processes to encrypt their own data
*before* transferring them to a storage service (e.g. a filesystem),
thanks to IPC primitives provided by the kernel.

Sure, what I'm not sure is that this needs to be mediated by the kernel. I could imagine a user space service that does key management and hands out keys to processes that the process can use to encrypt data going to/from a storage service. If the user space service doing key management is signed and verified, is it different than the kernel in terms of trust?
 

These syscalls are a bit similar, from a trust point of view, to the
zx_cprng_*() syscalls. The kernel is in charge of managing derivation
and this require almost no user space input (except to add entropy if
user space which to): the kernel doesn't need a storage (e.g.
filesystem) to derive and provide random numbers (even if user space can
make this more secure thanks to saved random seeds). This is similar for
the syscalls in this proposal. The new kernel objects don't need to rely
on user space inputs (other than critical drivers maybe).

From a performance point of view, it is much more handy to avoid context
switches to read a random value or to manage encrypted data. Mixing this
with filesystem encryption would make the performances even worse.

From a trust point of view, the kernel is already in the Trusted
Computing Base for every component instances. Delegating the secret key
management to user space processes would increase this TCB (and add
another overhead). Because this derivation model relies (in part) on the
integrity of the system (e.g. secure boot), the kernel is a central
actor there and it can manage these keys in a standalone way.

The TCB already extends far beyond the kernel and at least in terms of concerns about the size of the TCB I don't think this needs to be added to the kernel. I work on component manager and so of course I see it as the solution to many problems. Although I would try to put this in a regular component, not in component manager.
 

Another point is that the splice syscalls directly wrap socket and
stream objects with encryption/decryption primitives. This make these
cryptographic mechanism easier to use for user space and is important to
get decent performances.

Performance may be relevant here. In terms of ease of use for programs I wonder how much we can get out of libraries and tools.
 

I also guess there is a need to keep Zircon independent from a
full-feature Fuchsia, for instance to expose some (cryptographic)
primitives to early processes.

>
> There are a variety of benefits to putting stuff outside the kernel and
> since I imagine you're conscious of those I'll skip my soap box speech.
> I also wonder these things must be in the kernel if there is a way to
> prototype it outside the kernel with no or minimal change to the syscalls.

I think a pure user space prototype can be develop, except for the
splice syscalls, but I don't know if it's worth it.

Part of the equation I see here is that a user space prototype is something with fewer obstacles to implementation. Then I think the question becomes how easily could it be lifted into the kernel if we wanted to move it, if the moving cost is low, then not much seems lost.

Mickaël Salaün

unread,
Jan 7, 2021, 6:28:13 AM1/7/21
to Justin Mattson, discuss, Adam Barth

On 07/01/2021 00:33, Justin Mattson wrote:
>
>
> On Wed, Jan 6, 2021 at 1:14 PM Mickaël Salaün <m...@digikod.net
> <mailto:m...@digikod.net>> wrote:
>
>
> On 06/01/2021 20:13, Justin Mattson wrote:
> > I hope not to fork this thread. I sense I have not nearly the security
> > background that you do Mickaël, so forgive my naive question, but
> do you
> > see a way of doing this without a significant expansion of the kernel
> > APIs? What is the minimal kernel interface that could be used to
> get the
> > vast majority of the functionality into a usermode program? Would
> > "properly" controlled access to encryption-supporting hardware (ie.
> > TrustZone, TPM, etc) be sufficient?
>
> This is an important question. Extending the kernel with new syscalls
> increase the attack surface of the whole system.
>
> The main idea is to enable every processes to encrypt their own data
> *before* transferring them to a storage service (e.g. a filesystem),
> thanks to IPC primitives provided by the kernel.
>
>
> Sure, what I'm not sure is that this needs to be mediated by the kernel.
> I could imagine a user space service that does key management and hands
> out keys to processes that the process can use to encrypt data going
> to/from a storage service.

I think there is a misunderstanding. One goal of this proposal is to not
expose keys to processes using them. This avoid key misuse, key leaks,
and also enable to enforce encryption/authentication to get interesting
security guarantees (e.g. a component instance may be forced to only
write or transfer encrypted data). Not exposing secret key payloads
(thanks to key handles) forbids processes to (directly) encrypt data by
themselves but require them to rely on another party (the kernel, or
another process) that manages and directly uses the keys, which implies
extra round-trips (i.e. for each write and read operation) in the case
of a dedicated service. If the key management (and
encryption/signature/decryption/authentication) is delegated to the
kernel, there is no extra round-trips because processes already need to
talk to the kernel (e.g. to send data to a filesystem service).

Using cryptographic keys through handles is a very interesting property
that can leverage the micro-kernel architecture of Fuchsia and could
help make it "more secure" than monolithic kernel systems by removing
(local and remote) storage services from the TCB. This mechanism also
fits well with secure elements (or HSM) relying on PKCS#11 or similar
protocols.

> If the user space service doing key
> management is signed and verified, is it different than the kernel in
> terms of trust?

In theory it should be the same, but in practice the key management
service may rely on other services (e.g. component manager, storage
services, etc.) which then extend the TCB *for cryptographic operations*
(for the whole system). If the key management service is a standalone
process (i.e. not a component instance) for which all resources come
*from the ZBI*, then it should not extend much the TCB for cryptographic
operations (assuming there is no handle referring to this process, nor
to its threads, nor to its memory, etc.).

The kernel can also add extra key protection mechanisms not possible for
user space (e.g. memory protection keys, dedicated memory mapping
similar to Linux's MFD_SECRET patches, etc.), and make sure keys can be
wiped out from memory.

About system integrity/trust, key derivations should rely on a secure
boot mechanism. Early boot processes should be able to use cryptographic
primitives derived from the state of the running system. A kernel-side
stateless key derivation avoid key management bootstrapping issues.

>  
>
>
> These syscalls are a bit similar, from a trust point of view, to the
> zx_cprng_*() syscalls. The kernel is in charge of managing derivation
> and this require almost no user space input (except to add entropy if
> user space which to): the kernel doesn't need a storage (e.g.
> filesystem) to derive and provide random numbers (even if user space can
> make this more secure thanks to saved random seeds). This is similar for
> the syscalls in this proposal. The new kernel objects don't need to rely
> on user space inputs (other than critical drivers maybe).
>
> >From a performance point of view, it is much more handy to avoid
> context
> switches to read a random value or to manage encrypted data. Mixing this
> with filesystem encryption would make the performances even worse.
>
> >From a trust point of view, the kernel is already in the Trusted
> Computing Base for every component instances. Delegating the secret key
> management to user space processes would increase this TCB (and add
> another overhead). Because this derivation model relies (in part) on the
> integrity of the system (e.g. secure boot), the kernel is a central
> actor there and it can manage these keys in a standalone way.
>
>
> The TCB already extends far beyond the kernel and at least in terms of
> concerns about the size of the TCB I don't think this needs to be added
> to the kernel.

Thanks to the micro-kernel architecture, I consider that the TCB may
vary depending on the services a process is relying on. If all system
processes use the key management service (which they should), then the
TCB for all these processes include this service. However, the attack
surface on the kernel is not extended if the key management is done by a
process, but this implies important performance overheads and some
limitations: extra round-trips and extra memory copies (because splice
syscalls would not be possible).

> I work on component manager and so of course I see it as
> the solution to many problems. Although I would try to put this in a
> regular component, not in component manager.
The component manager is a critical part of Fuchsia because it is in
charge of launching most processes and managing their accesses. I think
it make sense to rely on the component manager to derive and distribute
key handles per component instances. This also enable component
instances to authenticate themselves in a secure way (i.e. the component
manager acts as the trust reference for its component instances).

>  
>
>
> Another point is that the splice syscalls directly wrap socket and
> stream objects with encryption/decryption primitives. This make these
> cryptographic mechanism easier to use for user space and is important to
> get decent performances.
>
>
> Performance may be relevant here. In terms of ease of use for programs I
> wonder how much we can get out of libraries and tools.

Libraries should play an important part, especially for an efficient
encrypted filesystem integration. FIDL could also be extended to enable
authenticated connections (which should help to simplify the special
handling of storage capabilities for the component manager), and ease
(or force) the use of encryption/authentication of transferred/shared data.

>  
>
>
> I also guess there is a need to keep Zircon independent from a
> full-feature Fuchsia, for instance to expose some (cryptographic)
> primitives to early processes.
>
> >
> > There are a variety of benefits to putting stuff outside the
> kernel and
> > since I imagine you're conscious of those I'll skip my soap box
> speech.
> > I also wonder these things must be in the kernel if there is a way to
> > prototype it outside the kernel with no or minimal change to the
> syscalls.
>
> I think a pure user space prototype can be develop, except for the
> splice syscalls, but I don't know if it's worth it.
>
>
> Part of the equation I see here is that a user space prototype is
> something with fewer obstacles to implementation. Then I think the
> question becomes how easily could it be lifted into the kernel if we
> wanted to move it, if the moving cost is low, then not much seems lost.

Right, this should be OK for common component instance use cases (but
probably not for boot integrity).

>  
>
>
> These new syscalls are cryptographic primitives which should be used by
> most component for their own use (e.g. secure storage) and leveraged
> with their exposed protocols/services. For instance, the
> zx_keyring_import() syscall can be use by specific component instances
> to share keys between multiple devices connected to the same user
> account (e.g. a Google account). The kernel exposes basic primitives to
> have a common interface wherever the root keys come from.
>
> >
> > Cheers,
> > Justin
> >
> > On Wednesday, January 6, 2021 at 10:28:56 AM UTC-8
> <mailto:component-framewo...@fuchsia.dev>
> >     > <mailto:component-framework-dev%2Bunsu...@fuchsia.dev
> <mailto:component-framework-dev%252Bunsu...@fuchsia.dev>>.
> >     >
> >
>

Mickaël Salaün

unread,
Feb 6, 2021, 2:08:59 PM2/6/21
to Justin Mattson, discuss, Adam Barth
Hi Justin, was my answers clear?

After more thought, I'm still convinced the keyring and msgkey kernel
objects should exist. However, I changed my mind about the
authentication handles (i.e. peerkey, passkey and passid). I think
component instance authentication can and should be implemented by the
component manager.

The initial handshake, when establishing a FIDL connection, is initiated
thanks to the component manager (if I understood correctly the
implementation and if it didn't change). The initial received request
could then include a "route/capability/client/connection ID" provided by
the component manager. This ID would be similar to an IP address
(without NAT) but unforgeable and trusted (because directly provided by
the component manager).
Similar to the passid/passkey proposition, this ID could be generated
determiniscally while protecting component instance privacy (i.e. IDs
relative to the component instance receiving it). This can be achieved
thanks to the use of a key derivation function using the absolute
component topologies (client and server) and a msgkey (owned and managed
by the component manager) derived from the secure boot state.

To enable stateful services (e.g. network storage) to remove data tied
to deleted component instances, the component manager could send events
with such connection IDs tied to the component instances being deleted.

Being able to dynamically grant access rights to component instances
could be handled by the component manager. This can be used for user
approval (e.g. Andoid authorization popup), or other kind of access
control systems managed by something else than the static policies
defined in each component or the static policy of the component manager.
For fine-grained access-rights, each component instance could ask the
component manager if a specific "connection ID" is allowed to perform a
specific access or not.

James Tucker

unread,
Feb 8, 2021, 3:06:36 PM2/8/21
to Mickaël Salaün, Justin Mattson, discuss, Adam Barth
On Sat, Feb 6, 2021 at 11:09 AM Mickaël Salaün <m...@digikod.net> wrote:
Hi Justin, was my answers clear?

After more thought, I'm still convinced the keyring and msgkey kernel
objects should exist. However, I changed my mind about the
authentication handles (i.e. peerkey, passkey and passid). I think
component instance authentication can and should be implemented by the
component manager.

The initial handshake, when establishing a FIDL connection, is initiated
thanks to the component manager (if I understood correctly the
implementation and if it didn't change). The initial received request
could then include a "route/capability/client/connection ID" provided by
the component manager. This ID would be similar to an IP address
(without NAT) but unforgeable and trusted (because directly provided by
the component manager).
Similar to the passid/passkey proposition, this ID could be generated
determiniscally while protecting component instance privacy (i.e. IDs
relative to the component instance receiving it). This can be achieved
thanks to the use of a key derivation function using the absolute
component topologies (client and server) and a msgkey (owned and managed
by the component manager) derived from the secure boot state.

In using a KDF in a scenario such as this, is it safe to use a low number of iterations, or would it be necessary for this design to add significant CPU overhead to component creation in order to ensure these are derivations sufficiently private? What would be the expected impact for, say, booting a complex system topology?
 
All posts must follow the Fuchsia Code of Conduct https://fuchsia.dev/fuchsia-src/CODE_OF_CONDUCT or may be removed.
---
You received this message because you are subscribed to the Google Groups "discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to discuss+u...@fuchsia.dev.
To view this discussion on the web visit https://groups.google.com/a/fuchsia.dev/d/msgid/discuss/bc0f6e6e-ca0a-ed25-d7c8-9aafa98e89a2%40digikod.net.

Mickaël Salaün

unread,
Feb 9, 2021, 8:05:42 AM2/9/21
to James Tucker, Justin Mattson, discuss, Adam Barth
This KDF should be carefully chosen to favor speed, keeping in mind that
part of the input is not a password but a random data (indirectly)
derived from the secure boot state. Moreover, known connection IDs can
be cached by the component manager (if this cache is only accessible
with a key derived from the secure boot state). Doing so, I think the
performance impact can be negligible compared to other operations.

The idea is for the component manager to generate a random (secret) seed
at paving time, and encrypt/authenticate it thanks to the secure boot
state (i.e. with a msgkey). This seed can then be used as part of the
input of the KDF, with the server topology, the client topology and the
(originally) offered capability name.

If we want to restore the original IDs after a new paving, we just need
to save and restore the (encrypted) seed; otherwise the previous IDs are
protected. A similar seed can be generated and used by the component
manager for msgkey derivation (this time performed by the kernel).

>
> To enable stateful services (e.g. network storage) to remove data tied
> to deleted component instances, the component manager could send events
> with such connection IDs tied to the component instances being deleted.
>
> Being able to dynamically grant access rights to component instances
> could be handled by the component manager. This can be used for user
> approval (e.g. Andoid authorization popup), or other kind of access
> control systems managed by something else than the static policies
> defined in each component or the static policy of the component manager.
> For fine-grained access-rights, each component instance could ask the
> component manager if a specific "connection ID" is allowed to perform a
> specific access or not.
>
>
> On 07/01/2021 12:28, Mickaël Salaün wrote:
> >
> > On 07/01/2021 00:33, Justin Mattson wrote:
> >>
> >>
> >> On Wed, Jan 6, 2021 at 1:14 PM Mickaël Salaün <m...@digikod.net
> <mailto:m...@digikod.net>
> >>     <mailto:component-framewo...@fuchsia.dev
> >>     <mailto:component-framework-dev%252Bunsu...@fuchsia.dev
> <mailto:component-framework-dev%25252Bunsu...@fuchsia.dev>>>.
> >>     >     >
> >>     >
> >>
> >
>
> --
> All posts must follow the Fuchsia Code of Conduct
> https://fuchsia.dev/fuchsia-src/CODE_OF_CONDUCT
> <https://fuchsia.dev/fuchsia-src/CODE_OF_CONDUCT> or may be removed.
> ---
> You received this message because you are subscribed to the Google
> Groups "discuss" group.
> To unsubscribe from this group and stop receiving emails from it,
> send an email to discuss+u...@fuchsia.dev
> <mailto:discuss%2Bunsu...@fuchsia.dev>.
> <https://groups.google.com/a/fuchsia.dev/d/msgid/discuss/bc0f6e6e-ca0a-ed25-d7c8-9aafa98e89a2%40digikod.net>.
>
> --
> All posts must follow the Fuchsia Code of Conduct
> https://fuchsia.dev/fuchsia-src/CODE_OF_CONDUCT
> <https://fuchsia.dev/fuchsia-src/CODE_OF_CONDUCT> or may be removed.
> ---
> You received this message because you are subscribed to the Google
> Groups "discuss" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to discuss+u...@fuchsia.dev
> <mailto:discuss+u...@fuchsia.dev>.
> To view this discussion on the web visit
> https://groups.google.com/a/fuchsia.dev/d/msgid/discuss/CALMiPQCR0AiwOm0YYeSTqdx_9s9eyaxOoF%3D001WfvQti2RoGNw%40mail.gmail.com
> <https://groups.google.com/a/fuchsia.dev/d/msgid/discuss/CALMiPQCR0AiwOm0YYeSTqdx_9s9eyaxOoF%3D001WfvQti2RoGNw%40mail.gmail.com?utm_medium=email&utm_source=footer>.

Justin Mattson

unread,
Feb 9, 2021, 6:46:10 PM2/9/21
to Mickaël Salaün, James Tucker, discuss, Adam Barth
Ultimately in the kernel vs in component manager vs in a component comes down to, in my mind, a trade-off between isolation, performance, and updatability. For example, if this functionality were hosted in a component there is much more of a possibility that we could one day develop an update mechanism that would update this code without a system reboot.

I'm not a decision maker here, but the discussion has lodged a question in my mind about the level at which encryption is handled.

Cheers,
Justin
Reply all
Reply to author
Forward
0 new messages