Crypto Data Types
EnvKey data types and properties that are relevant to cryptography or security.
Core Crypto Types
PublicKey
A 'compound key' that includes both NaCl encryption and signing public keys. Will always have a signature so it can be verified by the web of trust chain of signatures back to the TrustedRoot
, unless it is the initial public key generated to create a new organization, in which case it is the TrustedRoot
up to the point it is later revoked (if that ever happens).
{
keys: {
signingKey: `nacl.sign` signing public key (ed22519, base64 encoded);
encryptionKey: `nacl.box` encryption public key (Curve25519-XSalsa20-Poly1305, base64 encoded);
};
signature?: `nacl.sign` signature of `JSON.stringify(keys property)` (base64 encoded)
}
PrivateKey
A 'compound key' that includes both NaCl encryption and signing private keys.
{
keys: {
signingKey: `nacl.sign` signing private key (ed22519, base64 encoded);
encryptionKey: `nacl.box` encryption private key (Curve25519-XSalsa20-Poly1305, base64 encoded);
};
}
EncryptedData
An object that stores NaCl-encrypted data alongside its unique nonce. Could be asymmetrically encrypted (using nacl.box
) or symmetrically encrypted (using nacl.secretbox
) with a securely-generated high-entropy key (not a human-generated passphrase--those need a salted KDF).
{
data: asymmetric (`nacl.box`) or symmetric (`nacl.secretbox`) encrypted data (base64 encoded);
nonce: 24 random bytes (generated with `nacl.randomBytes`, base64 encoded);
}
PassphraseEncryptedData
Similar EncryptedData
above, but only for symmetrically encrypted data (using nacl.secretbox
), and also includes a salt that is passed to a KDF (key derivation function) before encryption (sjcl.misc.pbkdf2
with 300k iterations).
{
data: symmetric (`nacl.secretbox`) passphrase-encrypted data (base64 encoded);
nonce: 24 random bytes (generated with `nacl.randomBytes`, base64 encoded);
salt: 24 random bytes (generated with `nacl.randomBytes`, base64 encoded), passed to `sjcl.misc.pbkdf2` key derivation function (300k iterations);
}
SignedData
{
data: `nacl.sign` signed data (base64 encoded);
}
Web Of Trust Types
DevicePublicKey
PublicKey
CliOrRecoveryKeyPublicKey
PublicKey
InvitePublicKey
PublicKey
ENVKEYPublicKey
PublicKey
SignedById
string (`sha256(JSON.stringify(PublicKey))`)
InvitedById
string (`sha256(JSON.stringify(PublicKey))`)
TrustedDevice
["orgUserDevice", DevicePublicKey, InvitePublicKey, InvitedById]
TrustedCliOrRecoveryKey
["cliUser" | "recoveryKey", CliOrRecoveryKeyPublicKey, SignedById]
TrustedInvite
["invite" | "deviceGrant", InvitePublicKey, InvitedById]
TrustedENVKEY
["generatedEnvkey", ENVKEYPublicKey, SignedById]
TrustedRoot
The root of trust for an Org
. Initially, will always be theOrgUserDevice
that created the org, but if that device is later revoked (by another OrgUserDevice
, a CliKey
, or a RecoveryKey
), the revoking public key will take over as the new root.
["root", TrustedDevice | TrustedCliOrRecoveryKey]
RootTrustMap
Only has a single entry, as an org can only have a single TrustedRoot
.
{
[TrustedRootId: string]: TrustedRoot;
}
TrustChain
Public keys indexed by id (sha256 hash of public key), representing chain of signatures for a particular public key that allows it to be verified back to the TrustedRoot.
{
[id: string]: TrustedDevice | TrustedCliOrRecoveryKey;
}
SignedTrustedRoot
SignedData - `nacl.sign(JSON.stringify(RootTrustMap))`
SignedTrustChain
SignedData - `nacl.sign(JSON.stringify(TrustChain))`
SessionTrusted
Ephemeral in-memory map of public keys that have already been verified this session. Indexed by id (sha256(JSON.stringify(Public Key)
).
{
[id: string]:
| TrustedRoot
| TrustedDevice
| TrustedCliOrRecoveryKey
| TrustedInvite
| TrustedENVKEY;
}
Application Types
Org
{
id: string;
name: string;
settings: OrgSettings;
}
OrgSettings
{
crypto: {
requiresPassphrase: boolean; // require passphrase for all devices in the org? default false
requiresLockout: boolean; // require lockout timer for all devices in the org? default false
lockoutMs: boolean; // minimum required lockout timer for all devices in the org
};
auth: {
inviteExpirationMs: number; // invite expiration time - default 24 hrs
deviceGrantExpirationMs: number; // device authorization expiration - default 24 hrs
tokenExpirationMs: number; // API authentication token expiration - default 4 weeks
};
};
OrgUser
{
id: string;
firstName: string;
lastName: string;
email: string;
orgRoleId: string;
}
OrgRole
{
id: string;
name: string;
permissions: OrgPermission[];
}
OrgPermission
You can find a list of all org permissions here.
OrgUserDevice
A human user's device.
{
id: string;
name: string;
userId: string;
pubkey: PublicKey;
pubkeyId: string; // `sha256(JSON.stringify(pubkey attribute))`
signedTrustedRoot: SignedTrustedRoot;
signedTrustedRootUpdatedAt: number;
approvedByType: "creator" | "invite" | "deviceGrant" | "recoveryKey";
approvedAt?: number;
inviteId?: string;
deviceGrantId?: string;
recoveryKeyId?: string;
isRoot?: boolean;
revokedRootAt?: number;
deactivatedAt?: number;
}
LocalDeviceSettings
{
defaultDeviceName: string;
requiresPassphrase: boolean;
requiresLockout: boolean;
lockoutMs: string;
locked: boolean;
}
ClientUserAuth
Client-side authentication and encryption data for a user device. Stored encrypted on-device in file system. 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).
{
privkey: PrivateKey;
token?: string; // API authentication token
hostUrl: string;
hostType: "cloud" | "self-hosted";
deviceId: string;
deviceName: string;
userId: string;
orgId: string;
orgName: string;
email: string;
externalAuthProviderId?: string; // when using SSO
provider: string; // when using SSO (otherwise "email")
uid: string; // when using SSO (otherwise equal to email)
}
ClientCliAuth
Client-side authentication data for a CLI key. Stored in-memory only.
{
userId: string;
orgId: string;
privkey: PrivateKey;
hostUrl: string;
hostType: "cloud" | "self-hosted";
}
Invite
Invitation for a human user to join the org.
{
id: string;
pubkey: PublicKey;
pubkeyId: string; // `sha256(JSON.stringify(pubkey attribute))`
encryptedPrivkey: EncryptedData;
identityHash: string;
signedTrustedRoot: string;
inviteeId: string;
invitedByUserId: string;
invitedByDeviceId?: string;
signedById: string;
acceptedAt?: number;
expiresAt: number;
}
DeviceGrant
Invitation to authorize an additional device for a human user who already belongs to the org.
{
id: string;
pubkey: PublicKey;
pubkeyId: string; //`sha256(JSON.stringify(pubkey attribute))`
encryptedPrivkey: EncryptedData;
identityHash: string;
signedTrustedRoot: string;
granteeId: string;
grantedByUserId: string;
grantedByDeviceId?: string;
signedById: string;
acceptedAt?: number;
expiresAt: number;
}
CliKey
{
id: string;
name: string;
orgRoleId: string;
creatorId: string;
creatorDeviceId: string;
pubkey: PublicKey;
pubkeyId: string; // `sha256(JSON.stringify(pubkey attribute))`)
encryptedPrivkey: EncryptedData;
signedTrustedRoot: string;
isRoot?: boolean;
revokedRootAt?: number;
deactivatedAt?: number;
}
RecoveryKey
{
id: string;
identityHash: string;
deviceId: string;
pubkey: PublicKey;
pubkeyId: string; // `sha256(JSON.stringify(pubkey attribute))`
encryptedPrivkey: EncryptedData;
signedTrustedRoot: string;
}
App
{
id: string;
name: string;
}
AppRole
{
id: string;
name: string;
permissions: AppPermission[]
}
AppPermission
You can find a list of all app permissions here.
AppUserGrant
{
id: string;
userId: string;
appId: string;
appRoleId: string;
}
EnvironmentRole
{
id: string;
name: string;
}
AppRoleEnvironmentRole
{
id: string;
environmentRoleId: string;
appRoleId: string;
permissions: EnvironmentPermission[]
}
EnvironmentPermission
You can find a list of all environment permissions here.
Environment
{
id: string;
environmentRoleId: string;
isBranch: boolean;
branchName?: string;
parentEnvironmentId?: string
}
EnvWithMetaCell
{
val?: string;
inheritsEnvironmentId?: string;
isEmpty?: boolean;
isUndefined?: boolean;
}
EnvMetaOnlyCell
{
inheritsEnvironmentId?: string;
isEmpty?: boolean;
isUndefined?: boolean;
}
EnvWithMeta
{
inherits: { [inheritsEnvironmentId: string]: string[] };
variables: { [inheritsEnvironmentId: string]: EnvMetaOnlyCell };
}
EnvMetaOnly
{
inherits: { [inheritsEnvironmentId: string]: string[] };
variables: { [inheritsEnvironmentId: string]: EnvWithMetaCell };
}
EnvInheritsOnly
{
inherits: { [inheritsEnvironmentId: string]: string[] };
}
Changeset
{
id: string;
actions: ReplayableEnvUpdateAction[];
message?: string;
createdAt: number;
encryptedById: string;
createdById: string;
}
ReplayableEnvUpdateAction
{
type:
| CREATE_ENTRY
| UPDATE_ENTRY
| REMOVE_ENTRY
| UPDATE_ENTRY_VAL
| REVERT_ENVIRONMENT
| IMPORT_ENVIRONMENT;
payload: {
diffs: rfc6902.Patch;
reverse: rfc6902.Patch;
revert?: number;
};
meta: {
envParentId: string;
environmentId: string;
entryKeys: string[];
};
}
KeyableEnv
{
[entryKey: string]: {
val?: string;
inheritsEnvironmentId?: string;
}
}
Server
{
id: string;
name: string;
appId: string;
environmentId: string;
}
Local Key
{
id: string;
name: string;
appId: string;
environmentId: string;
}
GeneratedENVKEY
{
id: string;
appId: string;
environmentId: string;
keyableParentId: string;
keyableParentType: "server" | "localKey";
encryptedPrivkey: EncryptedData;
envkeyIdPart: string;
envkeyIdPartHash: string;
signedTrustedRoot: SignedTrustedRoot;
signedById: string;
creatorId: string;
creatorDeviceId: string;
}
ENVKEY
ek[IdPart-EncryptionKeyPart-HostPart]
ENVKEY IdPart
22 character secure random alphanumeric
ENVKEY EncryptionKeyPart
22 character secure random alphanumeric
InviteIdentityHash
sha256(
JSON.stringify({
invitedBy: {
type: "orgUser" | "cliUser";
id: string;
pubkey: PublicKey;
email?: string;
};
invitee: {
email: string;
};
host: string;
encryptionKey: string;
})
);
InviteEncryptionToken
i[InviteIdentityHash][InviteEncryptionKey]
InviteEncryptionKey
22 character secure random alphanumeric
DeviceGrantIdentityHash
sha256(
JSON.stringify({
deviceGrantedBy: {
type: "orgUser" | "cliUser";
id: string;
pubkey: Crypto.Pubkey;
email: string | undefined;
};
grantee: {
email: string;
};
host: string;
encryptionKey: string;
})
);
DeviceGrantEncryptionToken
dg[InviteIdentityHash][InviteEncryptionKey]
DeviceGrantEncryptionKey
22 character secure random alphanumeric
Fetch Env Response Types
UserEnvFetchResponse
IntermediateKey
22 character secure random alphanumeric strings. Used to symmetrically encrypt blobs.
EncryptedIntermediateKey
Asymmetrically encrypted IntermediateKey. Each blob has an intermediate key associated with it. These are encrypted for all PublicKeys that have access to the blob.
{
data: EncryptedDataSchema;
encryptedById: string;
}
EncryptedBlob
Symmetrically encrypted by an IntermediateKey. There's one for each environment, each set of local overrides, and each changeset.
{
data: EncryptedDataSchema;
encryptedById: string;
}
{
envs: {
keys: {
[compositeId: string]: EncryptedIntermediateKey;
};
blobs: {
[compositeId: string]: {
data: EncryptedDataSchema;
encryptedById: string;
};
};
};
changesets: {
keys: {
[environmentId: string]: {
data: EncryptedDataSchema;
encryptedById: string;
};
};
blobs: {
[environmentId: string]: {
data: EncryptedDataSchema;
encryptedById: string;
}[];
};
};
}
ENVKEYEnvFetchResponse
{
orgId: string;
encryptedPrivkey: EncryptedData;
pubkey: PublicKey;
env?: KeyableBlobFields;
subEnv?: KeyableBlobFields;
locals?: KeyableBlobFields;
inheritanceOverrides?: { [environmentId: string]: KeyableBlobFields };
blocks: {
env?: KeyableBlobFields;
subEnv?: KeyableBlobFields;
locals?: KeyableBlobFields;
inheritanceOverrides?: { [environmentId: string]: KeyableBlobFields };
}[];
signedTrustedRoot: SignedTrustedRoot;
rootPubkeyReplacements?: RootPubkeyReplacement[];
}
KeyableBlobFields
{
encryptedKey: EncryptedData;
encryptedEnv: EncryptedData;
encryptedByPubkeyId: string;
encryptedByPubkey: PublicKey;
encryptedByTrustChain: SignedTrustChain;
}
RootPubkeyReplacement
{
id: string;
replacingPubkeyId: string;
replacingPubkey: PublicKey;
signedReplacingTrustChain: SignedTrustChain;
}
Updated over 2 years ago