Logchains

Every member on a Krypton Team stores logs of their ssh accesses, git commits, and git tags. As an admin, being able to monitor your team members’ host accesses and git actions by viewing these audit logs can be very useful for understanding the security of your infrastructure.

Logs are timestamped and encrypted before being sent to a logging endpoint, such as krypt.co’s team server, for storage. Just as with the team sigchain, the logging infrastructure is end-to-end-verified: every logchain block is verified by each admin on the team.

In other words, teams and their members only trust krypt.co for liveness. krypt.co cannot read the contents of the logs or change the order of individual member logs.

Why should I care about this? If nothing else, this should give you and your team comfort that even if krypt.co is compromised one day, the privacy and integrity of your team data will be safe.

What’s in a Logchain

Each Krypton team member’s logs are stored as an independent signed hash chain, which we refer to as their logchain.

Logchains have the same structure as team sigchains with two main differences:

  1. Only a member can append to their own logchain
  2. A logchain may only be read by its corresponding member or the team admins

Whenever a new admin is promoted on the team, they are bootstrapped to be able to read any new logs each member encrypts. When an admin is demoted or removed, they lose the ability to decrypt new logs. This is critical as it prevents former admins from being able to read future log blocks.

The mechanism for log encryption and key rotation that enforces these rules is described in Wrapped Keys.

Contents

  1. Protocol Types
  2. Wrapped Keys
  3. Log Chain
  4. Log Operations

Protocol Extension

Below are the types used by logchains (note this is an extension to the main sigchain protocol types.

enum Body {
    //...extended
    Log(LogChain),
    // a Message can encode other Body types for non-sigchain and non-logchain purposes
}

enum LogChain {
    Create(GenesisLogBlock),
    Append(LogBlock),
}

// the first log block in a chain
struct GenesisLogBlock {
    team_pointer: TeamPointer,
    wrapped_keys: Vec<BoxedMessage>,
}

// subsequent log blocks in a chain
struct LogBlock {
    last_block_hash: Vec<u8>,
    operation: LogOperation,
}

// reference to a team
enum TeamPointer {
    PublicKey(Vec<u8>),
    LastBlockHash(Vec<u8>),
}

// wraps a symmetric key for an admin
struct BoxedMessage {
    recipient_public_key: Vec<u8>,
    sender_public_key: Vec<u8>,
    ciphertext: Vec<u8>, // box(..., JSON(PlaintextBody::LogEncryptionKey(symmetric_key)))
}

Log Chain

A LogChain block has two variants:

  • Create. A GenesisLogBlock starts every logchain and stores bootstrapping information. Namely, it contains a TeamPointer that identifies the member’s team by either team public key or main chain last block hash, and wrapped keys for all of the current admins. A logchain can be considered a side chain that is tied to the main chain by the GenesisLogBlock.

  • Append. Every LogBlock that follows the GenesisLogBlock encodes operations on the logchain. These operations are either encrypted logs of ssh accesses, git commits, and git tags, or key management operations that deal with adding a wrapped key for a new admin and rotating the symmetric key when admins are demoted/removed.

Wrapped Keys

In order to enforce admin-only read access to log blocks in a way that adapts to the promotion and demotion/removal of admins, we utilize both symmetric and public key encryption.

For every member, the creation of a log chain or demotion/removal of an admin causes the member to:

  1. Create a new symmetric key to encrypt and decrypt their logs.
  2. Encrypt this key under each current admin’s public key, forming what we refer to as wrapped keys

Each wrapped key can only be unwrapped by the admin it was wrapped for, meaning that only admins will be able to know the symmetric key and decrypt logs.

When a new admin is promoted on the team, each member’s current symmetric key is wrapped and made available on their logchain for the new admin. This grants the new admin read access to future log blocks and some older blocks going back to the last admin demotion/removal.

Log Operations

This section provides an overview of the different types of log operations and how they work.

enum LogOperation {
    EncryptLog(EncryptedLog),
    AddWrappedKeys(Vec<BoxedMessage>),
    RotateKey(Vec<BoxedMessage>),
}

Encrypting Logs

Whenever a member of a team ssh’s into a host or signs a git commit or tag, a Log is created. A Log contains a timestamp, the device information from the member’s Session, and the contents of the action being logged. The Session information gives admins insight into how many paired devices their members are using.

A Log includes the following type definitions:

struct EncryptedLog {
    ciphertext: [u8], //  symmetrically encrypted `Log`
}

struct Log {
    session: Session,
    unix_seconds: u64,
    body: LogBody,
}

struct Session {
    device_name: String,
    workstation_public_key_double_hash: Vec<u8>,
}

enum LogBody {
    Ssh(SSHSignature),
    GitTag(GitTagSignature),
    GitCommit(GitCommitSignature),
}

struct SSHSignature {
    user: String,
    host_authorization: Option<HostAuthorization>,
    session_data: Vec<u8>,
    result: SSHSignatureResult,
}

struct GitCommitSignature {
    tree: String,
    parents: Vec<String>,
    author: String,
    committer: String,
    message: Vec<u8>,
    message_string: Option<String>,
    result: GitSignatureResult,
}

struct GitTagSignature {
    object: String,
    type_: String,
    tag: String,
    tagger: String,
    message: Vec<u8>,
    message_string: Option<String>,
    result: GitSignatureResult,
}

Each Log is encrypted with the current symmetric key and added to the logchain.

Adding Wrapped Keys

Whenever a new admin is promoted on the team, they need to be able to able to read logs from their team’s members. To do so, each member:

  1. Creates BoxedMessages containing the current symmetric key encrypted under each new admin’s public key
  2. Adds an AddWrappedKey log block to their logchain containing the newly wrapped keys

This allows only the new admins access to the symmetric key, which will let them decrypt the member’s logs.

Rotating Keys

In the case that an admin loses admin status, we need to prevent them from reading any new logs. When a team member learns that an admin has been demoted or removed from the MainChain, they will:

  1. Generate a new symmetric key
  2. Create BoxedMessages that wrap the new key for each remaining admin
  3. Post a RotateKey log block containing the newly wrapped keys
  4. Encrypt future audit logs using the new symmetric key

When a remaining admin wants to read a new log, they first decrypt the log block that has the new symmetric key and continue as before.

The RotateKey operation changes the symmetric key, which means that new logs won’t be readable by former admins. Since logs are never re-encrypted, the former admin will in theory always be able to decrypt logs that were encrypted to them. However, krypt.co only serves logchain blocks to current team admins.