Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ARC-81] Partkey integrity hash #327

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions ARCs/arc-0081.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
arc: 81
title: Participation key integrity hash
description: Integrity checksum identifier for consensus participation keys
author: Tasos Bitsios (@tasosbit)
discussions-to: https://github.com/algorandfoundation/ARCs/issues/999
status: Draft
type: Standards Track
category: Core
created: 2024-12-12
---

## Abstract

This ARC proposes a new identifier for checking the integrity of a set of consensus participation keys

## Motivation

Participation key material can be inadvertently mutated between their generation on a participating node and the wallet where the key registration transaction is signed. Currently, the only way for a node runner to be diligent about signing key registration transactions is to individually verify 176 base64 characters across 3 key fields, as well as the 3 numeric fields for validity and key dilution.

An online keyreg transaction with incorrect partkey material can succeed, which would render the participating account silently delinquent: their registered participation keys would not correspond to the actual keypairs on the node.

The integrity hash aims to be a short, human readable identifier that hashes all relevant participation key material, which node runners can reference at a glance in order to verify the integrity of the participation keys between their node, wallet or later on an explorer.

## Specification
The key words "**MUST**", "**MUST NOT**", "**REQUIRED**", "**SHALL**", "**SHALL NOT**", "**SHOULD**", "**SHOULD NOT**", "**RECOMMENDED**", "**MAY**", and "**OPTIONAL**" in this document are to be interpreted as described in <a href="https://www.ietf.org/rfc/rfc2119.txt">RFC-2119</a>.

The integrity hash is a shortened version of a SHA512_256 hash of the concatenated part key material, represented in base32 encoding:

`base32_nopad(substr(sha512_256(concat_key_material), 0, 8))`

- `concat_key_material` uniquely identifies the participation key
- it is a concatenation of the following values:
- account address (raw encoding, length: 32 bytes)
- selection key (raw encoding, length: 32 bytes)
- vote key (raw encoding, length: 32 bytes)
- state proof key (raw encoding, length: 32 bytes)
- first valid voting round (encoded as uint64, length: 8 bytes)
- Note: this MUST NOT be omitted. When the first valid voting round is zero, an explicit uint64 zero value is expected.
- Implementation warning: this field can be missing in msgpack encoded keyreg transactions due to the `omitempty` behavior.
- last valid voting round (encoded as uint64, length: 8 bytes)
- key dilution (encoded as uint64, length: 8 bytes)
- its length MUST be exactly 184 bytes
- `sha512_256` refers to the SHA512_256 hashing function
- `substr(x, 0, 8)` refers to the "substring" function, i.e. returning the first 8 bytes of value `x`
- `base32_nopad` refers to the RFC 4648 base32 encoding function, without padding characters

## Rationale

- The key material to be hashed was chosen to uniquely identify the participation key
- SHA512_256 was chosen for its widespread availability alongside Algorand SDKs
- A truncated hash was chosen over using the full 32 bytes in order to improve the experience of comparing a hash exhaustively. In base32, 8 bytes encode to a 13-character string, vs. 32 bytes encoding to 52 characters.
- This choice was made while acknowledging that truncating the hash weakens its cryptographic strength significantly. This identifier is meant to be used as an error-detection checksum, not a cryptographically strong guarantee.

## Test Cases

Participation key:

- Account: OHQTAISSIGRGIGVN6TVJ6WYLBHFTUC437T4E2LRRXGWVNJ4GSZOXKPH7N4
- Selection Key: e4kBLu7zXOorjLVzJHOiAn+IhOBsYBCqqHKaJCiCdJs=
- Vote Key: WWHePYtNZ2T3sHkqdd/38EvoFWrnIKPrTo6xN/4T1l4=
- State Proof Key: 1GdNPOck+t6yXvuXxrDEPKqgi4I2sTaNugV1kd5ksUW2G1U6x1FT0WR3aT3ZSSmbYoDt3cVrB3vIPJA8GkqSYg==
- Vote First Valid: 3118965
- Vote Last Valid: 4104516
- Key Dilution: 993

Resulting integrity hash: JUKBSNRYTU4PU

## Reference Implementation

A reference implementation in Typescript is provided [here](../assets/arc-0081/src/index.ts).

## Security Considerations

The hash truncation involved in generating the integrity hash sacrifices cryptographic strength for ease of verification. It should not be considered a cryptographically strong identifier, but an error-detection checksum method.

## Copyright

Copyright and related rights waived via <a href="https://creativecommons.org/publicdomain/zero/1.0/">CCO</a>.
2 changes: 2 additions & 0 deletions assets/arc-0081/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dist/
node_modules/
20 changes: 20 additions & 0 deletions assets/arc-0081/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# ARC-81 Reference Implementation

"Participation key integrity hash" reference implementation in Typescript

Source under `src/index.ts`

## Setup & Build

```bash
npm install
npm run build
```

Built files will be in `dist/`

## Run example

```
npm run example
```
21 changes: 21 additions & 0 deletions assets/arc-0081/example/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { getPartKeyIntegrityHash } from "../dist/index.js";

const integrityHash = getPartKeyIntegrityHash({
account: "OHQTAISSIGRGIGVN6TVJ6WYLBHFTUC437T4E2LRRXGWVNJ4GSZOXKPH7N4",
selectionKeyB64: "e4kBLu7zXOorjLVzJHOiAn+IhOBsYBCqqHKaJCiCdJs=",
voteKeyB64: "WWHePYtNZ2T3sHkqdd/38EvoFWrnIKPrTo6xN/4T1l4=",
stateProofKeyB64: "1GdNPOck+t6yXvuXxrDEPKqgi4I2sTaNugV1kd5ksUW2G1U6x1FT0WR3aT3ZSSmbYoDt3cVrB3vIPJA8GkqSYg==",
voteFirstValid: 3118965,
voteLastValid: 4104516,
keyDilution: 993,
})

const expectedIntegrityHash = "JUKBSNRYTU4PU";

console.log("Integrity hash:", integrityHash, "\nExpecting:", expectedIntegrityHash)

if(integrityHash !== expectedIntegrityHash) {
throw new Error(`Error calculating integrity hash, expected: ${expectedIntegrityHash} received: ${integrityHash}`)
}

console.log("OK");
Loading
Loading