Crypto Functionality
Orgs
Creating a new org
1.) On the client, a new NaCl keypair, consisting of a PublicKey and PrivateKey, is generated.
2.) The PublicKey is associated with a new OrgUserDevice for the creating user's device.
3.) A new OrgUser is created for the creating user with the Org Owner
org role.
4.) A TrustedRootMap is created with the newly generated PublicKey, then signed, setting the new user's device as the TrustedRoot for the new org. The SignedTrustedRoot is set on the OrgUserDevice.
5.) A new Org, OrgUser, OrgUserDevice, and other associated objects are created on the EnvKey host. The PrivateKey is not sent to the host.
6.) The PrivateKey, along with a newly generated API authentication token and other authentication data (orgId, deviceId, userId, etc.) is stored in a ClientUserAuth object on the client.
7.) The user is prompted to generated a RecoveryKey in case access is lost.
Invites
Inviting a user
1.) On the client, a random 22 character alphanumeric EncryptionKey is securely generated.
2.) A new NaCl keypair, consisting of an InvitePublicKey and an InvitePrivateKey, is generated for the Invite.
3.) The InvitePrivateKey is symmetrically encrypted with the EncryptionKey and associated with a new Invite.
4.) The InvitePublicKey is signed by the PrivateKey of the authenticated device and associated with the new Invite.
5.) The TrustedRootMap is signed by the InvitePrivateKey and associated with the new Invite.
6.) An IdentityHash is generated for the new Invite. It's a sha256 hash of:
- The inviting user's id
- The inviting user's PublicKey
- The inviting user's email
- The invited user's email
- The EnvKey host url
- The Encryption Key generated in the first step
7.) Depending on the invited user's access level, intermediate keys associated with any blobs the invited user should have access to are encrypted for the InvitePublicKey and associated with the Invite.
8.) The Invite is created on the EnvKey API host. Only the encrypted InvitePrivateKey is sent to the host--the unencrypted InvitePrivateKey is not stored anywhere. An email is sent to the invited user including an Invite Token that will be required to accept the invite.
9.) The inviting user also sends the invited user an Encryption Token out-of-band, consisting of the IdentityHash and the EncryptionKey.
Accepting a user invite
1.) When a user is invited to an EnvKey org, they receive an Invite Token by email, and an Encryption Token out of band by the inviting user.
2.) On the client, the user inputs both the Invite Token and Encryption Token. A request is made to the EnvKey host with the Email Token and the first portion of the Encryption Token, the IdentityHash. The EnvKey host then returns the Invite and associated intermediate keys that were encrypted to the Invite's PublicKey.
3.) The Invite's details are verified against the IdentityHash.
4.) The Invite's SignedTrustedRoot is verified with the InvitePublicKey.
5.) If the TrustedRoot has been replaced since the Invite was sent and, process the replacement(s) to determine the new TrustedRoot.
6.) The encrypted InvitePrivateKey is decrypted with the EncryptionKey, the second portion of the Encryption Token.
7.) Intermediate keys associated with any blobs the invited user has access to are now decrypted and stored in memory.
8.) A new keypair is now generated, consisting of a PrivateKey and PublicKey.
9.) The newly generated PublicKey is signed by the InvitePrivateKey.
10.) The signed PublicKey is associated with a new OrgUserDevice for the creating invited user's device.
11.) Intermediate keys associated with any blobs the invited user has access to are now re-encrypted to the newly generated PublicKey.
12.) A new Org, OrgUser, OrgUserDevice, and other associated objects are created on the EnvKey host. The PrivateKey is not sent to the host.
13.) The PrivateKey, along with a newly generated API authentication token and other authentication data (orgId, deviceId, userId, etc.) is stored in a ClientUserAuth object on the client.
14.) The user is prompted to generated a RecoveryKey in case access is lost.
Revoking a user invite
When an outstanding Invite is revoked, the intermediate blob keys encrypted for the Invite are deleted immediately, then all blobs the Invite had access to and their associated intermediate keys to are marked for re-encryption.
Users
Note: In this section, 'user' can refer to either an OrgUser (a human user) or a CLI key.
Updating a user's org role
1.) When a user's org role is updated, any intermediate blob keys that the user should now have access to with the new role are encrypted for all PublicKeys associated with the user.
2.) If access to any blobs was removed by the update, the intermediate blob keys encrypted for the target user are deleted immediately, and these blobs are all marked for re-encryption.
Revoking a user
1.) When a target user is removed from the org, the intermediate blob keys encrypted for the target are deleted immediately, and the target is marked deactivated by marking the CLI key or all the devices associated with the user with the deactivatedAt
timestamp. When a user is deactivated, the target and their devices are still loaded by the client because the associated PublicKey(s) may be used in decryption or verification operations, but they are no longer visible to any users and the removed user can no longer authenticate requests.
2.) Next, all blobs the user had access to and their associated intermediate keys to are marked for re-encryption.
3.) If any of the PublicKeys associated with the user being removed are the RootTrustedPublicKey, the EnvKey host will queue a RootPubkeyReplacement for all devices, active invites or device grants, CLI keys, recovery keys, and ENVKEYs. Before performing any further operations, all EnvKey clients will first process the RootPubkeyReplacement, replacing the current TrustedRootPubkey with the device initiating removal.
4.) Once all blobs marked for re-encryption have been re-encrypted, the user can be deleted.
Renaming a user
Names are important for security--a fraudulent name change could cause a user to be granted access to data they shouldn't have access to. For this reason, a user must be authorized to make a name change.
By default, only users with the Org Owner
or Org Admin
role can update a user's name. A Basic User
that is an App Admin
also has limited invite permissions, so they can also update the name of a user they've invited until the invite is accepted.
Generating a CLI key
1.) On the client, a random 22 character alphanumeric CliKeyIdPart and a random 22 character alphanumeric EncryptionKey are securely generated.
2.) A new NaCl keypair, consisting of an CliPublicKey and an CliPrivateKey, is generated for the CliKey.
3.) The CliPrivateKey is symmetrically encrypted with the EncryptionKey and associated with a new CliKey.
4.) The CliPublicKey is signed by the PrivateKey of the authenticated device and associated with the new CliKey.
5.) The TrustedRootMap is signed by the CliPrivateKey and associated with the new CliKey.
6.) Depending on the CLIKey's access level, intermediate keys associated with any blobs the CliKey should have access to are encrypted for the CliPublicKey and associated with the CliKey.
7.) The CLIKey is created on the EnvKey API host. Only the encrypted CliPrivateKey is sent to the host--the unencrypted CliPrivateKey is not stored anywhere.
8.) The full CLI_ENVKEY
is displayed to the user: CLI_ENVKEY=[CLIKeyIdPart]_[EncryptionKey]_[CLIKeyHostUrl*]
*The CliKeyHostUrl is only included when the CliKey is generated in an org running a self-hosted EnvKey host.
Authenticating a CLI key
1.) The EnvKey CLI sends the CLIKeyIdPart to the EnvKey host. The host returns the encrypted CliPrivateKey.
2.) The encrypted CliPrivateKey is decrypted with the EncryptionKey. The decrypted CliPrivateKey and other authentication data (orgId, etc.) is stored in a ClientCliAuth object in memory.
3.) Subsequent requests are signed with the CliPrivateKey. The host authenticates signatures with the CliKey's PublicKey.
Devices
Authorizing a device
1.) On the client, a random 22 character alphanumeric EncryptionKey is securely generated.
2.) A new NaCl keypair, consisting of an DeviceGrantPublicKey and a DeviceGrantPrivateKey, is generated for the DeviceGrant.
3.) The DeviceGrantPrivateKey is symmetrically encrypted with the EncryptionKey and associated with a new Invite.
4.) The DeviceGrantPublicKey is signed by the PrivateKey of the currently authenticated device and associated with the new DeviceGrant.
5.) The TrustedRootMap is signed by the DeviceGrantPrivateKey and associated with the new DeviceGrant.
6.) An IdentityHash is generated for the new DeviceGrant. It's a sha256 hash of:
- The authorizing user's id
- The authorizing user's PublicKey
- The authorizing user's email
- The granted user's email
- The EnvKey host url
- The Encryption Key generated in the first step
7.) Any blobs the authorized device should have access to are encrypted for the DeviceGrantPublicKey and associated with the Invite.
8.) The DeviceGrant is created on the EnvKey API host. Only the encrypted DeviceGrantPrivateKey is sent to the host--the unencrypted DeviceGrantPrivateKey is not stored anywhere. An email is sent to the user whose device is being authorized including an Invite Token that will be required to accept the device authorization.
9.) The authorizing user also sends the user whose device is being authorized an Encryption Token out-of-band, consisting of the IdentityHash and the EncryptionKey.
Accepting a device invite
1.) When a new device is authorized, the device's owner receives an Invite Token by email, and an Encryption Token out of band by the authorizing user (which could be the device's owner or another user).
2.) On the client, the user inputs both the Invite Token and Encryption Token. A request is made to the EnvKey host with the Email Token and the first portion of the Encryption Token, the IdentityHash. The EnvKey host then returns the DeviceGrant and associated intermediate keys that were encrypted to the DeviceGrant's PublicKey.
3.) The DeviceGrant's details are verified against the IdentityHash.
4.) The DeviceGrant's SignedTrustedRoot is verified with the DeviceGrantPublicKey.
5.) If the TrustedRoot has been replaced since the DeviceGrant was sent and, process the replacement(s) to determine the new TrustedRoot.
6.) The encrypted DeviceGrantPrivateKey is decrypted with the EncryptionKey, the second portion of the Encryption Token.
7.) Intermediate keys associated with any blobs the grantee has access to are now decrypted and stored in memory.
8.) A new keypair is now generated, consisting of a PrivateKey and PublicKey.
9.) The newly generated PublicKey is signed by the InvitePrivateKey.
10.) The signed PublicKey is associated with a new OrgUserDevice.
11.) Intermediate keys associated with any blobs the grantee has access to are now re-encrypted to the newly generated PublicKey.
12.) A new Org, OrgUser, OrgUserDevice, and other associated objects are created on the EnvKey host. The PrivateKey is not sent to the host.
13.) The PrivateKey, along with a newly generated API authentication token and other authentication data (orgId, deviceId, userId, etc.) is stored in a ClientUserAuth object on the client.
Revoking a device invite
When an outstanding DeviceGrant is revoked, all blobs the grantee had access to and their associated intermediate keys to are marked for re-encryption.
Revoking a device
1.) When a device is revoked, the intermediate blob keys encrypted for the target device are deleted immediately, and the device is marked deactivated by marking it with the deactivatedAt
timestamp. When a device is deactivated, it is still loaded by the client because its PublicKey may be used in decryption or verification operations, but it's no longer visible to any users and the revoked device can no longer authenticate requests.
2.) Next, all blobs the user had access to and their associated intermediate keys to are marked for re-encryption.
3.) If any of the devices belonging to the user being removed are the RootTrustedPublicKey, the EnvKey host will queue a RootPubkeyReplacement for all devices, active invites or device grants, CLI keys, recovery keys, and ENVKEYs. Before performing any further operations, all EnvKey clients will first process the RootPubkeyReplacement, replacing the current TrustedRootPubkey with the device initiating removal.
Storing local device state
Some state is stored on the local device:
- Account authentication data (ClientUserAuth)
- Pending invites that haven't yet been sent
- Pending environment updates that haven't yet been committed
- Local device settings
- The last selected account and object in the EnvKey UI
- Available self-hosted host upgrades
This data is stored encrypted on-device in the file system. The encryption key for this file is stored in the OS credential store on Mac/Windows in the com.envkey.local-server.root-device-key
entry. On Linux, the encryption key for this file is stored in a config file in the file system (the config file is encrypted with a non-secret constant).
The encryption key is a 22 character secure random alphanumeric string, generated the first time EnvKey runs on a device. If a passphrase is set, the encryption key will be symmetrically encrypted with the passphrase, and only the encrypted key will be stored.
Locking EnvKey on a device
When EnvKey is locked, the device encryption key is encrypted with the passphrase, and all EnvKey data is cleared from memory.
Unlocking EnvKey on a device
When EnvKey is unlocked, the device encryption key is decrypted with the passphrase, then the encrypted local state (stored on the filesystem) is decrypted with the device encryption key, and local device state is loaded into memory.
Setting a local device lockout
If a lockout is set on a device, EnvKey will automatically lock when the lockout period is reached.
Recovery Keys
Generating a recovery key
1.) On the client, an EncryptionKey of 12 random words is securely generated from a pool of 1952 common English words. The user is directed to safely store the EncryptionKey. If the org is running self-hosted EnvKey, they are also directed to store the url of their host.
2.) A new NaCl keypair, consisting of a RecoveryPublicKey and a RecoveryPrivateKey, is generated for the RecoveryKey.
3.) The RecoveryPrivateKey is symmetrically encrypted with the EncryptionKey and associated with a new RecoveryKey.
4.) The RecoveryPublicKey is signed by the PrivateKey of the authenticated device and associated with the new RecoveryKey.
5.) The TrustedRootMap is signed by the RecoveryPrivateKey and associated with the new RecoveryKey.
6.) An IdentityHash is generated for the new RecoveryKey. It's a sha256 hash of:
- The EnvKey host url
- The Encryption Key generated in the first step
7.) Depending on the user's access level, intermediate keys associated with any blobs the user has access to are encrypted for the RecoveryPublicKey and associated with the RecoveryKey.
8.) The RecoveryKey is created on the EnvKey API host. Only the encrypted RecoveryKeyPrivateKey is sent to the host--the unencrypted RecoveryKeyPrivateKey is not stored anywhere.
Redeeming a recovery key
1.) If a user either loses access to their device or forgets their device passphrase, they can regain access to an org by redeeming a RecoveryKey. First, the user inputs the 15 word EncryptionKey. If their org is running self-hosted EnvKey, they'll also input the url of their host.
2.) The IdentityHash will be calculated from the EncryptionKey and host url. A request is made to the EnvKey host with the IdentityHash.
3.) The EnvKey host will look up the RecoveryKey, then send an EmailToken to the email address of the user who owns the RecoveryKey. A requires email auth
422 status is then returned to the client.
4.) The client will prompt the user for the EmailToken that was sent to the email address of the account they're attempting to recover. Another request is made with the IdentityHash and the EmailToken. The EnvKey host then returns the RecoveryKey and associated intermediate keys that were encrypted to the RecoverKey's PublicKey.
5.) The RecoveryKey's SignedTrustedRoot is verified with the RecoveryPublicKey.
6.) If the TrustedRoot has been replaced since the RecoveryKey was generated and, process the replacement(s) to determine the new TrustedRoot.
7.) The encrypted RecoveryPrivateKey is decrypted with the EncryptionKey.
8.) Intermediate keys associated with any blobs the redeeming user has access to are now decrypted and stored in memory.
9.) A new keypair is now generated, consisting of a PrivateKey and PublicKey.
10.) The newly generated PublicKey is signed by the RecoveryPrivateKey.
11.) The signed PublicKey is associated with a new OrgUserDevice.
12.) Intermediate keys associated with any blobs the grantee has access to are now re-encrypted to the newly generated PublicKey.
13.) A new OrgUserDevice and other associated objects are created on the EnvKey host. The PrivateKey is not sent to the host. Any previously active devices belonging to the user are now deactivated and marked for revocation. The RecoveryKey is marked redeemed so that it cannot be loaded again.
14.) The new PrivateKey, along with a newly generated API authentication token and other authentication data (orgId, deviceId, userId, etc.) is stored in a ClientUserAuth object on the client.
15.) The user is prompted to generate a new RecoveryKey.
Apps
Note: In this section, 'user' can refer to either an OrgUser (a human user) or a CLI key.
Granting a user access to an app
When a user is granted access to an app, any intermediate blob keys that the user should now have access to with the new role are encrypted for all PublicKeys associated with the user.
Revoking a user's access from an app
When a user's access to an app is removed, any intermediate blob keys that the user no longer has access to are deleted, and the associated blobs are marked for re-encryption.
Granting a team's access to an app
The same as granting a single user access, but the operation is carried out for all users belonging to the team.
Revoking a team's access to an app
The same as removing a single user's access, but the operation is carried out for all users belonging to the team.
Deleting an app
1.) When an app is deleted, all blobs and intermediate blob keys associated with the app are also deleted.
2.) In some cases, users may also lose access to block environments when an app is deleted. If that occurs, any blobs and intermediate blob keys associated with the environments and users whose access has been removed will be deleted, and then those blobs will be marked for re-encryption.
Blocks
Connecting a block to an app
When a block is connected to an app, users may gain access to blobs associated with the block. In that case, any intermediate blob keys that any user should now have access to are encrypted for all PublicKeys associated with these users.
Disconnecting a block from an app
When a block is disconnected from an app, users may lose access to blobs associated with the block. In that case, any blobs and intermediate blob keys associated with the environments and users whose access has been removed will be deleted, and then those blobs will be marked for re-encryption.
Environments
Updating environments and/or local overrides
When an env update is committed, a new changeset and the new values of any updates are symmetrically encrypted with the intermediate blob key associated with each blob and then posted to the EnvKey host for persistence.
Creating an environment
In some cases, creating an app environment may give users access to a corresponding block environments they didn't have access to previously.
Removing an environment
When an environment is deleted, all blobs and intermediate blob keys associated with the environment are also deleted. In some cases, users may also lose access to corresponding block environments
Teams
Adding a user to a team
When a user is added to a team, any intermediate blob keys that the user should now have access to as a member of the team are encrypted for all PublicKeys associated with the user.
Removing a user from a team
When a user is removed from a team, any intermediate blob keys that the user no longer has access to are deleted, and the associated blobs are marked for re-encryption.
Loading and fetching
Loading and verifying the current user
1.) When a user authenticates and fetches their view of the org graph, the client first verifies the PublicKey associated with the current device by encrypting a short constant for the PublicKey and signing the same constant with the PrivateKey stored on-device, then decrypting with the PrivateKey and verifying with the PublicKey, and ensuring the decrypted/verified data matches the original constant.
2.) The SignedTrustedRoot is verified with the current user's PublicKey.
3.) If any RootPubkeyReplacements are queued, these are verified and processed, setting a new TrustedRoot.
Loading and decrypting environments and changesets
1.) When an app or block's environments and/or changesets are loaded, the client first decrypts the intermediate blob keys returned by the EnvKey host with the on-device PrivateKey.
2.) In addition to the currently authenticated device's PrivateKey, decryption also requires the PublicKey of the user device or CLI key that encrypted each intermediate blob key (since authenticated decryption is built in to NaCl). Before decrypting any intermediate blob key, the PublicKey used to encrypt it will have its signature chain verified by the web of trust back to the TrustedRoot.
3.) Once an intermediate blob key has been decrypted, the associated blob is symmetrically decrypted with the intermediate key and stored in memory.
ENVKEYs
Generating an ENVKEY
1.) On the client, a random 22 character alphanumeric EnvkeyIdPart and a random 22 character alphanumeric EncryptionKey are securely generated.
2.) A new NaCl keypair, consisting of an ENVKEYPublicKey and an ENVKEYPrivateKey, is generated for the ENVKEY.
3.) The ENVKEYPrivateKey is symmetrically encrypted with the EncryptionKey and associated with a new ENVKEY.
4.) The ENVKEYPublicKey is signed by the PrivateKey of the authenticated device and associated with the new ENVKEY.
5.) The TrustedRootMap is signed by the ENVKEYPrivateKey and associated with the new ENVKEY.
6.) The full TrustChain of signatures, from the currently authenticated PublicKey back to the TrustedRoot, is generated, signed by the ENVKEYPrivateKey, and associated with the new ENVKEY.
7.) Depending on the environment the ENVKEY is generated for, intermediate keys associated with any blobs the ENVKEY should have access to are encrypted for the ENVKEYPublicKey and associated with the ENVKEY.
8.) The ENVKEY is created on the EnvKey API host. Only the encrypted ENVKEYPrivateKey is sent to the host--the unencrypted ENVKEYPrivateKey is not stored anywhere.
9.) The full ENVKEY
is displayed to the user: ENVKEY=[IdPart]_[EncryptionKey]_[HostUrl*]
*The ENVKEY HostUrl is only included when the ENVKEY is generated in an org running a self-hosted EnvKey host.
Revoking an ENVKEY
When an ENVKEY is revoked, any intermediate blob keys that the ENVKEY had access to are deleted, and the associated blobs are marked for re-encryption.
Loading an ENVKEY
1.) To fetch an ENVKEY, the envkey-source client first makes a request to the EnvKey host that includes the IdPart portion of the ENVKEY. The host returns an ENVKEYEnvFetchResponse.
2.) The client symmetrically decrypts the encrypted ENVKEY PrivateKey from the response with the EncryptionKey portion of the ENVKEY.
3.) The client verifies the PublicKey from the response by encrypting a short constant for the PublicKey and signing the same constant with the decrypted PrivateKey, then decrypting with the PrivateKey and verifying with the PublicKey, and ensuring the decrypted/verified data matches the original constant.
4.) The SignedTrustedRoot is verified with the ENVKEY's PublicKey from the response.
5.) The SignedTrustChain is verified with the ENVKEY's PublicKey from the response.
6.) If any RootPubkeyReplacements are queued, these are verified and processed, setting a new TrustedRoot.
7.) The client decrypts the intermediate blob keys from the response with the decrypted PrivateKey.
8.)Before decrypting any intermediate blob key, the PublicKey used to encrypt it will have its signature chain verified by the web of trust back to the TrustedRoot.
9.) Once an intermediate blob key has been decrypted, the associated blob is symmetrically decrypted with the intermediate key.
10.) The full decrypted result is assembled and output to stdout.
Web Of Trust and Re-Encryption
Verifying Public Keys
1.) Before a PublicKey is used to encrypt or decrypt any data, its signature chain is first verified by the web of trust.
2.) First, the client generates the PublicKey's id with sha256(JSON.stringify(PublicKey))
.
3.) Then the client checks whether the id is set in SessionTrustedPubkeys, which is an ephemeral in-memory cache of the PublicKeys that have already been verified in the current session. If the PublicKey was already verified, it is trusted and is safe to use.
4.) The client checks whether the PublicKey is the TrustedRoot by generating the PublicKey's id (sha256(JSON.stringify(PublicKey))
), and checking whether that id is set in the TrustedRootMap. If the PublicKey is the TrustedRoot, then it is trusted and is safe to use.
5.) Otherwise, the client attempts to recursively verify the PublicKey's signature chain back to the TrustedRoot. If it can do so, the PublicKey is trusted and is safe to use. If not, an error is thrown and the encryption or decryption operation is halted.
Revoking Trusted Public Keys
1.) If the PublicKey being revoked was the last to encrypt any blobs, those blobs will need to be marked for re-encryption.
2.) If the PublicKey being revoked had previously signed other PublicKeys (when inviting a user, authorizing a device, generating a CLI key, generating a recovery key, or generating an ENVKEY), those PublicKeys will need to be re-signed by the device or CLI key carrying out the revocation.
3.) Any trust chains including the PublicKey being revoked, or that were signed by the PublicKey being revoked, will need to be regenerated and re-signed.
4.) If the PublicKey being revoked is the RootTrustedPubkey, the EnvKey host will queue a RootPubkeyReplacement for all devices, active invites or device grants, CLI keys, recovery keys, and ENVKEYs. Before performing any further operations, all EnvKey clients will first process the RootPubkeyReplacement, replacing the current TrustedRootPubkey with the device initiating removal.
Re-Encryption
1.) Whenever any device, CLI key, recovery key, or ENVKEY has is accessed removed from a blob, that blob will be marked for re-encryption.
2.) In most cases, re-encryption will be processed immediately by the user or CLI key who initiated the action leading to blobs to be marked for re-encryption, but in case of network failure or edge cases where that user or CLI key doesn't have sufficient access to re-encrypt all blobs that require it, another user or CLI key with sufficient access could also process the re-encryption.
Replacing the TrustedRoot
1.) Whenever an EnvKey client loads and decrypts data, whether it's an authenticated device, a CLI key, or an ENVKEY, the host can potentially return an array of RootPubkeyReplacements.
2.) Before decrypting any data, these must be processed so that the new TrustedRoot can be determined and verified.
3.) Each RootPubkeyReplacement includes a chain of signatures to the previous TrustedRoot, allowing it to be verified before it is processed.
4.) After all RootPubkeyReplacements are processed and the new TrustedRoot is determined, the client signs the new TrustedRoot with the currently authenticated PrivateKey, then sends the new SignedTrustedRoot to the EnvKey host for storage.
Updated over 2 years ago