Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
OVIP: 3
Title: VASP Identity
Author: David Riegelnig <david.riegelnig@bitcoinsuisse.com>
Discussions-To: https://community.openvasp.org/#narrow/stream/21-protocol-.2F.20ovip
Status: Accepted
Type: Standard
Created: 2020-05-08

Abstract

The OpenVASP protocol uses Ethereum smart contracts as its public key infrastructure.

Each Virtual Assets Service Provider (VASP) deploys its own VASP Contract that represents the identity of the VASP and that stores the public keys required by the protocol.

VASP Contracts are deployed via a universal smart contract factory, the Index Contract, which maintains a register of all deployed VASP Contracts.

When a VASP Contract is deployed, the VASP can specify a unique VASP Code as an identifier, which can henceforth be used to retrieve the VASP Contract from the Index Contract.

This OVIP specifies the interface of the VASP Contract and the Index Contract, which together set the rules how a VASP can establish its identity within the OpenVASP protocol. It is a revision of the initial specification proposed in the OpenVASP White Paper.

Specification

Public Key Format

The signingKey, messageKey and the transportKey must be stored in compressed form. That is it must be represented with exactly 33 bytes and is prefixed with '02' or '03'. This format must be enforced by the smart contract, any call changing the key to a byte array in invalid format must fail.

VASP Code

The VASP Code consists of 8 hexadecimal characters and is a unique identifier for the VASP Contract representing the VASP's identity.

It can be freely chosen by the VASP out of all possible combinations still available when the VASP Contract is deployed (see sections below).

VASP Identifier

The Index Contract implements the Addressing Layer as described in ovip-0009. There can be multiple implementations of this layer and even multiple istances of the Index Contract.

To ensure global uniqueness, the VASP Code Type specifies for which implementation of the Addressing Layer the VASP Code is registered (see ovip-0002).

The combination of the VASP Code Type and the VASP Code globally, uniquely identifies a VASP and is referred to as the VASP Identifier.

Index Contract

The Index Contract serves as a universal smart contract factory and is deployed only once by the OpenVASP Association. The owner of the contract merely has the ability to halt or terminate the contract in emergency situations or if required by protocol upgrades.

Functions

createVASPContract

function createVASPContract
(
    bytes4 vaspCode,
    address owner,
    bytes4 channels,
    bytes calldata transportKey,
    bytes calldata messageKey,
    bytes calldata signingKey
)
    external
    returns (address)

Description:
Deploys a VASP Contract, owned by owner and returns the address of the deployed contract. The Index Contract records the deployed contract's address under the specified vaspCode in its internal mapping. The channels, transportKey, messageKey and signingKey respectively are set on deployment of the VASP contract.

Conditions:
owner must be a valid Ethereum address; and
vaspCode must be unique in the internal mapping of the Index Contract, that is, vaspCode must have never been used in a successful call of this function before. Public Key Format must be enforced for transportKey, messageKey and signingKey.

Use Case:
Called by a VASP to deploy a VASP Contract that represents the identity of the VASP within the OpenVASP protocol and that is mapped to a unique VASP Code chosen by the VASP.

Event log:

VASPContractCreated(bytes4 indexed vaspCode, address indexed vaspAddress)
ChannelsChanged(bytes4 indexed vaspCode, bytes4 previousChannels, bytes4 newChannels);
TransportKeyChanged(bytes4 indexed vaspCode, bytes previousTransportKey, bytes newTransportKey);
MessageKeyChanged(bytes4 indexed vaspCode, bytes previousMessageKey, bytes newMessageKey);
SigningKeyChanged(bytes4 indexed vaspCode, bytes previousSigningKey, bytes newSigningKey);

getVASPAddressByCode

function getVASPAddressByCode
(
    bytes4 vaspCode
)
    external view
    returns (address)

Description:
Returns the address of the VASP Contract which has been recorded under the specified vaspCode. If no entry is found in the internal mapping, an empty address (0x) is returned by the function.

Use Case:
Called to get the VASP Contract which is uniquely assigned to a specific VASP Code. Intended method to retrieve a VASP Contract.

getVASPCodeByAddress

function getVASPCodeByAddress
(
    address vaspAddress
)
    external view
    returns (bytes4)

Description:
Returns the VASP Code that was assigned to the specified vaspAddress by the Index Contract. If no entry is found in the internal mapping, an empty address (0x) is returned by the function.

Use Case:
Called to get the VASP Code which is uniquely mapped to a given VASP Contract address.

pause

function pause()
    external

Description:
Disables createVASPContract functionality for all callers and arguments.

Access Can be called only by the owner

Use Case:
Pauses the lifecycle of the contract while preserving the current mapping and allowing queries towards existing entries.

unpause

function unpause()
    external

Description:
Re-enables createVASPContract functionality for all callers and arguments.

Access Can be called only by the owner

Use Case:
Resumes the lifecycle of the contract.

terminate

function terminate
(
    address payable recipient
)
    external

Description:
Frees memory allocated by the contract and transfers all potentially accidentally funds send to the contract to recipient.

Use Case:
End the lifecycle of the VASP Index without preserving the existing mapping. Invalidates and obsoletes existing entries.

transferOwnerRole

function transferOwnerRole
(
    address newOwnerCandidate
)
    external

Description:
Sets the specified address as a contract's owner candidate.

Access Can be called only by the owner

Use Case:
Called by the contract owner to prepare the transfer of the contract's ownership to a new owner.

Event log:

OwnerRoleTransferStarted(address indexed currentOwner, address indexed newOwnerCandidate);

cancelOwnerRoleTransfer

function cancelOwnerRoleTransfer()
    external

Description:
Cancels ownership transfer.

Access Can be called only by the owner

Use Case:
Called by the contract owner to cancel the transfer of the contract's ownership to a new owner.

Event log:

OwnerRoleTransferCancelled();

acceptOwnerRole

function acceptOwnerRole()
    external

Description:
Changes the owner of the contract.

Access Can be called only by the new owner candidate

Use Case:
Called by the future owner in order to accept transfer of the contract's ownership.

Event log:

OwnerRoleTransferCompleted(address indexed previousOwner, address indexed newOwner);

renounceOwnerRole

function renounceOwnerRole() 
    external

Description:
Renounces the owner role

Access Can be called only by the owner

Use Case:
Called by the future owner in order to accept transfer of the contract's ownership.

Use Case: Can be called to leave the contract without owner. It will not be possible to call functions callable only by the owner anymore.

VASP Contract

Each VASP participating in the OpenVASP protocol deploys and owns a dedicated VASP Contract, representing the VASP's identity.

The VASP Contract is used to provide the public keys required to decrypt and verify protocol messages that were signed and sent by the VASP.

VASP Contracts can only be deployed together with a unique VASP Code and via the Index Contract, which keeps track of all deployed VASP Contracts and their respective VASP Code. Each VASP Contract can always be retrieved from the Index Contract by the associated VASP Code.

The owner of the VASP Contract can change the public keys and can transfer ownership.

Functions

channels

function channels()
    external view
    returns (bytes4)

Description:
Returns the value of the channels.

signingKey

function signingKey()
    external view
    returns (bytes)

Description:
Returns the value of the signing key.

messageKey

function messageKey()
    external view
    returns (bytes)

Description:
Returns the value of the message key.

transportKey

function transportKey()
    external view
    returns (bytes)

Description:
Returns the value of the transport key.

setChannels

setChannels
(
    bytes4 newChannels
)
    external

Description:
Sets the contract's channels.

Access:
Can be called only by the owner.

Use Case:
Owner changes the communication channels the VASP is accepting for OpenVASP messages. channels is to be interpreted as a bit mask, where each bit indicates a specific channel. At this time, the following channels are available:

Bit Mask Channel
00000000 000000000 00000000 00000001 Ethereum Whisper as specified per ovip-0010

Event log:

ChannelsChanged(bytes8 indexed vaspCode, bytes4 previousChannels, bytes4 newChannels);
TransportKeyChanged(bytes8 indexed vaspCode, string previousTransportKey, string newTransportKey);
MessageKeyChanged(bytes8 indexed vaspCode, string previousMessageKey, string newMessageKey);
SigningKeyChanged(bytes8 indexed vaspCode, string previousSigningKey, string newSigningKey);

setTransportKey

function setTransportKey
(
    bytes calldata newTransportKey
)
    external

Description:
Changes the contract's transport key. Public Key Format must be enforced for newTransportKey.

Access:
Can be called only by the owner.

Use Case:
Owner changes the public key required to secure communication on the transport layer (e.g. encryption of Whisper envelopes).

Event log:

TransportKeyChanged(bytes4 indexed vaspCode, bytes previousTransportKey, bytes newTransportKey);

setMessageKey

function setMessageKey
(
    bytes calldata newMessageKey
)
    external

Description:
Changes the contract's message key. Public Key Format must be enforced for newMessageKey.

Access:
Can be called only by the owner.

Use Case:
Owner changes the public key required to encrypt OpenVASP messages (session layer).

Event log:

MessageKeyChanged(bytes4 indexed vaspCode, bytes previousMessageKey, bytes newMessageKey);

setSigningKey

function setSigningKey
(
    bytes calldata newSigningKey
)
    external

Description:
Changes the contract's signing key. Public Key Format must be enforced for newSigningKey.

Access:
Can be called only by the owner.

Use Case:
Owner changes the public key required to verify message signatures.

Event log:

SigningChanged(bytes4 indexed vaspCode, string previousMessageKey, string newMessageKey);

transferOwnerRole

function transferOwnerRole
(
    address newOwnerCandidate
)
    external

Description:
Sets the specified address as a contract's owner candidate.

Access Can be called only by the owner

Use Case:
Called by the contract owner to prepare the transfer of the contract's ownership to a new owner.

Event log:

OwnerRoleTransferStarted(address indexed currentOwner, address indexed newOwnerCandidate);

cancelOwnerRoleTransfer

function cancelOwnerRoleTransfer()
    external

Description:
Cancels ownership transfer.

Access Can be called only by the owner

Use Case:
Called by the contract owner to cancel the transfer of the contract's ownership to a new owner.

Event log:

OwnerRoleTransferCancelled();

acceptOwnerRole

function acceptOwnerRole()
    external

Description:
Changes the owner of the contract.

Access Can be called only by the new owner candidate

Use Case:
Called by the future owner in order to accept transfer of the contract's ownership.

Event log:

OwnerRoleTransferCompleted(address indexed previousOwner, address indexed newOwner);

renounceOwnerRole

function renounceOwnerRole() 
    external

Description:
Renounces the owner role

Access Can be called only by the owner

Use Case:
Called by the future owner in order to accept transfer of the contract's ownership.

Use Case: Can be called to leave the contract without owner. It will not be possible to call functions callable only by the owner anymore.

Migration Blueprint

Predicting the future is fairly difficult. However we must assume that a situation arises which demands a change to the VASP Index in one form or another. We see two scenarios. One where the VASP Contracts become obsolete and another where they could be reused by the new Index contract. In both cases we would like to be able to reserve previously reserved VASP Codes. This is not a technically required feature as the VASP Code Type of the VASP Identifier will change in any case but will provide more clarity. The blueprint for achieving this would be to have a new Index contract call the exisiting one to disallow reusage of existing VASP Codes unless the caller is the owner of the VASP Contract which is mapped to the VASP Code in the obsolete VASP Index. If possible it would then also be easy to point the new index entry towards the existing VASP contract, reducing redundancy and gas cost.

Motivation

  • Community feedback -- This OVIP reflects community feedback on the initial VASP Contract specification suggested in the OpenVASP White Paper.
  • Clarity -- OpenVASP implementation teams need clarity about the VASP identity mechanism to complete a first version of interoperable implementations.

Rationale

Decentralized approach

The specification maintains its decentralized approach by allowing the VASP to self-assign a VASP Code and deploy the VASP Contract without having to go through a central authority.

Reduced VASP Contract

One of the key changes to the initial design proposal is the reduction of the VASP Contract to a minimal record for public keys. This is done for the following reasons:

  • Separating identity from verification -- The protocol's trust model has always seen identity verification by trusted third parties as an additional layer on top of peer-to-peer trust. Consequently, identity attributes (such as name, address, etc.) are better stored where these attributes are actually verified. Omitting identity attributes from the self-assigned VASP Contract on the other hand avoids any false sense of security from these unverified properties.
  • Simplicity -- The revised specification simplifies smart contract design.
  • Interoperability -- A reduced VASP Contract together with the layered approach to identity verification allows for easier interoperability with other mechanisms for identity management, e.g. used by other travel rule solutions.
VASP Code

Maintaining the VASP Code as an identifier for the VASP Contract is done for practicality in (human) processing and its use in the Virtual Assets Account Number (VAAN). See OVIP-2 for more details about the VAAN.

Indirect retrieval of the VASP Contract via the Index Contract

The VASP Code is not stored in the VASP Contract, but only in the internal mapping of the Index Contract. In this way, developers interacting with these smart contracts are constantly reminded to always retrieve the VASP Contract from the source (Index Contract) by the help of the getVaspByCode() function.

Smart contract ownership

The Index Contract It has no owner with special privileges to rule out any unwanted influence on this security-critical component after deployment.

For the VASP Contract, the ability to transfer ownership is considered a must to deal with situations like change of ownership of the VASP entity or a compromised private key.

Separate keys for transport and session layer

Separating encryption on transport and session layers will allow for a better security architecture as the internet-facing Whisper could be decoupled from the server handling the sessions. Further, it allows the usage of a Whisper node by different session handlers without giving access to session layers details, particularly originator/beneficiary data.

Identity verification

Ways to verify the VASP's identity will be covered in a separate OVIP.

Backwards Compatibility

The specification proposed in this OVIP is not backwards compatible.