Перейти к содержанию

Interface: Protocol

Secure MPC signature protocol for ECDSA and EDDSA systems

Builds upon Crypto and Transport Drivers, thus completely encapsulating data cryptography and message transmission within the MPC algorithms. For now, only the 2-out-of-2 signature scheme is supported internally, however, the interface should still remain the same for more schemes with more parties.

Example

// Assuming an already existing secret bound to secretId

// Generate a random session ID for a new synchronization session
const syncSessionId = uuid(randomBytes);

// Perform an ECDSA-over-secp256k1 synchronization procedure for a key with path ```m/44'/0'/0'/0'/0'```
const publicKey = await syncDistributedEcdsaKey(protocol, secretId, syncSessionId, 'secp256k1', 0, 0);

// At this point, synchronization data is saved to the storage, so one may access it to get a compound public key
const _publicKey = await getEcdsaPublicKey(protocol, secretId, syncSessionId);

// Which is obviously the same key that was returned from the synchronization procedure itself
expect(publicKey).toBe(_publicKey);

// Generate a random session ID for a new signing session
const signSessionId = uuid(randomBytes);

// Message to be signed
const message = '7hJ9yN2OdIQo2kJu0arZgw2EKnMX5FGtQ6jlCWuLXCM='

// Generate an ECDSA signature
const signature = await signEcdsaMessage(protocol, secretId, syncSessionId, signSessionId, message);

// Verify the resulting signature against a compound public key with an external library (elliptic.js here)
expect(new ec('secp256k1').verify(
  Buffer.from(message, 'base64'),
  {
    s: Buffer.from(signature.s, 'base64'),
    r: Buffer.from(signature.r, 'base64'),
    recoveryParam: signature.recovery,
  },
  Buffer.from(publicKey, 'base64'),
)).toBeTruthy();

Implemented by

Methods

getEcdsaPublicKey

getEcdsaPublicKey(secretId, syncSessionId): Promise<string>

Get a compound public key from an already performed synchronization session

Example

// Assume an already performed synchronization procedure

const publicKey = await getEcdsaPublicKey(protocol, secretId, syncSessionId);

Parameters

Name Type Description
secretId string ID (UUID) of a secret used for synchronization
syncSessionId string ID (UUID) of a synchronization procedure

Returns

Promise<string>

compound public key, synchronized during that session


getEddsaPublicKey

getEddsaPublicKey(secretId, syncSessionId): Promise<string>

Get a compound public key from an already performed synchronization session

Example

// Assume an already performed synchronization procedure

const publicKey = await getEddsaPublicKey(protocol, secretId, syncSessionId);

Parameters

Name Type Description
secretId string ID (UUID) of a secret used for synchronization
syncSessionId string ID (UUID) of a synchronization procedure

Returns

Promise<string>

compound public key, synchronized during that session


off

off(): void

Unsubscribe from server-side events

Server-side events are not limited to an actual server, but may also be used to process requests on clients, the deciding factor here is who initiates the procedure.

Returns

void


on

on(): void

Subscribe to server-side events

Server-side events are not limited to an actual server, but may also be used to process requests on clients, the deciding factor here is who initiates the procedure.

Returns

void


removeDistributedEcdsaKey

removeDistributedEcdsaKey(secretId, syncSessionId): Promise<void>

Remove an already performed synchronization session from storage

Example

// Assume an already performed synchronization procedure

await removeDistributedEcdsaKey(protocol, secretId, syncSessionId);

Parameters

Name Type Description
secretId string ID (UUID) of a secret used for synchronization
syncSessionId string ID (UUID) of a synchronization procedure

Returns

Promise<void>


removeDistributedEddsaKey

removeDistributedEddsaKey(secretId, syncSessionId): Promise<void>

Remove an already performed synchronization session from storage

Example

// Assume an already performed synchronization procedure

await removeDistributedEddsaKey(protocol, secretId, syncSessionId);

Parameters

Name Type Description
secretId string ID (UUID) of a secret used for synchronization
syncSessionId string ID (UUID) of a synchronization procedure

Returns

Promise<void>


signEcdsaMessage

signEcdsaMessage(secretId, syncSessionId, signSessionId, message): Promise<EcdsaSignature>

Generate a distributed ECDSA signature for a given message

This method requires an already performed synchronization procedure and does not store anything in permanent storage. Messages of a length different from 32 bytes are padded/truncated according to the ECDSA algorithm.

Example

// Assume an already performed synchronization procedure and an established connection

// Generate a random session ID for a new signing session
const signSessionId = uuid(randomBytes);

// Message to be signed
const message = '7hJ9yN2OdIQo2kJu0arZgw2EKnMX5FGtQ6jlCWuLXCM='

// Generate an ECDSA signature
const signature = await signEcdsaMessage(protocol, secretId, syncSessionId, signSessionId, message);

// Verify the resulting signature against a compound public key with an external library (elliptic.js here)
expect(new ec('secp256k1').verify(
  Buffer.from(message, 'base64'),
  {
    s: Buffer.from(signature.s, 'base64'),
    r: Buffer.from(signature.r, 'base64'),
    recoveryParam: signature.recovery,
  },
  Buffer.from(publicKey, 'base64'),
)).toBeTruthy();

Parameters

Name Type Description
secretId string ID (UUID) of a secret used for synchronization
syncSessionId string ID (UUID) of a corresponding synchronization procedure
signSessionId string ID (UUID) of this signing procedure
message string message (32 bytes, base64 encoded) to be signed

Returns

Promise<EcdsaSignature>

ECDSA signature in standard format


signEddsaMessage

signEddsaMessage(secretId, syncSessionId, signSessionId, message): Promise<EddsaSignature>

Generate a distributed EDDSA signature for a given message

This method requires an already performed synchronization procedure and does not store anything in permanent storage. Messages of a length different from 32 bytes are padded/truncated according to the EDDSA algorithm.

Example

// Assume an already performed synchronization procedure and an established connection

// Generate a random session ID for a new signing session
const signSessionId = uuid(randomBytes);

// Message to be signed
const message = '7hJ9yN2OdIQo2kJu0arZgw2EKnMX5FGtQ6jlCWuLXCM='

// Generate an EDDSA signature
const signature = await signEddsaMessage(protocol, secretId, syncSessionId, signSessionId,  message);

// Verify the resulting signature against a compound public key with an external library (elliptic.js here)
expect(new ed('ed25519').verify(
  Buffer.from(message, 'base64').toString('hex'),
  Buffer.concat([
    Buffer.from(encodeEddsaPoint('ed25519', signature.R), 'base64'),
    Buffer.from(encodeEddsaBN('ed25519', signature.s), 'base64'),
  ]).toString('hex'),
  Buffer.from(encodeEddsaPoint('ed25519', publicKey), 'base64').toString('hex'),
)).toBeTruthy();

Parameters

Name Type Description
secretId string ID (UUID) of a secret used for synchronization
syncSessionId string ID (UUID) of a corresponding synchronization procedure
signSessionId string ID (UUID) of this signing procedure
message string message (32 bytes, base64 encoded) to be signed

Returns

Promise<EddsaSignature>

EDDSA signature in standard format


syncDistributedEcdsaKey

syncDistributedEcdsaKey(secretId, syncSessionId, curve, derivationCoin, derivationAccount): Promise<string>

Perform a distributed key synchronization procedure

Each secret is independently used to generate distributed key fragments (BIP-44 with path m/44'/(coin)'/(account)'/0'/0'), and then MPC key synchronization is performed over these fragments in order to get a compound public key for that pair.

Despite each session bearing a unique set of data, the resulting key is deterministic in regards to further synchronizations, e.g., each set of secrets, coin, and account always result in the same compound public key (and ephemeral private keys).

As a result of this operation, new synchronization session data is saved to persistent storage, accessible by the provided session ID. From this point, one may freely call getEcdsaPublicKey or signEcdsaMessage as long as synchronization data (and secret) is accessible in the storage.

Example

// Assume an already generated secret and an established connection

// Generate a random session ID for a new synchronization session
const syncSessionId = uuid(randomBytes);

// Perform an ECDSA-over-secp256k1 synchronization procedure for a key with path ```m/44'/0'/0'/0'/0'```
const publicKey = await syncDistributedEcdsaKey(protocol, secretId, syncSessionId, 'secp256k1', 0, 0);

// At this point, synchronization data is saved to the storage, so one may access it to get a compound public key
const _publicKey = await getEcdsaPublicKey(protocol, secretId, syncSessionId);

// Which is obviously the same key that was returned from the synchronization procedure itself
expect(publicKey).toBe(_publicKey);

Parameters

Name Type Description
secretId string ID (UUID) of a secret to be used for synchronization
syncSessionId string ID (UUID) of this synchronization procedure
curve "secp256k1" elliptic curve to use (only secp256k1 is supported at the moment)
derivationCoin number coin property of HD key derivation (BIP-44)
derivationAccount number account property of HD key derivation (BIP-44)

Returns

Promise<string>

compound public key (which may also be accessed via getEcdsaPublicKey later)


syncDistributedEddsaKey

syncDistributedEddsaKey(secretId, syncSessionId, eddsa, derivationCoin, derivationAccount): Promise<string>

Perform a distributed key synchronization procedure

Each secret is independently used to generate distributed key fragments (BIP-44 with path m/44'/(coin)'/(account)'/0'/0'), and then MPC key synchronization is performed over these fragments in order to get a compound public key for that pair.

Despite each session bearing a unique set of data, the resulting key is deterministic in regards to further synchronizations, e.g., each set of secrets, coin, and account always result in the same compound public key (and ephemeral private keys).

As a result of this operation, new synchronization session data is saved to persistent storage, accessible by the provided session ID. From this point, one may freely call getEddsaPublicKey or signEddsaMessage as long as synchronization data (and secret) is accessible in the storage.

Example

// Assume an already generated secret and an established connection

// Generate a random session ID for a new synchronization session
const syncSessionId = uuid(randomBytes);

// Perform an EDDSA-over-secp256k1 synchronization procedure for a key with path ```m/44'/0'/0'/0'/0'```
const publicKey = await syncDistributedEddsaKey(protocol, secretId, syncSessionId, 'ed25519', 0, 0);

// At this point, synchronization data is saved to the storage, so one may access it to get a compound public key
const _publicKey = await getEddsaPublicKey(protocol, secretId, syncSessionId);

// Which is obviously the same key that was returned from the synchronization procedure itself
expect(publicKey).toBe(_publicKey);

Parameters

Name Type Description
secretId string ID (UUID) of a secret to be used for synchronization
syncSessionId string ID (UUID) of this synchronization procedure
eddsa "ed25519" -
derivationCoin number coin property of HD key derivation (BIP-44)
derivationAccount number account property of HD key derivation (BIP-44)

Returns

Promise<string>

compound public key (which may also be accessed via getEddsaPublicKey later)