Skip to content

[Bug] Reference Guardian SessionContext hash does not implement §8.2 construction #125

Description

@shivang-sec

What is broken

The reference Guardian's SessionContext hash construction does not match the normative hashing algorithm specified in Instrument Specification §8.2. It uses a custom JSON.stringify([...]) construction instead of the required RFC 8785 JCS construction with the specified predecessor bytes.

Where

reference-implementations/agt/packages/guardian/src/session-context.ts

What the specification says, and what happens instead

What the specification requires

Instrument Specification §8.2 defines the SessionContext chain hashing algorithm as:

entry_hash = lowercase-hex(SHA-256(content_bytes || prev_hash_bytes))

The specification requires:

  1. content_bytes is the UTF-8 encoding of the RFC 8785 (JCS) canonicalization of the ContextEntry object with entry_hash and previous_hash removed.
  2. prev_hash_bytes is the raw 32-byte decoding of previous_hash.
  3. For the first entry, prev_hash_bytes is the empty byte string.
  4. The resulting bytes are SHA-256 hashed and represented as lowercase hexadecimal.
  5. Section §8.2 explicitly states that alternative canonicalization schemes are not permitted in v0.1.

The specification also states in §8.1 that the storage representation is implementation-defined, but that the canonical form used for hashing is constrained by the specification.

What the reference Guardian does instead

The reference Guardian's hashEntry() implementation in:

reference-implementations/agt/packages/guardian/src/session-context.ts

currently constructs the hash input using:

JSON.stringify([
  input.prev_hash,
  input.session_id,
  input.seq,
  input.recorded_at,
  input.method,
  input.request_id,
  input.tool_name,
])

and then applies SHA-256.

The implementation also defines the first-entry predecessor as:

export const GENESIS_HASH = "0".repeat(64);

Therefore, the first entry incorporates a 64-character ASCII string containing zeroes rather than the empty prev_hash_bytes required by §8.2.

This is also not RFC 8785 JCS canonicalization of the ContextEntry object. It is a positional JSON array constructed from the implementation's internal SessionContext fields.

Live reproduction

A live reference Guardian run produced the following first SessionContext entry:

{
  "session_id": "44444444-4444-4444-8444-444444444444",
  "seq": 1,
  "prev_hash": "0000000000000000000000000000000000000000000000000000000000000000",
  "recorded_at": "2026-09-15T05:22:00.516Z",
  "method": "steps/toolCallRequest",
  "request_id": "33333333-3333-4333-8333-333333333333",
  "tool_name": "Bash",
  "hash": "dca6559ef53ce9ca189fa4c37122e8af4e4877d10df8645a68f148fc9ae97914"
}

Recomputing the hash using the reference Guardian's current hashEntry() algorithm, including the JSON.stringify([...]) construction and the 64 ASCII-zero prev_hash value, reproduces the recorded hash exactly:

dca6559ef53ce9ca189fa4c37122e8af4e4877d10df8645a68f148fc9ae97914

This confirms that the recorded SessionContext chain is being generated using the implementation's current custom construction.

However, that construction is different from the normative §8.2 construction.

Additional evidence

The existing SessionContext tests codify the current internal hash / prev_hash chain behavior. In particular, they assert that:

the first entry's prev_hash equals GENESIS_HASH;
subsequent entries use the previous entry's hash as prev_hash;
hashes are persisted using the internal hash / prev_hash representation.

This indicates that the observed behavior is part of the current reference implementation and is not merely an isolated malformed log entry.

Impact on implementers

A conformant implementation or verification tool following §8.2 cannot independently reproduce the reference Guardian's recorded SessionContext hashes from the log using the normative construction.

As a result, the reference Guardian's SessionContext chain is not interoperable with conformant chain-verification tooling. Independent implementations following §8.2 will derive different hashes from the same logical ContextEntry content, preventing reliable cross-implementation chain verification and audit comparison.

Because SessionContext hashing is intended to provide a portable chain commitment, this difference affects interoperability rather than only internal storage.

Current Priority Scope

Feeds the runnable Guardian reference implementation

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    status:needs-triageNot yet triaged. Applied by the issue formstype:bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions