Skip to content

Commit

Permalink
CIP-0022: Pool Operator Verification
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewWestberg committed Jun 25, 2021
1 parent 7913cf5 commit 24c6709
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 0 deletions.
134 changes: 134 additions & 0 deletions CIP-0022/CIP-0022.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
---
CIP: 22
Title: Pool operator verification
Authors: Andrew Westberg <[email protected]>, Martin Lang <[email protected]>, Ola Ahlman <[email protected]>
Comments-URI: no comments yet
Status: Draft
Type: Informational
Created: 2021-06-21
License: CC-BY-4.0
---

# Abstract

This proposal describes a method allowing a stakepool operator to provide credentials to verify that they are the rightful manager for their stakepool.

# Motivation

Many websites such pooltool.io, adapools.org, and others need to allow pool operators special access to modify the way their pool appears on the website. SPOCRA and other organizations also have a need to allow voting on proposals and ensure that each vote cast is from a valid pool operator. Today, these sites and organizations all use different techniques for validating pool operators.

pooltool.io - Validates operators by receiving 1 ada spent from the pool's registered rewards account

adapools.org - Validates operators by requesting that the operator include a special generated value in their extended pool metadata json file.

This proposal desires to simplify and streamline a single approach that all can reference in order to verify that a pool operator is who they say they are.

Current Tools that have implemented it already or have plans to implement it:
* [CNCLI](https://github.com/AndrewWestberg/cncli)
* [JorManager](https://bitbucket.org/muamw10/jormanager/)
* [StakePoolOperator Scripts](https://github.com/gitmachtl/scripts)
* [CNTools](https://cardano-community.github.io/guild-operators/#/Scripts/cntools)


# Specification

In order to achieve the goals of this CIP, the pool operator needs to provide some credential or credentials to the validating party which cannot be spoofed. The VRF pool keys and VRF signature algorithm implemented in libsodium are chosen to build and provide this credential/signature. This signature can then be validated and the operator verified without ever exposing any of the pool's private key information. This technique is very similar to verifying that a block produced by another pool is valid. The only difference is that instead of validating the slot seed for a given pool, we're validating a pre-determined message hash.

## Verification Steps:

1. Stakepool Operator (SPO) sends their pool_id to the Validating Server (VS)
2. VS checks the blockchain to retrieve the most recent block made by the pool. If the pool has made a block, the VRF public key is already known to the VS.
3. The VS sends a challenge request to the SPO.
1. VS creates a blake2b hash of "cip-0022{seedphrase}{random_nonce}"
2. If the pool has not yet made a block, VS also requires SPO to submit their public VRF key along with their challenge response.
4. The SPO signs the challenge with their VRF private key and sends this to VS as the challenge response.
5. The VS validates the VRF public key against the hash in the pool's registration to ensure it is properly registered.
6. The VS validates the signed challenge response


## Code Example (Validating server):

```
// Server side, create a challenge
val random = SecureRandom()
val seed = "pooltool.io"
val nonce = ByteArray(64)
random.nextBytes(nonce)
val message = "cip-0022${seed}".toByteArray() + nonce
val challenge = SodiumLibrary.cryptoBlake2bHash(message, null)
println("challenge: ${challenge.toHexString()}")
```

## Code Example (Pool Operator side):

```
// Node operational VRF-Verification-Key: pool.vrf.vkey
//{
// "type": "VrfVerificationKey_PraosVRF",
// "description": "VRF Verification Key",
// "cborHex": "5820e0ff2371508ac339431b50af7d69cde0f120d952bb876806d3136f9a7fda4381"
//}
//
// Node operational VRF-Signing-Key: pool.vrf.skey
//{
// "type": "VrfSigningKey_PraosVRF",
// "description": "VRF Signing Key",
// "cborHex": "5840adb9c97bec60189aa90d01d113e3ef405f03477d82a94f81da926c90cd46a374e0ff2371508ac339431b50af7d69cde0f120d952bb876806d3136f9a7fda4381"
//}
// We assume the pool operator has access to the pool's vrf secret key
val skeyCbor = "5840adb9c97bec60189aa90d01d113e3ef405f03477d82a94f81da926c90cd46a374e0ff2371508ac339431b50af7d69cde0f120d952bb876806d3136f9a7fda4381".hexToByteArray()
val vrfSkey = (CborReader.createFromByteArray(skeyCbor).readDataItem() as CborByteString).byteArrayValue()
val vkeyCbor = "5820e0ff2371508ac339431b50af7d69cde0f120d952bb876806d3136f9a7fda4381".hexToByteArray()
val vrfVkey = (CborReader.createFromByteArray(vkeyCbor).readDataItem() as CborByteString).byteArrayValue()
// Client side, sign the challenge
val signature = SodiumLibrary.cryptoVrfProve(vrfSkey, challenge)
println("signature: ${signature.toHexString()}")
```

## Code Example (Validating server):

```
// Server side, verify based on only knowing the pool_id, public vkey, signature, and challenge
// Get the vkeyHash for a pool from the "query pool-params" cardano-cli command
// This comes from the pool's registration certificate on the chain.
val vkeyHash = "f58bf0111f8e9b233c2dcbb72b5ad400330cf260c6fb556eb30cefd387e5364c".hexToByteArray()
// Verify that the vkey from the latest minted block on the blockchain (or the client supplied if they
// haven't yet minted a block) is the same as the one on-chain in the pool's registration certificate
val vkeyHashVerify = SodiumLibrary.cryptoBlake2bHash(vrfVkey, null)
assertThat(vkeyHash).isEqualTo(vkeyHashVerify)
// Verify that the signature is a valid format. This will fail if the signature is mal-formed
val signatureHash = SodiumLibrary.cryptoVrfProofToHash(signature)
println("signatureHash: ${signatureHash.toHexString()}")
// Verify that the signature matches
val verification = SodiumLibrary.cryptoVrfVerify(vrfVkey, signature, challenge)
println("verification: ${verification.toHexString()}")
assertThat(signatureHash).isEqualTo(verification)
println("Verification SUCCESS!")
```

## Code Example output:

```
vrfSkey: adb9c97bec60189aa90d01d113e3ef405f03477d82a94f81da926c90cd46a374e0ff2371508ac339431b50af7d69cde0f120d952bb876806d3136f9a7fda4381
vrfVkey: e0ff2371508ac339431b50af7d69cde0f120d952bb876806d3136f9a7fda4381
challenge: 2bbbafe8edd7ffc6ea7b9c73da2aa72f12091676df3d9f2da7b9d5a7fff7c26a
signature: e51f3236e06bb10dc44619c38c12a2f99d47b1653fe1bd431bfcd86ca9dd4fce058f9eb82b1defbf5ede066ecada550c9050c72d61fa78adc7091b10459374aff67114917ca64f1be611fabaaf6eef02
signatureHash: 93ac3a97236d4a041e0c264175d93134bea1d9ba2dbb34fabd3177b3284decd7eaaca7f29a0c5da7409455d492c2f734d3187485242b73278f0aa9211b053663
verification: 93ac3a97236d4a041e0c264175d93134bea1d9ba2dbb34fabd3177b3284decd7eaaca7f29a0c5da7409455d492c2f734d3187485242b73278f0aa9211b053663
Verification SUCCESS!
```

# Rationale

Implementing this simplifies and commonizes the process for verifying that a pool operator is who they say they are in 3rd party systems. Having a common way of verify pool operators also allows simple integration into pool management tools. There is also some overlap with [CIP-006](https://github.com/cardano-foundation/CIPs/blob/master/CIP-0006/CIP-0006.md#extended-metadata---flexible-but-validable) and the `rawdata-sign` command although it chooses to generate a new key instead of utilizing the pool's existing vrf.skey to sign like this proposal.

# Copyright

This CIP is licensed under [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/legalcode)
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ The current process is described in details in [CIP1 - "CIP Process"](https://gi
| 15 | [Catalyst Registration Transaction](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0015) | Draft |
| 16 | [Cryptographic Key Serialisation Formats](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0016) | Draft |
| 19 | [Cardano Addresses](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0019) | Draft |
| 22 | [Pool Operator Verification](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0022) | Draft |
| 1852 | [HD Wallets for Cardano](https://github.com/cardano-foundation/CIPs/tree/master/CIP-1852) | Draft |
| 1853 | [HD Stake Pool Cold Keys](https://github.com/cardano-foundation/CIPs/tree/master/CIP-1853) | Draft |
| 1854 | [Multi-signatures HD Wallets](https://github.com/cardano-foundation/CIPs/tree/master/CIP-1854) | Draft |
Expand Down

0 comments on commit 24c6709

Please sign in to comment.