ПРОТОКОЛ
ProtocolSwift
Надежный MPC протокол подписания для ECDSA и EDDSA систем
Основан на Crypto и Transport Drivers, следовательно, полностью инкапсулирует криптографические данные и данные передачи сообщений внутри MPC алгоритмов. Сейчас поддерживается только 2-из-2 схема подписания, однако интерфейс должен остаться таким же для большего количества схем.
Пример
// Предполагаем что уже есть существующий секрет, связанный с secretId
// Генерируем случайный session ID для новой сессии синхронизации
let syncSessionId = UUID().uuidString
// Выполняем ECDSA-over-secp256k1 процедуру синхронизации для ключа с путем ```m/44'/0'/0'/0'/0'```
let publicKey = await syncDistributedEcdsaKey(driver, secretId, syncSessionId, .secp256k1 , 0, 0);
// К этому моменту данные синхронизации уже сохранены в хранилище и составной ключ доступен к получению
let _publicKey = await getEcdsaPublicKey(driver, secretId, syncSessionId);
// Этот ключ, очевидно, является тем же ключом, который мы получаем из самой процедуры синхронизации
XCTAssertEqual(publicKey, _publicKey)
// Генерируем случайный session ID для новой сессии подписания
const signSessionId = UUID().uuidString
// Сообщение к подписанию
let message = "7hJ9yN2OdIQo2kJu0arZgw2EKnMX5FGtQ6jlCWuLXCM="
// Генерируем EDDSA подпись
let signature = await signEcdsaMessage(driver, secretId, syncSessionId, signSessionId, message, signatureToken)
// Верифицируем подпись к составному ключу
print([
"curve": EcdsaCurve.secp256k1,
"publicKey": publicKey,
"message": message,
"signature": [
"r": signature.r,
"s": signature.s,
"recovery": signature.recovery
] as [String : Any]
] as [String : Any])
Методы
syncDistributedEcdsaKey(secretId:syncSessionId:curve:derivationCoin:derivationAccount:)
func syncDistributedEcdsaKey(secretId: String, syncSessionId: String, curve: EcdsaCurve, derivationCoin: UInt32, derivationAccount: UInt32) async throws -> String
Выполняет процедуру синхронизации распределенного ключа (отдельный помощник)
Каждый секрет используется независимо для генерации фрагментов распределенного ключа (BIP-44 с путем m/44'/(coin)'/(account)'/0'/0'
),
и после с этими фрагментами выполняется синхронизация MPC ключа для получения составного публичного ключа для этой пары.
Вопреки тому, что каждая сессия содержит уникальный сет данных, полученный ключ детерминирован для последующих синхронизаций, например, каждый набор секретов, монета и аккаунт всегда создадут тот же составной ключ (и эфемерные приватные ключи)
В результате этой операции новые данные синхронизации сохранены в постоянное хранилище, доступ к которым можно получить с помощью session ID.
С этого момента можно свободно вызывать getEcdsaPublicKey(secretId:syncSessionId:)
or signEcdsaMessage(secretId:syncSessionId:signSessionId:message:signatureToken)
пока данные синхронизации (или секрет) доступны в хранилище.
Пример
// Считаем, что процедура синхронизации уже пройдена и соединение установлено
// Генерируем рандомный session ID для новой сессии подписания
let syncSessionId = UUID().uuidString
// Выполняем ECDSA-over-secp256k1 процедуру синхронизации для ключа с путем ```m/44'/0'/0'/0'/0'```
let publicKey = await syncDistributedEcdsaKey(secretId, syncSessionId, curve, derivationCoin, derivationAccount)
// К этому моменту данные синхронизации уже сохранены в хранилище и составной ключ доступен к получению
let _publicKey = await getEcdsaPublicKey(secretId, syncSessionId)
// Этот ключ, очевидно, является тем же ключом, который мы получаем из самой процедуры синхронизации
XCTAssertEqual(publicKey, _publicKey)
Параметры
Имя | Описание |
---|---|
secretId | ID (UUID) секрета, используемого для синхронизации |
syncSessionId | ID (UUID) данной процедуры синхронизации |
curve | эллиптическая кривая (ECC) (на данный момент поддерживается только secp256k1 ) |
derivationCoin | coin свойство формирования HD ключа (BIP-44) |
derivationAccount | account свойство формирования HD ключа (BIP-44) |
Возвращает
String
составной публичный ключ (который так же можно получить позже с помощью `getEcdsaPublicKey(secretId:syncSessionId:)
)
getEcdsaPublicKey(secretId:syncSessionId:)
Получение составного публичного ключа из уже выполненного сеанса синхронизации
Пример
// Считаем, что процедура синхронизации уже пройдена
let publicKey = await getEcdsaPublicKey(secretId: secretId, syncSessionId: syncSessionId)
Параметры
Имя | Описание |
---|---|
secretId | ID (UUID) секрета, использующегося для синхронизации |
syncSessionId | ID (UUID) процедуры синхронизации |
Возвращает
String
составной публичный ключ, синхронизированный в ходе этой сессии
removeDistributedEcdsaKey(secretId:syncSessionId:)
Удаляет уже существующую сессию синхронизации из хранилища
Пример
// Считаем, что процедура синхронизации уже пройдена
await removeDistributedEcdsaKey(secretId: secretId, syncSessionId: syncSessionId)
Параметры
Имя | Описание |
---|---|
secretId | ID (UUID) секрета, использующегося для синхронизации |
syncSessionId | ID (UUID) процедуры синхронизации |
Возвращает
void
signEcdsaMessage(secretId:syncSessionId:signSessionId:message:signatureToken:)
func signEcdsaMessage(secretId: String, syncSessionId: String, signSessionId: String, message: String, signatureToken: String) async throws -> EcdsaSignature
Генерирует распределенную ECDSA подпись для данного сообщения
Для этого метода необходима уже пройденная процедура синхронизации и так же ничего не должно быть помещено в постоянное хранилище. Сообщения с объемом информации отличным от 32 байт будут дополнены/урезаны в соответствии с ECDSA алгоритмом.
Пример
// Считаем, что процедура синхронизации уже пройдена и соединение установлено
// Генерируем рандомный session ID для новой сессии подписания
let signSessionId = UUID().uuidString
// Сообщение к подписанию
let message = "7hJ9yN2OdIQo2kJu0arZgw2EKnMX5FGtQ6jlCWuLXCM="
// Генерируем ECDSA подпись
let signature = await signEcdsaMessage(driver, secretId, syncSessionId, signSessionId, message, signatureToken)
// Верифицируем подпись к составному ключу
print([
"curve": EcdsaCurve.secp256k1,
"publicKey": publicKey,
"message": message,
"signature": [
"r": signature.r,
"s": signature.s,
"recovery": signature.recovery
] as [String : Any]
] as [String : Any])
Параметры
Имя | Описание |
---|---|
secretId | ID (UUID) секрета, используемого для синхронизации |
syncSessionId | ID (UUID) соответствующей процедуры синхронизации |
signSessionId | ID (UUID) данной процедуры подписания |
message | сообщение (32 байта base64-encoded) к подписанию |
signatureToken | - |
Возвращает
EcdsaSignature
ECDSA подпись в стандартном формате
syncDistributedEddsaKey(secretId:syncSessionId:curve:derivationCoin:derivationAccount:)
func syncDistributedEddsaKey(secretId: String, syncSessionId: String, curve: EddsaCurve, derivationCoin: UInt32, derivationAccount: UInt32) async throws -> String
Выполняет процедуру синхронизации распределенного ключа (отдельный помощник)
Каждый секрет используется независимо для генерации фрагментов распределенного ключа (BIP-44 с путем m/44'/(coin)'/(account)'/0'/0'
),
и после с этими фрагментами выполняется синхронизация MPC ключа для получения составного публичного ключа для этой пары.
Вопреки тому, что каждая сессия содержит уникальный сет данных, полученный ключ детерминирован для последующих синхронизаций, например, каждый набор секретов, монета и аккаунт всегда создадут тот же составной ключ (и эфемерные приватные ключи)
В результате этой операции новые данные синхронизации сохранены в постоянное хранилище, доступ к которым можно получить с помощью session ID.
С этого момента можно свободно вызывать getEddsaPublicKey(secretId:syncSessionId:)
или signEddsaMessage(secretId:syncSessionId:signSessionId:message:signatureToken)
пока данные синхронизации (или секрет) доступны в хранилище.
Пример
// Считаем, что процедура синхронизации уже пройдена и соединение установлено
// Генерируем рандомный session ID для новой сессии подписания
let syncSessionId = UUID().uuidString
// Выполняем EDDSA-over-secp256k1 процедуру синхронизации для ключа с путем ```m/44'/0'/0'/0'/0'```
let publicKey = await syncDistributedEddsaKey(secretId, syncSessionId, curve, derivationCoin, derivationAccount)
// К этому моменту данные синхронизации уже сохранены в хранилище и составной ключ доступен к получению
let _publicKey = await getEddsaPublicKey(driver, secretId, syncSessionId)
// Этот ключ, очевидно, является тем же ключом, который мы получаем из самой процедуры синхронизации
XCTAssertEqual(publicKey, _publicKey)
Параметры
Имя | Описание |
---|---|
secretId | ID (UUID) секрета используемого для синхронизации |
syncSessionId | ID (UUID) данной процедуры синхронизации |
curve | эллиптическая кривая (ECC) (на данный момент поддерживается только secp256k1 ) |
derivationCoin | coin свойство формирования HD ключа (BIP-44) |
derivationAccount | account свойство формирования HD ключа (BIP-44) |
Возвращает
String
составной публичный ключ (который так же можно получить позже с помощью getEddsaPublicKey(secretId:syncSessionId:)
getEddsaPublicKey(secretId:syncSessionId:)
Получение составного публичного ключа из уже выполненного сеанса синхронизации
Пример
// Считаем, что процедура синхронизации уже пройдена
let publicKey = await getEddsaPublicKey(secretId: secretId, syncSessionId: syncSessionId)
Параметры
Имя | Описание |
---|---|
secretId | ID (UUID) of a secret used for synchronisation |
syncSessionId | ID (UUID) of a synchronisation procedure |
Возвращает
String
составной публичный ключ, синхронизированный в ходе этой сессии
removeDistributedEddsaKey(secretId:syncSessionId:)
Удаляет уже существующую сессию синхронизации из хранилища
Пример
// Считаем, что процедура синхронизации уже пройдена
await removeDistributedEddsaKey(secretId: secretId, syncSessionId: syncSessionId)
Параметры
Имя | Описание |
---|---|
secretId | ID (UUID) секрета, использующегося для синхронизации |
syncSessionId | ID (UUID) процедуры синхронизации |
Возвращает
void
signEddsaMessage(secretId:syncSessionId:signSessionId:message:signatureToken)
func signEddsaMessage(secretId: String, syncSessionId: String, signSessionId: String, message: String, signatureToken: String) async throws -> EddsaSignature
Генерирует распределенную EDDSA подпись для данного сообщения
Для этого метода необходима уже пройденная процедура синхронизации и так же ничего не должно быть помещено в постоянное хранилище. Сообщения с объемом информации отличным от 32 байт будут дополнены/урезаны в соответствии с EDDSA алгоритмом.
Пример
// Считаем, что процедура синхронизации уже пройдена и соединение установлено
// Генерируем рандомный session ID для новой сессии подписания
let signSessionId = UUID().uuidString
// Сообщение к подписанию
let message = "7hJ9yN2OdIQo2kJu0arZgw2EKnMX5FGtQ6jlCWuLXCM="
// Генерируем EDDSA подпись
let signature = await signEddsaMessage(driver, secretId, syncSessionId, signSessionId, message, signatureToken)
// Верифицируем подпись к составному ключу
print([
"curve": EddsaCurve.ed25519,
"publicKey": publicKey,
"message": message,
"signature": [
"R": signature.R,
"s": signature.s
] as [String : Any]
] as [String : Any])
Параметры
Имя | Описание |
---|---|
secretId | ID (UUID) секрета, использующегося для синхронизации |
syncSessionId | ID (UUID) соответствующей процедуры синхронизации |
signSessionId | ID (UUID) данной процедуры подписания |
message | сообщение (32 байта base64-encoded) к подписанию |
signatureToken | - |
Возвращает
EddsaSignature
EDDSA подпись в стандартном формате
on(validateMessage:)
Подписка на серверные события
Серверные события не ограничивают сам сервер, но могут быть использованы для обработки запросов клиента. Решающим фактором является, кто инициирует процедуру
Параметры
Имя | Описание |
---|---|
validateMessage | Внешний валидатор транзакции |
Возвращает
void
off()
Отписка от серверных событий
Серверные события не ограничивают сам сервер, но могут быть использованы для обработки запросов клиента. Решающим фактором является, кто инициирует процедуру
Возвращает
void