Skip to content

Add shielded access control #88

@andrew-fleming

Description

@andrew-fleming

Shielded Access Control Proposal

I aim to outline a Shielded Access Control module that would enable developers to define role-based access control mechanisms without publicly revealing the role assignments of accounts.

Challenges

We can’t enforce the HKDF nonce derivation, since it’s computed and shared off-chain. Without circuit verification in Compact, users can use any nonce, as long as it’s unique for the PK-role pair to avoid commitment collisions. The HKDF method is a secure recommendation for randomness and privacy, but enforcing it requires on-chain HKDF verification, which isn’t supported. Users should follow this approach for best security. To address this challenge we should provide a HKDF nonce derivation implementation.

Contract Outline

Ledger

// MerkleTree with 2^4 - 1 leaves
// Each leaf equals H(PK, role, nonce)
export ledger _roles: MerkleTree<4, Bytes<32>;

export ledger DEFAULT_ADMIN_ROLE: Bytes<32>;

Private State

// Generic role names to be modified by implementation
type RoleNames = "admin" | "minter" | "burner";

type RoleData = {
  [role in RoleNames]: {
    merkleTreeIndex: bigint; // Not sure about this one
    nonce: Uint8Array;
  };
};

type ShieldedAccessControlPrivateState = {
  readonly secretKey: Uint8Array;
  roles: RoleData
};

External Circuits

export circuit getAdminRole(role: Bytes<32>)


export circuit grantRole(pk: Bytes<32>, role: Bytes<32>, nonce: Bytes<16>): Bytes<32> {
    commitment = hashUserRole(PK || role || nonce)         
    Call _addRole(commitment)                        
    Return commitment
}

export circuit revokeRole(pk: Bytes<32>, role: Bytes<32>, nonce: Bytes<16>): [] {
    commitment = hashUserRole(PK || role || nonce)         
    Call _revokeRole(commitment)                        
    Return commitment
}

export circuit renounceRole(role: Bytes<32>, callerConfirmation: Either<ZswapCoinPublicKey, ContractAddress>): [] {
  assert callerConfirmation == left<ZswapCoinPublicKey,ContractAddress>(own_public_key()) "ShieldedAccessControl: bad confirmation";
  _revokeRole(role);
}

export circuit hasRole(role: Bytes<32>, account: Either<ZswapCoinPublicKey, ContractAddress>, nonce: Bytes<16>): Boolean

export circuit assertOnlyRole(role: Bytes<32>): [] {
}

/**
* @description creates public hash of secret key 
export circuit publicKey(sk: Bytes<32>, nonce: Bytes<16>): Bytes<32> {
 return persistent_hash<Vector<3, Bytes<32>>>([pad(32, "shieldedAccessControl:pk:"), pad(32, nonce) , sk]);
}

Internal Circuits

_grantRole(merkleTreeCommitment: Bytes<32>): Boolean
_revokeRole(merkleTreeCommitment: Bytes<32>): Boolean

Witnesses

/**
* @description Provide proof of knowledge of `role` - H(PK, role, nonce) - in MerkleTree
* Since nonce is only known locally and to the admin it's not possible 
*/ 
witness assertOnlyRole(role: Bytes<32>): MerkleTreePath<10, Bytes<32>>;

/**
* @description Getter for secret key
*/ 
witness localSecretKey();

Off-chain Functions

function HKDF(keyingMaterial, info, len)
function roleRequest(pubkey, role, nonce)

Metadata

Metadata

Assignees

Labels

enhancementNew feature or requestwork-in-progressPull requests which are still being worked on, more changes will follow.

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions