Interface: Protocol
Надежный MPC протокол подписания для ECDSA и EDDSA систем
Основан на Crypto и Transport Drivers, следовательно, полностью инкапсулирует криптографические данные и данные передачи сообщений внутри MPC алгоритмов. Сейчас поддерживается только 2-из-2 схема подписания, однако интерфейс должен остаться таким же для большего количества схем.
Пример
// Предполагаем что уже есть существующий секрет, связанный с secretId
// Генерируем случайный session ID для новой сессии синхронизации
const syncSessionId = uuid(randomBytes);
// Выполняем ECDSA-over-secp256k1 процедуру синхронизации для ключа с путем ```m/44'/0'/0'/0'/0'```
const publicKey = await syncDistributedEcdsaKey(protocol, secretId, syncSessionId, 'secp256k1', 0, 0);
// К этому моменту данные синхронизации уже сохранены в хранилище и составной ключ доступен к получению
const _publicKey = await getEcdsaPublicKey(protocol, secretId, syncSessionId);
// Этот ключ, очевидно, является тем же ключом, который мы получаем из самой процедуры синхронизации
expect(publicKey).toBe(_publicKey);
// Генерируем случайный session ID для новой сессии подписания
const signSessionId = uuid(randomBytes);
// Сообщение к подписанию
const message = '7hJ9yN2OdIQo2kJu0arZgw2EKnMX5FGtQ6jlCWuLXCM='
// Получаем signingToken
const signingToken = await getSigningToken
// Генерируем EDDSA подпись
const signature = await signEcdsaMessage(protocol, secretId, syncSessionId, signSessionId, message, signingToken);
// Верифицируем подпись к составному ключу с внешней библиотекой (тут - elliptic.js)
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();
Имплементировано
Методы
getEcdsaPublicKey
▸ getEcdsaPublicKey(secretId
, syncSessionId
): Promise
<string
>
Получение составного публичного ключа из уже выполненного сеанса синхронизации
Пример
// Считаем, что процедура синхронизации уже пройдена
const publicKey = await getEcdsaPublicKey(protocol, secretId, syncSessionId);
Параметры
Имя | Тип | Описание |
---|---|---|
secretId |
string |
ID (UUID) секрета, использующегося для синхронизации |
syncSessionId |
string |
ID (UUID) процедуры синхронизации |
Возвращает
Promise
<string
>
составной публичный ключ, синхронизированный в ходе этой сессии
getEddsaPublicKey
▸ getEddsaPublicKey(secretId
, syncSessionId
): Promise
<string
>
Получение составного публичного ключа из уже выполненного сеанса синхронизации
Пример
// Считаем, что процедура синхронизации уже пройдена
const publicKey = await getEddsaPublicKey(protocol, secretId, syncSessionId);
Параметры
Имя | Тип | Описание |
---|---|---|
secretId |
string |
ID (UUID) секрета, используемого для синхронизации |
syncSessionId |
string |
ID (UUID) процедуры синхронизации |
Возвращает
Promise
<string
>
составной публичный ключ, синхронизированный в ходе этой сессии
off
▸ off(): void
Отписка от серверных событий
Серверные события не ограничивают сам сервер, но могут быть использованы для обработки запросов клиента. Решающим фактором является, кто инициирует процедуру
Возвращает
void
on
▸ on(): void
Подписка на серверные события
Серверные события не ограничивают сам сервер, но могут быть использованы для обработки запросов клиента. Решающим фактором является, кто инициирует процедуру
Возвращает
void
removeDistributedEcdsaKey
▸ removeDistributedEcdsaKey(secretId
, syncSessionId
): Promise
<void
>
Удаляет уже существующую сессию синхронизации из хранилища
Example
// Считаем, что процедура синхронизации уже пройдена
await removeDistributedEcdsaKey(protocol, secretId, syncSessionId);
Параметры
Имя | Тип | Описание |
---|---|---|
secretId |
string |
ID (UUID) секрета, использующегося для синхронизации |
syncSessionId |
string |
ID (UUID) процедуры синхронизации |
Возвращает
Promise
<void
>
removeDistributedEddsaKey
▸ removeDistributedEddsaKey(secretId
, syncSessionId
): Promise
<void
>
Удаляет уже существующую сессию синхронизации из хранилища
Example
// Считаем, что процедура синхронизации уже пройдена
await removeDistributedEddsaKey(protocol, secretId, syncSessionId);
Параметры
Имя | Тип | Описание |
---|---|---|
secretId |
string |
ID (UUID) секрета, использующегося для синхронизации |
syncSessionId |
string |
ID (UUID) процедуры синхронизации |
Возвращает
Promise
<void
>
signEcdsaMessage
▸ signEcdsaMessage(secretId
, syncSessionId
, signSessionId
, message
, signingToken
): Promise
<EcdsaSignature
>
Генерирует распределенную ECDSA подпись для данного сообщения
Для этого метода необходима уже пройденная процедура синхронизации и так же ничего не должно быть помещено в постоянное хранилище. Сообщения с объемом информации отличным от 32 байт будут дополнены/урезаны в соответствии с ECDSA алгоритмом.
Пример
// Считаем, что процедура синхронизации уже пройдена и соединение установлено
// Генерируем рандомный session ID для новой сессии подписания
const signSessionId = uuid(randomBytes);
// Сообщение к подписанию
const message = '7hJ9yN2OdIQo2kJu0arZgw2EKnMX5FGtQ6jlCWuLXCM='
// Получаем signingToken
const signingToken = await getSigningToken(message)
// Генерируем ECDSA подпись
const signature = await signEcdsaMessage(protocol, secretId, syncSessionId, signSessionId, message, signingToken);
// Верифицируем подпись к составному ключу с внешней библиотекой (тут - elliptic.js)
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();
Параметры
Имя | Тип | Описание |
---|---|---|
secretId |
string |
ID (UUID) секрета, используемого для синхронизации |
signSessionId |
string |
ID (UUID) данной процедуры подписания |
syncSessionId |
string |
ID (UUID) соответствующей процедуры синхронизации |
message |
string |
сообщение (32 байта base64-encoded) к подписанию |
Возвращает
Promise
<EcdsaSignature
>
ECDSA подпись в стандартном формате
signEddsaMessage
▸ signEddsaMessage(secretId
, syncSessionId
, signSessionId
, message
, signingToken
): Promise
<EddsaSignature
>
Генерирует распределенную EDDSA подпись для данного сообщения
Для этого метода необходима уже пройденная процедура синхронизации и так же ничего не должно быть помещено в постоянное хранилище. Сообщения с объемом информации отличным от 32 байт будут дополнены/урезаны в соответствии с EDDSA алгоритмом.
Пример
// Считаем, что процедура синхронизации уже пройдена и соединение установлено
// Генерируем рандомный session ID для новой сессии подписания
const signSessionId = uuid(randomBytes);
// Сообщение к подписанию
const message = '7hJ9yN2OdIQo2kJu0arZgw2EKnMX5FGtQ6jlCWuLXCM='
// Получаем signingToken
const signingToken = await getSigningToken(message)
// Генерируем EDDSA подпись
const signature = await signEddsaMessage(protocol, secretId, syncSessionId, signSessionId, message, signingToken);
// Верифицируем подпись к составному ключу с внешней библиотекой (тут - elliptic.js)
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();
Параметры
Имя | Тип | Описание |
---|---|---|
secretId |
string |
ID (UUID) секрета, использующегося для синхронизации |
signSessionId |
string |
ID (UUID) данной процедуры подписания |
syncSessionId |
string |
ID (UUID) соответствующей процедуры синхронизации |
message |
string |
сообщение (32 байта base64-encoded) к подписанию |
Возвращает
Promise
<EddsaSignature
>
EDDSA подпись в стандартном формате
syncDistributedEcdsaKey
▸ syncDistributedEcdsaKey(secretId
, syncSessionId
, curve
, derivationCoin
, derivationAccount
): Promise
<string
>
Выполняет процедуру синхронизации распределенного ключа (отдельный помощник)
Каждый секрет используется независимо для генерации фрагментов распределенного ключа (BIP-44 с путем m/44'/(coin)'/(account)'/0'/0'
),
и после с этими фрагментами выполняется синхронизация MPC ключа для получения составного публичного ключа для этой пары.
Вопреки тому, что каждая сессия содержит уникальный сет данных, полученный ключ детерминирован для последующих синхронизаций, например, каждый набор секретов, монета и аккаунт всегда создадут тот же составной ключ (и эфемерные приватные ключи)
В результате этой операции новые данные синхронизации сохранены в постоянное хранилище, доступ к которым можно получить с помощью session ID.
С этого момента можно свободно вызывать getEcdsaPublicKey или signEcdsaMessage пока данные синхронизации (или секрет) доступны в хранилище.
Пример
// Считаем, что процедура синхронизации уже пройдена и соединение установлено
// Генерируем рандомный session ID для новой сессии подписания
const syncSessionId = uuid(randomBytes);
// Выполняем ECDSA-over-secp256k1 процедуру синхронизации для ключа с путем ```m/44'/0'/0'/0'/0'```
const publicKey = await syncDistributedEcdsaKey(protocol, secretId, syncSessionId, 'secp256k1', 0, 0);
// К этому моменту данные синхронизации уже сохранены в хранилище и составной ключ доступен к получению
const _publicKey = await getEcdsaPublicKey(protocol, secretId, syncSessionId);
// Этот ключ, очевидно, является тем же ключом, который мы получаем из самой процедуры синхронизации
expect(publicKey).toBe(_publicKey);
Параметры
Имя | Тип | Описание |
---|---|---|
syncSessionId |
string |
ID (UUID) данной процедуры синхронизации |
secretId |
string |
ID (UUID) секрета, используемого для синхронизации |
curve |
"secp256k1" |
эллиптическая кривая (ECC) (на данный момент поддерживается только secp256k1 ) |
derivationCoin |
number |
coin свойство формирования HD ключа (BIP-44) |
derivationAccount |
number |
account свойство формирования HD ключа (BIP-44) |
Возвращает
Promise
<string
>
составной публичный ключ (который так же можно получить позже с помощью getEcdsaPublicKey)
syncDistributedEddsaKey
▸ syncDistributedEddsaKey(syncSessionId
, secretId
, eddsa
, derivationCoin
, derivationAccount
): Promise
<string
>
Выполняет процедуру синхронизации распределенного ключа (отдельный помощник)
Каждый секрет используется независимо для генерации фрагментов распределенного ключа (BIP-44 с путем m/44'/(coin)'/(account)'/0'/0'
),
и после с этими фрагментами выполняется синхронизация MPC ключа для получения составного публичного ключа для этой пары.
Вопреки тому, что каждая сессия содержит уникальный сет данных, полученный ключ детерминирован для последующих синхронизаций, например, каждый набор секретов, монета и аккаунт всегда создадут тот же составной ключ (и эфемерные приватные ключи)
В результате этой операции новые данные синхронизации сохранены в постоянное хранилище, доступ к которым можно получить с помощью session ID.
С этого момента можно свободно вызывать getEddsaPublicKey или signEddsaMessage пока данные синхронизации (или секрет) доступны в хранилище.
Пример
// Считаем, что процедура синхронизации уже пройдена и соединение установлено
// Генерируем рандомный session ID для новой сессии подписания
const syncSessionId = uuid(randomBytes);
// Выполняем EDDSA-over-secp256k1 процедуру синхронизации для ключа с путем ```m/44'/0'/0'/0'/0'```
const publicKey = await syncDistributedEddsaKey(protocol, secretId, syncSessionId, 'ed25519', 0, 0);
// К этому моменту данные синхронизации уже сохранены в хранилище и составной ключ доступен к получению
const _publicKey = await getEddsaPublicKey(protocol, secretId, syncSessionId);
// Этот ключ, очевидно, является тем же ключом, который мы получаем из самой процедуры синхронизации
expect(publicKey).toBe(_publicKey);
Параметры
Имя | Тип | Описание |
---|---|---|
secretId |
string |
ID (UUID) секрета используемого для синхронизации |
syncSessionId |
string |
ID (UUID) данной процедуры синхронизации |
eddsa |
"ed25519" |
эллиптическая кривая (ECC) (на данный момент поддерживается только secp256k1 ) |
derivationCoin |
number |
coin свойство формирования HD ключа (BIP-44) |
derivationAccount |
number |
account свойство формирования HD ключа (BIP-44) |
Возвращает
Promise
<string
>
составной публичный ключ (который так же можно получить позже с помощью getEddsaPublicKey)