Protocol Specification v0.6

Wire Format

An OIP (opens in a new tab) v0.6 frame is a canonical Protocol Buffers Packet containing one Core and signature bytes. Implementations validate the original byte sequence before a generated parser can discard unknown fields or normalize duplicate values. The schema provides field numbers; the canonical rules determine which encodings are valid.

Canonical packet

A packet is the Core bytes and a signature over prefix, length and those exact bytes; Core fields keep one order.

A packet is the Core bytes and a signature over prefix, length and those exact bytes; Core fields keep one order. Packet field 1 carries the Core bytes and field 2 the signature, which is exactly 64 bytes for SINGLE. The Core message is drawn as fields 1 to 25 in wire order in three rows: field 14, secondary, is the only optional field, and the body is one of fields 16 to 22. The signing input is the ASCII text OIP and ENVELOPE, each followed by a NUL byte, then the Core length in four big-endian bytes and the Core bytes unchanged. With SINGLE, Ed25519 signs these bytes and the result is field 2. Packet 1 1 bytes core the Core message below, as bytes 2 bytes signature SINGLE, 64 B field by field, in tag order Core 2 1 varint version 2 varint purpose 3 to 10 fixed64 eight handles and times 11 bytes uuid 12 varint family 13 varint primary 14 varint, optional secondary 3 15 varint signature_scheme 16 to 22 bytes body tag = 15 + family 4 23 varint atomicity 24 varint priority 25 fixed64 route_scope copied unchanged Signing input 5 4 bytes "OIP" 00 9 bytes "ENVELOPE" 00 4 bytes length of core the received bytes core Ed25519 signs these bytes into field 2
  1. 1

    Two fields

    A packet holds field 1, the Core bytes, then field 2, the signature, which is exactly 64 bytes for SINGLE. Nothing else may appear.

  2. 2

    One fixed order

    Fields appear once each, in tag order, with the wire type of the schema. Fields 3 to 10 are deployment, source_domain, target_domain, principal, key_id, authority_epoch, issued_seconds and ttl_seconds.

  3. 3

    One optional field

    Only secondary may be absent. Any other missing, repeated or out-of-order field is INVALID_FORMAT.

  4. 4

    Exactly one body

    The body is one Operation whose tag is 15 plus the family, so a query (family 4) arrives as field 19.

  5. 5

    Signed as received

    The signature covers the prefix, the four-byte big-endian length and the Core bytes exactly as received, never a re-encoding.

  • The Core bytes: received in field 1 and signed unchanged
  • The signature and the step that makes it
  • A field that may be absent
  • The Core message

Schematic: cell widths are not byte lengths.

Figure 1: Packet fields, Core field order and the envelope signing input

Version check and unknown fields#

NormativeBytes, test vectors and proofs

  • On a version 6 channel, a receiver MUST read the declared version of a frame within its size limit before canonical decoding of the rest of the frame. The declared version is the varint value of Core field 1 when the frame starts with Packet field 1 and the embedded Core starts with field 1.
  • A frame that declares a version other than 6 MUST receive UNSUPPORTED_VERSION, whatever fields follow. A frame that declares no version continues to the canonical checks. This check does not precede checks of the installed channel or host configuration.
  • The position and encoding of Packet field 1, Core field 1 and its varint form the version selector of the canonical protobuf format. The selector requires the shortest varints for the Packet tag, Core length, Core field-1 tag and unsigned 64-bit version, and requires the complete declared Core payload. It does not scan past a different first field. If any selector component is missing, truncated, overlong or out of range, the selector declares no version and canonical decoding decides the error. In particular, a truncated Core with a visible version prefix does not select that version. The reference version 5 compatibility path uses its configured JSON record decoder without this protobuf version selector; a malformed compatibility record receives INVALID_FORMAT.
  • Within a version, a receiver MUST reject an unknown field, an unknown wire type or an out-of-order field as INVALID_FORMAT. A new field can enter the protocol only through a new version that both peers negotiate.
  • After canonical decoding, an operation outside the selected version profile MUST receive UNSUPPORTED_VERSION before its source, signature or authority is checked. The selected version profile is the negotiated version together with the operations that the receiver's configuration accepts.
  • The reference configuration accepts 25 of the 31 operations, listed in the "Reference accepts" column of operations (opens in a new tab). Rust checks the configured operation set in the authority entry paths, including response generation and exact Debit resolution.
  • The generic Rust transport decoder has no configuration-specific operation set. Go exposes the configured check through ReceiveAccepting; its generic Receive path does not select that set.

Both reference implementations read the declared version from the first bytes of the frame and nothing else, so the answer stays the same when a later version adds fields.

Declared version checksrc/wire/codec.rsL335-L365Highlighted lines are discussed in the textSource (opens the lines on GitHub in a new tab)
/// The version that a received frame declares: the varint of core field 1/// when the frame starts with packet field 1 and the core it carries starts/// with field 1. Nothing else in the frame is read, so the answer does not/// depend on fields that a later version may add. A frame that does not start/// this way declares no version, and the canonical decoder reports it.pub fn declared_version(bytes: &[u8]) -> Option<u64> {    let mut input = bytes;    if take_varint(&mut input).ok()? != (1 << 3) | 2 {        return None;    }    let size = usize::try_from(take_varint(&mut input).ok()?).ok()?;    let mut core = input.get(..size)?;    if take_varint(&mut core).ok()? != 1 << 3 {        return None;    }    take_varint(&mut core).ok()}/// The version check that runs in front of the canonical decoder and the/// admission gate. A frame within the size limit that declares a version/// other than 6 receives UnsupportedVersion, whatever fields follow. Every/// other frame continues to the canonical checks unchanged.pub fn check_declared_version(bytes: &[u8], max_frame: usize) -> Result<()> {    if bytes.len() > max_frame {        return Err(BAD);    }    match declared_version(bytes) {        Some(version) if version != 6 => Err(WireError::UnsupportedVersion),        _ => Ok(()),    }}

Canonical signed message#

NormativeBytes, test vectors and proofs

  • The v0.6 envelope has a canonical protobuf core and signature bytes. The core binds version and purpose, deployment, source and target domain, authenticated principal, key and authority epoch (opens in a new tab), issued UTC seconds and delivery TTL (opens in a new tab), the full UUID, declared message family (opens in a new tab) and exactly one matching body.
  • Keep the seven established message family discriminators.
  • Each body is an Operation record with position-based operands determined by family and opcode. Operations (opens in a new tab) gives the position, type and meaning of every operand, and it is generated from the table operations.json (opens in a new tab).
  • A request carries its own caller-qualified sequence, session and cut and any exact origin, evidence, role or receipt correlation used by the product consumer. The full transport UUID and durable request and operation identifiers are separate.
  • Use ascending known field tags, shortest protobuf varints and lengths, exact wire types, a single encoding for each field, and an exact oneof and presence discipline at every nested record. Varints have at most 10 bytes. Field tags range from 1 to 2^29 - 1, and only wire types 0, 1 and 2 occur.
  • Validate original bytes before the generated parser can normalize duplicates, unknown fields or competing alternatives. The canonical parser preserves signature bytes and the original unknown secondary enum number and presence. Unknown primary values are rejected. An unknown secondary number does not authorize any product effect.
  • Deployment, source domain, target domain and principal are registered 64-bit handles, and so are the key identifier, authority epoch and route scope. A receiver compares each with its registrations and the negotiated channel. The core carries no address or chain name text.
  • Purpose MUST be 1 in this version. A frame with another purpose receives INVALID_SOURCE.
  • The transport UUID MUST be 16 bytes with the version 4 bits and the RFC 4122 variant bits: the high four bits of byte 6 are 4, and the high two bits of byte 8 are binary 10.
  • Issued seconds and finite appeal deadlines (opens in a new tab) are UTC seconds since the Unix epoch. Delivery TTL, durations and elapsed values are seconds. Amounts are unsigned integers in base units.
  • Preserve the absent deadline, the indefinite deadline and the finite deadline as separate forms on the wire (deadline modes (opens in a new tab)). In this version the product gives the absent and the indefinite forms the same meaning: no appeal deadline.
  • Strings use validated UTF-8 and canonical ordering by bytes for sets and maps, with sequence order preserved. Transport UUIDs are 16 bytes and unsigned 256-bit integers have one fixed 32-byte big-endian representation. Product bindings use checked range conversion before effects.
  • Reject malformed lengths and out-of-profile scope rather than truncating values. The maximum frame size and nesting depth are configuration values of the receiving host. A frame larger than the configured maximum MUST be refused as INVALID_FORMAT. Only complete canonical frames enter the validator.
  • The immutable origin signing input is ASCII OIP followed by NUL, ASCII ENVELOPE followed by NUL, four big-endian bytes of core length, and the canonical core. Derive it from the authoritative fields and sign the exact bytes.
  • Negotiation uses its own binary signing inputs, and the reference token, policy and source records use the signing inputs in JSON encodings (opens in a new tab). Verify the scheme and signature with the current authenticated source key and scope.
  • SINGLE (opens in a new tab) uses Ed25519 (opens in a new tab) (RFC 8032) with a 32-byte public key and a 64-byte signature. A verifier MUST reject a SINGLE signature unless the public key and the first 32 bytes of the signature (R) are canonical point encodings, with the y coordinate below 2^255 - 19, of points that are not of small order, the last 32 bytes (S) encode an integer below the group order, and the equation [S]B = R + [k]A holds without cofactor multiplication.
  • THRESHOLD (opens in a new tab) (scheme 2) has no verifier in this version. The reference has no THRESHOLD adapter and refuses every scheme 2 frame as INVALID_THRESHOLD_SIGNATURE, with no fallback. In transport admission (opens in a new tab) this refusal comes after the version, source and time checks. A THRESHOLD adapter MUST enforce exact current distinct authorized signers and a trusted quorum. With no such adapter in the reference, this requirement cannot be tested against it.

Field order on the wire#

The reference decoders check the order of the fields on the received bytes before a generated parser reads them. Field 14 is the only field that may be missing, and the tag of the body must be 15 plus the family.

Core field ordersrc/wire/codec.rsL166-L206Highlighted lines are discussed in the textSource (opens the lines on GitHub in a new tab)
fn validate_core_bytes(bytes: &[u8]) -> Result<()> {    let values = fields(bytes)?;    let [        Field::Varint(1, _),        Field::Varint(2, _),        Field::Fixed64(3),        Field::Fixed64(4),        Field::Fixed64(5),        Field::Fixed64(6),        Field::Fixed64(7),        Field::Fixed64(8),        Field::Fixed64(9),        Field::Fixed64(10),        Field::Bytes(11, _),        Field::Varint(12, family),        Field::Varint(13, _),        tail @ ..,    ] = values.as_slice()    else {        return Err(BAD);    };    let tail = if matches!(tail.first(), Some(Field::Varint(14, _))) {        &tail[1..]    } else {        tail    };    let [        Field::Varint(15, _),        Field::Bytes(tag, op),        Field::Varint(23, _),        Field::Varint(24, _),        Field::Fixed64(25),    ] = tail    else {        return Err(BAD);    };    if !(1..=7).contains(family) || u64::from(*tag) != 15 + family {        return Err(BAD);    }    validate_operation_bytes(op)}

Every varint must be the shortest encoding of its value, so each value has one byte form. Rust refuses a last byte of zero after a continuation byte, and Go compares the length read with the shortest length of the value.

Shortest varintssrc/wire/codec.rsL43-L60Highlighted lines are discussed in the textSource (opens the lines on GitHub in a new tab)
fn take_varint(input: &mut &[u8]) -> Result<u64> {    let mut value = 0;    for index in 0..10 {        let (byte, rest) = input.split_first().ok_or(BAD)?;        *input = rest;        if index == 9 && *byte > 1 {            return Err(BAD);        }        value |= u64::from(byte & 127) << (index * 7);        if byte & 128 == 0 {            if index > 0 && *byte == 0 {                return Err(BAD);            }            return Ok(value);        }    }    Err(BAD)}

Decoding and re-encoding#

After these checks, each decoder parses the packet with the generated code, encodes the result again and refuses the frame unless the two byte strings are equal.

Packet decodingsrc/wire/codec.rsL316-L333Highlighted lines are discussed in the textSource (opens the lines on GitHub in a new tab)
pub fn decode_packet(bytes: &[u8], max_frame: usize) -> Result<CanonicalPacket> {    if bytes.len() > max_frame {        return Err(BAD);    }    let values = fields(bytes)?;    let [Field::Bytes(1, core), Field::Bytes(2, _)] = values.as_slice() else {        return Err(BAD);    };    let checked_core = decode_core(core)?;    let packet = Packet::decode(bytes).map_err(|_| BAD)?;    if packet.core.as_ref() != Some(&checked_core) || encode_packet(&packet)? != bytes {        return Err(BAD);    }    Ok(CanonicalPacket {        packet,        core_bytes: core.to_vec(),    })}

Signing input and signature check#

Decoding stores the bytes of Packet field 1 as they arrived, and the packet builds its signing input from that stored copy.

Received Core bytessrc/wire/codec.rsL289-L314Highlighted lines are discussed in the textSource (opens the lines on GitHub in a new tab)
#[derive(Clone, Debug, PartialEq)]pub struct CanonicalPacket {    packet: Packet,    core_bytes: Vec<u8>,}impl CanonicalPacket {    pub fn core(&self) -> &Core {        self.packet.core.as_ref().expect("validated core")    }    pub fn signature(&self) -> &[u8] {        self.packet            .signature            .as_deref()            .expect("validated signature presence")    }    pub fn core_bytes(&self) -> &[u8] {        &self.core_bytes    }    pub fn encode(&self) -> Result<Vec<u8>> {        encode_packet(&self.packet)    }    pub fn signing_bytes(&self) -> Result<Vec<u8>> {        signing_bytes(&self.core_bytes)    }}

The signing input is the prefix, the four-byte big-endian Core length and those Core bytes.

Signing inputsrc/wire/codec.rsL367-L372Source (opens the lines on GitHub in a new tab)
pub fn signing_bytes(core: &[u8]) -> Result<Vec<u8>> {    let mut out = b"OIP\0ENVELOPE\0".to_vec();    out.extend(u32::try_from(core.len()).map_err(|_| BAD)?.to_be_bytes());    out.extend(core);    Ok(out)}

Admission first checks the version, the source fields and the issue and expiry times. A frame that passes them and names scheme 2 is refused as INVALID_THRESHOLD_SIGNATURE before any signature check. Any other frame needs a scheme of the negotiated profile and a valid signature over the signing input.

Frame admissionsrc/wire/admission.rsL87-L110Highlighted lines are discussed in the textSource (opens the lines on GitHub in a new tab)
pub fn admit_packet(    packet: &CanonicalPacket,    profile: &Profile,    boundary: &Boundary,) -> Result<()> {    check_scope(packet.core(), profile, boundary)?;    let scheme = packet        .core()        .signature_scheme        .ok_or(WireError::InvalidFormat)?;    if scheme == 2 {        return Err(WireError::InvalidThresholdSignature);    }    if !profile.schemes.contains(&scheme)        || !verify(            &boundary.public_key,            &packet.signing_bytes()?,            packet.signature(),        )    {        return Err(WireError::InvalidSignature);    }    Ok(())}

Both implementations apply the strict SINGLE rules above before they accept a signature.

Strict Ed25519 verificationsrc/wire/negotiation.rsL131-L151Highlighted lines are discussed in the textSource (opens the lines on GitHub in a new tab)
pub(crate) fn verify(key: &[u8; 32], bytes: &[u8], signature: &[u8]) -> bool {    fn canonical_point(point: &[u8]) -> bool {        let Some(mut y) = <[u8; 32]>::try_from(point).ok() else {            return false;        };        y[31] &= 127;        let mut prime = [255u8; 32];        prime[0] = 237;        prime[31] = 127;        y.iter().rev().cmp(prime.iter().rev()).is_lt()    }    if !canonical_point(key) || !signature.get(..32).is_some_and(canonical_point) {        return false;    }    VerifyingKey::from_bytes(key)        .and_then(|key| {            let sig = Signature::from_slice(signature)?;            key.verify_strict(bytes, &sig)        })        .is_ok()}

Protocol Buffers schema#

NormativeBytes, test vectors and proofs

This is the schema of the reference edition, shown from the public repository. Field presence is validated by the canonical decoder; optional in the schema is not permission to omit a required protocol value.

Protocol BuffersPacketproto/oip.protoL1-L13Source (opens the lines on GitHub in a new tab)
syntax = "proto2";package oip.v06;option go_package = "github.com/Oraclizer/oip-spec/go/wirepb";// The canonical OIP v0.6 wire schema. Receivers accept only the canonical// encoding of these messages and reject unknown fields; the encoding rules are// in spec/wire-conformance.md. New fields enter only through a new negotiated// version.message Packet {  optional Core core = 1;  optional bytes signature = 2;}
Protocol BuffersCoreproto/oip.protoL15-L44Highlighted lines are discussed in the textSource (opens the lines on GitHub in a new tab)
message Core {  optional uint64 version = 1;  optional uint64 purpose = 2;  optional fixed64 deployment = 3;  optional fixed64 source_domain = 4;  optional fixed64 target_domain = 5;  optional fixed64 principal = 6;  optional fixed64 key_id = 7;  optional fixed64 authority_epoch = 8;  optional fixed64 issued_seconds = 9;  optional fixed64 ttl_seconds = 10;  optional bytes uuid = 11;  optional uint64 family = 12;  optional uint64 primary = 13;  optional uint64 secondary = 14;  optional uint64 signature_scheme = 15;  // Exactly one member is present, and its tag is 15 plus the family.  oneof body {    Operation state_sync = 16;    Operation regulatory_action = 17;    Operation lock_management = 18;    Operation query = 19;    Operation ack = 20;    Operation heartbeat = 21;    Operation governance = 22;  }  optional uint64 atomicity = 23;  optional uint64 priority = 24;  optional fixed64 route_scope = 25;}
Protocol BuffersOperationproto/oip.protoL46-L64Highlighted lines are discussed in the textSource (opens the lines on GitHub in a new tab)
// An operation carries positional operands. The family of the enclosing body// member and the opcode fix the meaning and order of every entry in words and// blobs. The field-meaning table spec/operations.json lists, for each family// and opcode, the positions, enumerations and whether the reference// configuration accepts the operation.message Operation {  optional uint64 opcode = 1;  repeated fixed64 words = 2 [packed = false];  repeated bytes blobs = 3;  // The appeal deadline of a regulatory action (family 2). Operations of  // other families reject both members. In this version an absent member and  // appeal_deadline_indefinite both mean that no appeal deadline applies.  oneof appeal_deadline {    // Only the value true is canonical.    bool appeal_deadline_indefinite = 4;    // Unix time in UTC seconds.    fixed64 appeal_deadline_utc_seconds = 5;  }}

Two groups of bytes in OIP v0.6 are JSON text. The governance (opens in a new tab) blobs endpoint_contexts_json and session_bindings_json are wire bytes of the protocol core (opens in a new tab), and operations (opens in a new tab) gives their shapes. The reference profile also signs or hashes JSON text for its tokens, policy bindings, authorizations and source attestations.

Encoding rules#

NormativeBytes, test vectors and proofs

The JSON text of a value is its compact encoding under the rules below. A producer MUST emit exactly these bytes. A receiver of a governance blob MUST decode it, encode the decoded value again and reject the blob unless the two byte strings are equal.

  • The text is UTF-8 with no byte order mark and no whitespace outside strings.
  • A record is an object. Its keys are the field names in the declaration order of the record, and each key appears at most once.
  • An absent optional value appears as null. The exception is the reference configuration record and the endpoint grants it contains, which omit some fields when they are empty or absent, so that a configuration without them keeps its bytes.
  • An unsigned integer is a JSON number in decimal digits with no sign, leading zero, fraction or exponent. Every value up to 2^64 - 1 appears in full. A reader MUST NOT pass such a value through a floating-point type.
  • A fixed byte array, such as a 32-byte key or digest, is an array of numbers from 0 to 255 in byte order.
  • An enum value without fields is a string holding the variant name. A variant with fields is an object with one key, the variant name, whose value is the object of its fields.
  • A set is an array of its elements in ascending order without duplicates. Integers compare by value, tuples compare element by element, and enum values compare by the declaration order of their variants. A tuple is an array of its elements in order. A sequence keeps its order.
  • A map with integer keys is an object whose keys are the decimal strings of the keys in ascending numeric order. The dependency map of an endpoint context is instead an array of [key, value] pairs in ascending key order.
  • A string escapes the quotation mark, the reverse solidus and the control characters U+0000 to U+001F. The control characters use \b, \t, \n, \f and \r where those exist and \u00 followed by two lowercase hexadecimal digits otherwise. No other character is escaped.

Reference signing inputs#

NormativeBytes, test vectors and proofs

Each reference signing input is an ASCII domain string, one NUL byte, and the JSON text of the value. The signer signs these exact bytes with Ed25519. Where the table says digest, the reference binds the SHA-256 digest of the signing input instead of signing it. The reference verifies these signatures with strict Ed25519 verification and rejects small-order public keys.

In the reference, one function appends the JSON text to the domain bytes, and each caller passes its domain with the NUL byte already at the end, as in oip/reference-session/v1\0. Verification refuses small-order public keys.

RustSigning input and strict verificationsrc/authority/recovery/wire/ed25519.rsL403-L419Highlighted lines are discussed in the textSource (opens the lines on GitHub in a new tab)
fn preimage(domain: &[u8], fields: &impl Serialize) -> Result<Vec<u8>, WireError> {    let mut bytes = domain.to_vec();    bytes.extend(serde_json::to_vec(fields).map_err(|_| WireError::InvalidFormat)?);    Ok(bytes)}fn verifying_key(raw: &[u8; 32]) -> Option<VerifyingKey> {    VerifyingKey::from_bytes(raw)        .ok()        .filter(|key| !key.is_weak())}fn verify_strict(key: &[u8; 32], bytes: &[u8], signature: &[u8]) -> bool {    verifying_key(key)        .zip(Signature::from_slice(signature).ok())        .is_some_and(|(key, signature)| key.verify_strict(bytes, &signature).is_ok())}
DomainValueUse
oip/reference-session/v1Session claims: profile, epoch, identity, session, issuer, grants_sha256, issued_seconds, expires_secondsSession token signed by the configured session issuer
oip/reference-session-grants/v1The configured grants of the sessionDigest carried in grants_sha256
oip/reference-maintenance/v1Maintenance claims: role, profile, epoch, principal, issuer, issued_seconds, expires_secondsMaintenance token signed by the configured maintenance issuer
oip/reference-authority-policy/v1The authority configuration recordDigest bound into the retained image as the policy fingerprint
oip/reference-ordered-maintenance/v1Maintenance request: profile, principal, cut, selection, actionSigned ordered maintenance action
oip/reference-successor-ready/v1Tuple of recovery profile, current binding, next policy fingerprint and endpoint contextsReadiness signature of the next key before a handover is selected
oip/reference-handover-consent/v1Tuple of recovery profile, current binding, next binding, role, identity and handover proposalConsent of the current owner or of the successor
oip/reference-legal-order/v1Tuple of recovery profile, target, stage tag, call identifier, call payload and orderLegal authority signature of an order stage
oip/reference-enforcement-order/v1Tuple of recovery profile, target, call identifier, call payload and orderEnforcement authority signature of an enforcement spend
oip/reference-claim-recovery/v1Tuple of recovery profile, identity, session, session epoch, target, origin, call identifier and call payloadOwner endorsement of a recovery request
oip/source-terminal-record/v1Source statement: profile, origin_epoch, evidenceSource authority signature of its terminal decision
oip/source-terminal-quorum/v1Source statement: profile, origin_epoch, evidenceVote of a registered quorum signer

A source certificate is the JSON text of the record with statement and votes, with no domain prefix.

Fixed vectors#

NormativeBytes, test vectors and proofs

These vectors come from running the reference serializer on the values shown. Each JSON text decodes and encodes again to the same bytes.

An endpoint_contexts_json blob with one context: endpoint 2, epoch 9, grants of principal 7 on the normal route for Reserve and Debit, asset 17 depending on assets 17 and 18, and a write permission of principal 7 on asset 17 with value 1. The grants appear in the declaration order of the permission variants. The blob is 142 bytes.

Textendpoint_contexts_json, 142 bytesspec/json-encodings.mdL49-L50Source (opens the lines on GitHub in a new tab)
json: [{"endpoint":2,"epoch":9,"grants":[[7,"Normal","Reserve"],[7,"Normal","Debit"]],"dependencies":[[17,[17,18]]],"write_permissions":[[7,17,1]]}]hex: 5b7b22656e64706f696e74223a322c2265706f6368223a392c226772616e7473223a5b5b372c224e6f726d616c222c2252657365727665225d2c5b372c224e6f726d616c222c224465626974225d5d2c22646570656e64656e63696573223a5b5b31372c5b31372c31385d5d5d2c2277726974655f7065726d697373696f6e73223a5b5b372c31372c315d5d7d5d

A session_bindings_json blob binding session 5 to principal 7 at authority epoch 9. The blob is 41 bytes.

Textsession_bindings_json, 41 bytesspec/json-encodings.mdL56-L57Source (opens the lines on GitHub in a new tab)
json: [[5,{"principal":7,"authority_epoch":9}]]hex: 5b5b352c7b227072696e636970616c223a372c22617574686f726974795f65706f6368223a397d5d5d

For the signing inputs below, the input to sign is the domain bytes followed by the JSON text.

Maintenance claims with role ReferenceControl, profile deployment 7, format 1 and authority profile 3, epoch 2, principal 9, an issuer key whose first byte is 1 and last byte is 255 with zeros between, issued 1700000000 and expiring 1700000600. The signing input is 274 bytes.

TextMaintenance claims, 274-byte signing inputspec/json-encodings.mdL65-L67Source (opens the lines on GitHub in a new tab)
domain hex: 6f69702f7265666572656e63652d6d61696e74656e616e63652f763100json: {"role":"ReferenceControl","profile":{"deployment":7,"format":1,"authority_profile":3},"epoch":2,"principal":9,"issuer":[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255],"issued_seconds":1700000000,"expires_seconds":1700000600}input hex: 6f69702f7265666572656e63652d6d61696e74656e616e63652f7631007b22726f6c65223a225265666572656e6365436f6e74726f6c222c2270726f66696c65223a7b226465706c6f796d656e74223a372c22666f726d6174223a312c22617574686f726974795f70726f66696c65223a337d2c2265706f6368223a322c227072696e636970616c223a392c22697373756572223a5b312c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c3235355d2c226973737565645f7365636f6e6473223a313730303030303030302c22657870697265735f7365636f6e6473223a313730303030303630307d

Session claims with the same profile, epoch, issuer and validity window, identity principal 9, domain 4, OCID (opens in a new tab) 11 and RAI (opens in a new tab) 12, session 5, and a grants digest whose second byte is 16 with zeros elsewhere. The signing input is 382 bytes.

TextSession claims, 382-byte signing inputspec/json-encodings.mdL73-L75Source (opens the lines on GitHub in a new tab)
domain hex: 6f69702f7265666572656e63652d73657373696f6e2f763100json: {"profile":{"deployment":7,"format":1,"authority_profile":3},"epoch":2,"identity":{"principal":9,"domain":4,"ocid":11,"rai":12},"session":5,"issuer":[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255],"grants_sha256":[0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"issued_seconds":1700000000,"expires_seconds":1700000600}input hex: 6f69702f7265666572656e63652d73657373696f6e2f7631007b2270726f66696c65223a7b226465706c6f796d656e74223a372c22666f726d6174223a312c22617574686f726974795f70726f66696c65223a337d2c2265706f6368223a322c226964656e74697479223a7b227072696e636970616c223a392c22646f6d61696e223a342c226f636964223a31312c22726169223a31327d2c2273657373696f6e223a352c22697373756572223a5b312c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c3235355d2c226772616e74735f736861323536223a5b302c31362c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c226973737565645f7365636f6e6473223a313730303030303030302c22657870697265735f7365636f6e6473223a313730303030303630307d

Edition history#

  • 0.6.0: Added this edition. The v0.5 specification remains available at its original addresses.