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>.
>