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

CLASS

SpatiumProtocol

open class SpatiumProtocol: ProtocolSwift

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 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 random session ID for a new synchronisation session
 let syncSessionId = UUID().uuidString

 // Perform a ECDSA-over-secp256k1 synchronisation procedure for a key with path "m/44'/0'/0'/0'/0'"
 let publicKey = await syncDistributedEcdsaKey(driver, secretId, syncSessionId, .secp256k1 , 0, 0);

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

 // Which is obviously the same key that was returned from the synchronisation procedure itself
 XCTAssertEqual(publicKey, _publicKey)

 // Generate random session ID for a new signing session
 const signSessionId = UUID().uuidString

 // Message to be signed
 let message = "7hJ9yN2OdIQo2kJu0arZgw2EKnMX5FGtQ6jlCWuLXCM="

 // Generate a ECDSA signature
 let signature = await signEcdsaMessage(driver, secretId, syncSessionId, signSessionId, message, signatureToken)

 // Verify the resulting signature against a compound public key
 print([
     "curve": EcdsaCurve.secp256k1,
     "publicKey": publicKey,
     "message": message,
     "signature": [
         "r": signature.r,
         "s": signature.s,
         "recovery": signature.recovery
     ] as [String : Any]
 ] as [String : Any])

Properties

crypto

public final let crypto: Crypto

software or hardware crypto module to perform computations with

transport

public final let transport: TransportDriver

message passing implementation, be it socket, HTTP(S) or other means of communication

Methods

init(crypto:transport:timeout:)

public init(crypto: Crypto, transport: TransportDriver, timeout: UInt?)

SpatiumProtocol Constructor

Parameters

Name Description
crypto software or hardware crypto module to perform computations with
transport message passing implementation, be it socket, HTTP(S) or other means of communication
timeout (optional) per-message request timeout, after which a procedure is dropped

on(validateMessage:)

public func on(validateMessage: @escaping (_ message: String, _ signatureToken: String) async -> Bool)

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.

Parameters

Name Description
validateMessage external transaction validator

Returns

void

syncDistributedEcdsaKey(secretId:syncSessionId:curve:derivationCoin:derivationAccount:)

public func syncDistributedEcdsaKey(secretId: String, syncSessionId: String, curve: EcdsaCurve, derivationCoin: UInt32, derivationAccount: UInt32) async throws -> String

Perform distributed key synchronisation 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 synchronisation is performed over these fragments in order to get a compound public key for that pair.

Despite each session is bearing a unique set of data, the resulting key is deterministic in regards to further synchronisations, 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, a new synchronisation session data is saved to a persistent storage, accessible by the provided session ID. From this point one may freely call getEcdsaPublicKey(secretId:syncSessionId:) or signEcdsaMessage(secretId:syncSessionId:signSessionId:message:signatureToken:) as long as synchronisation data (and secret) is accessible in the storage.

Example

 // Assume an already generated secret and established connection

 // Generate random session ID for a new synchronisation session
 let syncSessionId = UUID().uuidString

 // Perform a ECDSA-over-secp256k1 synchronisation procedure for a key with path `m/44'/0'/0'/0'/0'
 let publicKey = await syncDistributedEcdsaKey(secretId, syncSessionId, curve, derivationCoin, derivationAccount)

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

 // Which is obviously the same key that was returned from the synchronisation procedure itself
 XCTAssertEqual(publicKey, _publicKey)

Parameters

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

Returns

String

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

getEcdsaPublicKey(secretId:syncSessionId:)

public func getEcdsaPublicKey(secretId: String, syncSessionId: String) async throws -> String

Get a compound public key from an already performed synchronisation session

  • Parameter secretId: ID (UUID) of a secret used for synchronisation
  • Parameter syncSessionId: ID (UUID) of a synchronisation procedure

Example

 // Assume an already performed synchronisation procedure

 let publicKey = await getEcdsaPublicKey(secretId, syncSessionId)

Parameters

Name Description
secretId ID (UUID) of a secret used for synchronisation
syncSessionId ID (UUID) of a synchronisation procedure

Returns

String

compound public key, synchronised during that session

removeDistributedEcdsaKey(secretId:syncSessionId:)

public func removeDistributedEcdsaKey(secretId: String, syncSessionId: String) async throws

Remove an already performed synchronisation session from storage

Example

 // Assume an already performed synchronisation procedure

 await removeDistributedEcdsaKey(secretId: secretId, syncSessionId: syncSessionId)

Parameters

Name Description
secretId ID (UUID) of a secret used for synchronisation
syncSessionId ID (UUID) of a synchronisation procedure

Returns

void

signEcdsaMessage(secretId:syncSessionId:signSessionId:message:signatureToken:)

public func signEcdsaMessage(secretId: String, syncSessionId: String, signSessionId: String, message: String, signatureToken: String) async throws -> EcdsaSignature

Generate distributed ECDSA signature for a given message

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

Example

 // Assume an already performed synchronisation procedure and established connection

 // Generate random session ID for a new signing session
 let signSessionId = UUID().uuidString

 // Message to be signed
 let message = "7hJ9yN2OdIQo2kJu0arZgw2EKnMX5FGtQ6jlCWuLXCM="

 // Generate a ECDSA signature
 let signature = await signEcdsaMessage(driver, secretId, syncSessionId, signSessionId, message, signatureToken)

 // Verify the resulting signature against a compound public key
 print([
     "curve": EcdsaCurve.secp256k1,
     "publicKey": publicKey,
     "message": message,
     "signature": [
         "r": signature.r,
         "s": signature.s,
         "recovery": signature.recovery
     ] as [String : Any]
 ] as [String : Any])

Parameters

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

Returns

EcdsaSignature

ECDSA signature in standard format

syncDistributedEddsaKey(secretId:syncSessionId:curve:derivationCoin:derivationAccount:)

public func syncDistributedEddsaKey(secretId: String, syncSessionId: String, curve: EddsaCurve, derivationCoin: UInt32, derivationAccount: UInt32) async throws -> String

Perform distributed key synchronisation 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 synchronisation is performed over these fragments in order to get a compound public key for that pair.

Despite each session is bearing a unique set of data, the resulting key is deterministic in regards to further synchronisations, 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, a new synchronisation session data is saved to a persistent storage, accessible by the provided session ID. From this point one may freely call getEddsaPublicKey(secretId:syncSessionId:) or signEddsaMessage(secretId:syncSessionId:signSessionId:message:signatureToken:) as long as synchronisation data (and secret) is accessible in the storage.

Example

 // Assume an already generated secret and established connection

 // Generate random session ID for a new synchronisation session
 let syncSessionId = UUID().uuidString

 // Perform a EDDSA-over-secp256k1 synchronisation procedure for a key with path "m/44'/0'/0'/0'/0'"
 let publicKey = await syncDistributedEddsaKey(secretId, syncSessionId, curve, derivationCoin, derivationAccount)

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

 // Which is obviously the same key that was returned from the synchronisation procedure itself
 XCTAssertEqual(publicKey, _publicKey)

Parameters

Name Description
secretId ID (UUID) of a secret to be used for synchronisation
syncSessionId ID (UUID) of this synchronisation procedure
curve elliptic curve to use (only .ed25519 is supported at the moment)
derivationCoin “coin” property of HD key derivation (BIP-44)
derivationAccount “account” property of HD key derivation (BIP-44)

Returns

String

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

getEddsaPublicKey(secretId:syncSessionId:)

Get a compound public key from an already performed synchronisation session

public func getEddsaPublicKey(secretId: String, syncSessionId: String) async throws -> String

Parameters

Name Description
secretId ID (UUID) of a secret used for synchronisation
syncSessionId ID (UUID) of a synchronisation procedure

Returns

String

compound public key, synchronised during that session

removeDistributedEddsaKey(secretId:syncSessionId:)

public func removeDistributedEddsaKey(secretId: String, syncSessionId: String) async throws

Remove an already performed synchronisation session from storage

Example

 // Assume an already performed synchronisation procedure

 await removeDistributedEddsaKey(secretId: secretId, syncSessionId: syncSessionId)

Parameters

Name Description
secretId ID (UUID) of a secret used for synchronisation
syncSessionId ID (UUID) of a synchronisation procedure

Returns

void

signEddsaMessage(secretId:syncSessionId:signSessionId:message:signatureToken:)

public func signEddsaMessage(secretId: String, syncSessionId: String, signSessionId: String, message: String, signatureToken: String) async throws -> EddsaSignature

Generate distributed EDDSA signature for a given message

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

Example

 // Assume an already performed synchronisation procedure and established connection

 // Generate random session ID for a new signing session
 let signSessionId = UUID().uuidString

 // Message to be signed
 let message = "7hJ9yN2OdIQo2kJu0arZgw2EKnMX5FGtQ6jlCWuLXCM="

 // Generate a EDDSA signature
 let signature = await signEddsaMessage(driver, secretId, syncSessionId, signSessionId, message, signatureToken)

 // Verify the resulting signature against a compound public key
 print([
     "curve": EddsaCurve.ed25519,
     "publicKey": publicKey,
     "message": message,
     "signature": [
         "R": signature.R,
         "s": signature.s
     ] as [String : Any]
 ] as [String : Any])

Parameters

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

Returns

EddsaSignature

EDDSA signature in standard format

off()

public func off()

Unsubscribe from server-side events

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

Returns

void