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;
}