跳转到内容

sigma_protocol_key_rotation - [devnet]

此内容尚不支持你的语言。

The key rotation NP relation (Rkeyrot\mathcal{R}_\mathsf{keyrot})

\def\old#1{{\color{red}{\dot{#1}}}}\def\new#1{{\color{teal}{\widetilde{#1}}}}

A ZKPoK of having rotated an encryption key to a new one and re-encrypted (part of) a Twisted ElGamal ciphertext.

Notation

  • \oldx\old{x} denotes a stale/old ciphertext component; \newx\new{x} denotes a fresh/new one.
  • \ell: number of available balance chunks.

The relation

\mathcal{R}_\mathsf{keyrot}^\ell\left(\begin{array}{l} H, \mathsf{ek}, \new{\mathsf{ek}}, \old{\mathbf{R}}, \new{\mathbf{R}} \textbf{;}\\ \mathsf{dk}, \delta, \delta_\mathsf{inv} \end{array}\right) = 1 \Leftrightarrow \left\{\begin{array}{r@{\,\,}l@{\quad}l} H &= \mathsf{dk} \cdot \mathsf{ek}\\ \new{\mathsf{ek}} &= \delta \cdot \mathsf{ek}\\ \mathsf{ek} &= \delta_\mathsf{inv} \cdot \new{\mathsf{ek}}\\ \new{R}_i &= \delta \cdot \old{R}_i, &\forall i \in [\ell]\\ \end{array}\right.

Homomorphism

This can be framed as a homomorphism check ψ(w)=f(X)\psi(\mathbf{w}) = f(\mathbf{X}) where w=(dk,δ,δinv)\mathbf{w} = (\mathsf{dk}, \delta, \delta_\mathsf{inv}) is the witness and X=(H,ek,\newek,\oldR,\newR)\mathbf{X} = (H, \mathsf{ek}, \new{\mathsf{ek}}, \old{\mathbf{R}}, \new{\mathbf{R}}) is the statement.

  1. The homomorphism ψ\psi is:
ψ(dk,δ,δinv)=(dkekδekδinv\newekδ\oldRi,i[])\psi(\mathsf{dk}, \delta, \delta_\mathsf{inv}) = \begin{pmatrix} \mathsf{dk} \cdot \mathsf{ek}\\ \delta \cdot \mathsf{ek}\\ \delta_\mathsf{inv} \cdot \new{\mathsf{ek}}\\ \delta \cdot \old{R}_i, &\forall i \in [\ell]\\ \end{pmatrix}
  1. The transformation function ff is:
f(X)=(H\newekek\newRi,i[])f(\mathbf{X}) = \begin{pmatrix} H\\ \new{\mathsf{ek}}\\ \mathsf{ek}\\ \new{R}_i, &\forall i \in [\ell]\\ \end{pmatrix}
use 0x1::bcs;
use 0x1::chain_id;
use 0x1::error;
use 0x1::fungible_asset;
use 0x1::object;
use 0x1::ristretto255;
use 0x1::sigma_protocol_fiat_shamir;
use 0x1::sigma_protocol_proof;
use 0x1::sigma_protocol_representation;
use 0x1::sigma_protocol_representation_vec;
use 0x1::sigma_protocol_statement;
use 0x1::sigma_protocol_statement_builder;
use 0x1::sigma_protocol_utils;
use 0x1::sigma_protocol_witness;
use 0x1::signer;
use 0x1::vector;

Constants

The homomorphism or transformation function implementation is not inserting points at the expected positions.

const E_STATEMENT_BUILDER_INCONSISTENCY: u64 = 6;

Index of dk\mathsf{dk} (old decryption key) in the witness’s scalars vector.

const IDX_DK: u64 = 0;

Index of ek\mathsf{ek} (old encryption key) in the statement’s points vector.

const IDX_EK: u64 = 1;

Index of HH in the statement’s points vector.

const IDX_H: u64 = 0;

Protocol ID used for domain separation

const PROTOCOL_ID: vector<u8> = [65, 112, 116, 111, 115, 67, 111, 110, 102, 105, 100, 101, 110, 116, 105, 97, 108, 65, 115, 115, 101, 116, 47, 75, 101, 121, 82, 111, 116, 97, 116, 105, 111, 110, 86, 49];

The key rotation proof was invalid

const E_INVALID_KEY_ROTATION_PROOF: u64 = 5;

Index of δ\delta in the witness’s scalars vector.

const IDX_DELTA: u64 = 1;

Index of δinv\delta_\mathsf{inv} in the witness’s scalars vector.

const IDX_DELTA_INV: u64 = 2;

Index of ek~\widetilde{\mathsf{ek}} (new encryption key) in the statement’s points vector.

const IDX_EK_NEW: u64 = 2;

The old R values (R˙i\dot{R}_i ) occupy indices 3 to 3 + (num_chunks - 1), inclusive.

Note: The new R values (R~i\widetilde{R}_i) occupy indices 3 + num_chunks to 3 + (2*num_chunks - 1), inclusive. A get_start_idx_for_new_R(num_chunks) function can be used to fetch the 3 + num_chunks starting index.

const START_IDX_OLD_R: u64 = 3;

Structs

KeyRotation

Phantom marker type for key rotation statements.

struct KeyRotation has drop
Fields
dummy_field: bool

KeyRotationSession

Used for domain separation

struct KeyRotationSession has drop
Fields
sender: address
token_type: object::Object<fungible_asset::Metadata>
num_chunks: u64

Functions

get_start_idx_for_new_R

Returns the starting index of new_R values.

fun get_start_idx_for_new_R(): u64
Implementation
inline fun get_start_idx_for_new_R(): u64 {
START_IDX_OLD_R + get_num_available_chunks()
}

assert_key_rotation_statement_is_well_formed

Ensures the statement is of the form: (H,ek,ek~,(R˙i)i[]),(R~i)i[])\left( H, \mathsf{ek}, \widetilde{\mathsf{ek}}, (\dot{R}_i)_{i \in [\ell]}), (\widetilde{R}_i)_{i \in [\ell]} \right)

fun assert_key_rotation_statement_is_well_formed(stmt: &sigma_protocol_statement::Statement<sigma_protocol_key_rotation::KeyRotation>)
Implementation
fun assert_key_rotation_statement_is_well_formed(
stmt: &Statement<KeyRotation>,
) {
assert!(stmt.get_points().length() == 3 + 2 * get_num_available_chunks(), e_wrong_num_points());
assert!(stmt.get_scalars().length() == 0, e_wrong_num_scalars());
}

new_session

public(friend) fun new_session(sender: &signer, token_type: object::Object<fungible_asset::Metadata>): sigma_protocol_key_rotation::KeyRotationSession
Implementation
public(friend) fun new_session(sender: &signer, token_type: Object<Metadata>): KeyRotationSession {
KeyRotationSession {
sender: signer::address_of(sender),
token_type,
num_chunks: get_num_available_chunks(),
}
}

new_key_rotation_statement

Creates a new key rotation statement. The order matches the NP relation: (H,ek,ek~,R˙,R~)(H, \mathsf{ek}, \widetilde{\mathsf{ek}}, \dot{\mathbf{R}}, \widetilde{\mathbf{R}}). Note that the # of chunks is inferred from the sizes of the old and new balance ciphertexts.

All points are decompressed internally from their compressed forms by the StatementBuilder.

@param compressed_ek: Compressed form of the old encryption key @param compressed_new_ek: Compressed form of the new encryption key @param compressed_old_R: Compressed forms of old_R (by reference; num_chunks elements) @param compressed_new_R: Compressed forms of new_R (by reference; num_chunks elements)

public(friend) fun new_key_rotation_statement(compressed_ek: ristretto255::CompressedRistretto, compressed_new_ek: ristretto255::CompressedRistretto, compressed_old_R: &vector<ristretto255::CompressedRistretto>, compressed_new_R: &vector<ristretto255::CompressedRistretto>): sigma_protocol_statement::Statement<sigma_protocol_key_rotation::KeyRotation>
Implementation
public(friend) fun new_key_rotation_statement(
compressed_ek: CompressedRistretto,
compressed_new_ek: CompressedRistretto,
compressed_old_R: &vector<CompressedRistretto>,
compressed_new_R: &vector<CompressedRistretto>,
): Statement<KeyRotation> {
let err = error::internal(E_STATEMENT_BUILDER_INCONSISTENCY);
let b = new_builder();
assert!(b.add_point(get_encryption_key_basepoint_compressed()) == IDX_H, err); // H
assert!(b.add_point(compressed_ek) == IDX_EK, err); // ek
assert!(b.add_point(compressed_new_ek) == IDX_EK_NEW, err); // new_ek
assert!(b.add_points(compressed_old_R) == START_IDX_OLD_R, err); // old_R
assert!(b.add_points(compressed_new_R) == START_IDX_OLD_R + get_num_available_chunks(), err); // new_R
let stmt = b.build();
assert_key_rotation_statement_is_well_formed(&stmt);
stmt
}

psi

The homomorphism ψ\psi for the key rotation relation.

Given witness (dk,δ,δinv)(dk, \delta, \delta_{inv}), outputs:

[
dk * ek, // should equal H
delta * ek, // should equal new_ek
delta_inv * new_ek, // should equal ek
delta * old_R_i, // should equal new_R_i, for i in [1..num_chunks]
]
fun psi(_stmt: &sigma_protocol_statement::Statement<sigma_protocol_key_rotation::KeyRotation>, w: &sigma_protocol_witness::Witness): sigma_protocol_representation_vec::RepresentationVec
Implementation
fun psi(_stmt: &Statement<KeyRotation>, w: &Witness): RepresentationVec {
// WARNING: Crucial for security
assert_key_rotation_statement_is_well_formed(_stmt);
// WARNING: Crucial for security
assert!(w.length() == 3, e_wrong_witness_len());
let dk = *w.get(IDX_DK);
let delta = *w.get(IDX_DELTA);
let delta_inv = *w.get(IDX_DELTA_INV);
// Build the representation vector
let reprs = vector[
// dk * ek
repr_scaled(IDX_EK, dk),
// delta * ek
repr_scaled(IDX_EK, delta),
// delta_inv * new_ek
repr_scaled(IDX_EK_NEW, delta_inv),
];
// delta * old_R_i for each chunk
let ell = get_num_available_chunks();
reprs.append(vector::range(0, ell).map(|i|
repr_scaled(START_IDX_OLD_R + i, delta)
));
// WARNING: Crucial for security
assert!(reprs.length() == 3 + ell, e_wrong_output_len());
new_representation_vec(reprs)
}

f

The transformation function ff for the key rotation relation.

Given the statement, outputs:

[
H,
new_ek,
ek,
new_R_i for i in [1..num_chunks]
]
fun f(_stmt: &sigma_protocol_statement::Statement<sigma_protocol_key_rotation::KeyRotation>): sigma_protocol_representation_vec::RepresentationVec
Implementation
fun f(_stmt: &Statement<KeyRotation>): RepresentationVec {
// WARNING: We do not re-assert the stmt is well-formed anymore here, since wherever the transformation function
// is called, so is the homomorphism, so the check will be done.
let ell = get_num_available_chunks();
let idx_r_new_start = get_start_idx_for_new_R();
let reprs = vector[
// H
repr_point(IDX_H),
// new_ek
repr_point(IDX_EK_NEW),
// ek
repr_point(IDX_EK),
];
// new_R_i for each chunk
reprs.append(vector::range(0, ell).map(|i|
repr_point(idx_r_new_start + i)
));
// Note: Not needed for security, since a mismatched f(X) length will be caught in the verifier. But good practice
// for catching mistakes *early* when implementing your f(X).
assert!(reprs.length() == 3 + ell, e_wrong_output_len());
new_representation_vec(reprs)
}

assert_verifies

Asserts that a key rotation proof verifies

public(friend) fun assert_verifies(self: &sigma_protocol_key_rotation::KeyRotationSession, stmt: &sigma_protocol_statement::Statement<sigma_protocol_key_rotation::KeyRotation>, proof: &sigma_protocol_proof::Proof)
Implementation
public(friend) fun assert_verifies(self: &KeyRotationSession, stmt: &Statement<KeyRotation>, proof: &Proof) {
assert_key_rotation_statement_is_well_formed(stmt);
let success = sigma_protocol::verify(
new_domain_separator(@aptos_framework, chain_id::get(), PROTOCOL_ID, bcs::to_bytes(self)),
|_X, w| psi(_X, w),
|_X| f(_X),
stmt,
proof
);
assert!(success, error::invalid_argument(E_INVALID_KEY_ROTATION_PROOF));
}