# keyless/signer функциональная спецификация > Иван Кочешев > @devmanio > ik@l2.team ## 0. Введение Сайнер, компонент описываемый в рамках текущего документа будет являтся ключевым компонентом в рамках развертывания следующих сервисов: * **keyless.id/cloud** - сервис предостовляющие услуги ЭЦП/шифрования(api only) в облачной инфраструктуре. Подразумевает использование как конечными пользователями так и развертывание в приватном облаке клиента. * **[keyless.one](https://keyless.one)** - веб интерфейс к браузерной версии сайнера. keyless.id/signer предпологает две версии развертования - в браузере пользователя(**keyless.one**), в отдельном процессе(сервис воркере) и в виртуальной машине в облаке. Первая предостовляет услуги пользователям без использования сетевой инфраструктуры, вторая предостовляет услуги сетевым сервисам. Кмк идеально использовать WASM для реализации тк WASM поддерживает исполнение как в середе браузера, так и в среде ОС(WASI). Тк критически важно не допустить ошибок или недокументированной функциональности в имплементации сервиса идеально использовать rustlang. **signer** является ключевым компонентом обоих версий, по возможности мы должны получить одну версию модуля сайнера, ответственную за работу с криптографическими примитивами(шифросьютами), которая будет использоватся как ключевой компонент для облачной и клиентской версии сайнера. > **Важное условие/ОДЗ**: приватный ключ не покидавет сайнер в открытом виде не при каких условиях. Базовым объектом, с которым оперирует сайнер является сертификат ключа. Каждый сертификат имеет уникальный идентификатор. Помимо идентификатора он содержит мета-данные ресурса, стоящего за этим идентефикатором и политики доступа к ресурсам этого ключа. Этот концеп более известен как AWS IAM[^aws_iam]. Проще всего его представить в виде JSON: ```jsonld= { "version": "1", // версия keyless identity and acces managment key's certificate "identity": {} // содержит один или более идентефикаторов ключей "policy": {} // содержит одну или более политик доступа к ресурсам(методам) ключа } ``` ### 2.1. Сертификат ключа(identity) Сертификат ключа - сущность включающая в себя все метаданные ключа, необходимые при исполнении протоколов сайнера, в том числе и приватные ключи для ЭЦП и/или Шифрования. Сертификат ключа может включать в себя надор из нескольких(вложенных) ключей. Такую группировку удобно использовать например при применении bip39(hd) кошельков для пользователей, или для группировки ключей нескольких кошельков для учетной записи к мультичейн бирже. ```jsonld= { "version": "1", "identity": { "type": "username", "username": { "keys": { "devmanio": { privKey: "harshaPrivateKey", status: "enabled" }, }, }, "type": "bitcoin": { "bitcoin": { "keys": { "btc_first_wallet": { privKey: "harshaPrivateKey", status: "enabled" }, "btc_second_wallet": { privKey: "harshaPrivateKey", status: "enabled" }, } }, }, "type": "ethereum": { "ethereum": { "keys": { "eth_first_wallet": { privKey: "harshaPrivateKey", status: "enabled" }, "eth_second_wallet": { privKey: "harshaPrivateKey", status: "enabled" }, } } } } ``` ### 2.2. Политики доступа к ресурсам ключа(policy) Каждый id в сайнере является идентификатором ключа. У id можно прочитать его метаданные(например pubKey), им можно подписать, его можно экспортировать. Все тонкости доступа к ресурсу задает пользователь, сгенерировавший ресурс в виде политик доступа. > ARN: arn:partition:service:region:account:resource Where: > **partition** identifies the partition that the resource is in. For standard AWS regions, the partition is aws. If you have resources in other partitions, the partition is aws-partitionname. For example, the partition for resources in the China (Beijing) Region is aws-cn. > **service** identifies the AWS product. For IAM resources, this is always iam. > **region** is the Region the resource resides in. For IAM resources, this is always left blank. > **account** is the AWS account ID with no hyphens (for example, 123456789012). > **resource** is the portion that identifies the specific resource by name. > You can use ARNs in IAM for users (IAM and federated), groups, roles, policies, instance profiles, virtual MFA devices, and server certificates. The following table shows the ARN format for each and an example. The Region portion of the ARN is blank because IAM resources are global. ```jsonld= { "policy": { "type": "ethereum", "ethereum": { "keys": { "eth_first_wallet": { "Version": "2012-10-17", "Statement": [ { "Action": [ "signer:signTX", ], "Effect": "Allow", "Resource": "arn:keyless:one:local:devmanio:signier/ethereum/eth_first_wallet" //для локального сайнера // "arn:keyless:oxa:global:devmanio:signier/ethereum/eth_first_wallet" // для облачного сайнера } ] }, } } } ``` #### ВАЖНО!!! С политиками много дел - пока у всего единая политика: * > Работа сайнера подчиняется наборам протоколов, описанных в разделе Протоколы, далее по тексту. > В будущем возможно появления дополнительных протоколов управления сервисом, доступ к которым осущевсвляется пользователем подписавшем идентификатор инстанции. ### 2.3. Жизненный цикл сайнера //нарисовать доставка приложения приложения // Ключ от разработчика запуск приложения // Ключ от пользователя/системного администратора Импорт Keyless.ID's / Генерация Keyless.ID's // Ключ от пользователя/системного администратора Экспорт Keyless.ID's Остановка сервиса В момент развертывания экземпляр сайнера подписывается ключем сервиса доставки приложения(читай разработчика), в рамках подписи экземпляру сайнера присваивается идентефикатор версии client_ID/destination_target/version/ (напр: keyless.id/oxa_cloud/1/ или keyless.id/browser/1/ или keyless.id/aws/1/). **identity неразвернутого приложения сайнера**(структура подписи доставленного приложения): ```json { vendorName : string // Вендор, тот кто поставляет приложение destinationTarget : string, // мета идентефикатор среды использования. version : int8 // версия приложения userAdr : uint256 // Адрес пользователя, для протокола инициализации, опционально. ttl : //Время жизни экземпляра } ``` **policy неразвернутого приложения сайнера** ```json { } ``` В момент инициализации присваиваится идентефикатор инстанции, подписывемый ключем инстанции. **identity развернутой инстанции сайнера**(структура подписи развернутого приложения): ```json ID = { serviceName: "keyless.one", // Сервис, предостовляющий услугу, удостоверяющий центр userID: "devmanio", // Имя пользователя, уникальное имя пользователя сервиса userInstanceID: "chrome_mac_12" // Инстанция пользователя, инстанция сервиса, в рамках пользователя(home_mac_11/aws_instance_11 итд) EDS: { pubKey : "0x...", alg: "secp256k1" } // публичный ключ и шифросьют для ЭЦП инстанции RSAKey: {pubKey: "0x...", alg: ""} // публичный ключ и шифросьют для шифрования инстанции IPv4 // ipv4 инстанции IPv6 // ipv6 инстанции ttl // Время жизни инстанции(timestamp) } ``` **policy развернутой инстанции сайнера**: ```json { } ``` ### 1. Шифросьюты > ### Шифрование !!!!! > **ТК про современные алгоритмы шифрования я мало что знаю - используем то что первым попадется под руку. Ассиметричное или Симетричное - не важно. Потом пофиксим.** Алгоритмы mac[^mac] шифрования и ЭЦП образуют шифросьют. Иными словами они описывают криптографические алгоритмы применяемые для ЭЦП и/или Шифрования, осущевствляемых сервисом в рамках своей работы. | Кейс | ЭЦП | Шифрование | MAC[^mac] | Сьют | | -------- | -------- | -------- | -------- |-------- | | Геренировани/Проверка транзакции для bitcoin | secp256k1 | - | sha256 | slap_btc | | Геренировани/Проверка транзакции для ethereum | Text | - | Text | | Геренировани/Проверка транзакции для Litning Network | Text | - | Text | | Геренировани/Проверка транзакции для NEO | Text | - | Text | | Геренировани/Проверка транзакции для QTUM | Text | - | Text | | Геренировани/Проверка транзакции для EOS | Text | - | Text | | Геренировани/Проверка эцп для РФ госуслуг | - | - | - | | Геренировани/Проверка эцп для эстонских госуслуг | - | - | - | | Геренировани/Проверка эцп для США госуслуг | - | - | - | ### 2. Объекты системы > Ключ не оч коректное название для данного примитива, но тк оно устоялось - пока будет ключем. Вариант - можно переименовать в сертификат. Важно найти название которое поймут пользователи без вышки. В системе есть два вида ключей: * Ключ инстанции - ключ инстанции приложения, генерируемой для конкретной инстанции приложения(браузера или виртуальной машины), включает в себя как ключ ЭЦП, так и ключ шифрования. > Фактически ключ инстанции может быть TLS сертификатом сервиса с поддержкой ENS. Но тк **a)** Мы можем быть в браузере и **b)** Некогда раскуривать x.509 и TLS - пока это вариант подумать на будущее. * Ключи сервиса - ключи сервисов, генерируемые или импортируемые для сервисов(как внешних, например сервис MPC подписей или Lightning Network нода, так и внутренних - например bitcon кошелек пользователя). включает в себя ключ ЭЦП, ключ шифрования опционален. Ключ сервиса может включать в себя другие ключи(HD wallet, мультичейн сервис). Помимо прочего, ключ содержит набор политик, регламентирующих его использование. (псевдокод) ```rust= //сгенерированныи или импортированный ключ-исходник, содержит приватные ключи generatedKey = { serviceID : uint8 //индекс сервиса, характеризующий // название и шифросьют используемы сервисом userID : bytes[20] //имя пользователя или иной идентефикатор ключа serviceUserHint : string //пользовательское обозначение ключа edsKey : { privKey: string , type: int8 } //type = параметр // характерезующий свойства ключа, например : bip32, bip39, mpc rtc cipherKey : { privKey: string , type: int8 } serviceAPN : uint256 // адрес сервиса ( ip или контракта) ttl : uint64 // timestamp до которого ключем можно пользоватся keys : [...] //массив ключей необходимых для использования сервиса policies : [...] //политики использования сервиса } //подготовленный для подписи ключ, приватные ключи //замененны на публичные unsignedKey = generatedKey - privKey + pubKey + timestamp. //UID ключа = его хэш keyID = serviceCipherSuite.EDS.mac(unsignedKey) //получение класса шифросьюта по ID сервиса, //Этот класс имплементирующий алгоритмы ЭЦП, //Шифрования и Мак(хэширование) serviceCipherSuite = keyless.id.signer.getCipheSuite(unsignedKey.serviceID) //получение подписи. keySignature = serviceCipherSuite.EDS.sign(unsignedKey, key.EDS.PrivKey) //подписанный ключ имеет следующий вид signedKey = unsignedKey + keySignature //если сервис предпологает процедуру регистрации, //те получение подписи на уже подписанный ключ - //после регистрации имеем registredServiceKey registredServiceKey = signedKey + serviceSignature ; } ``` ## 3. Протоколы >#### Для ускорения реализации протоколы описанны с использованием технологии построения rpc сервисов:worried: Apache Thrift[^rpc]. Потенциально ее использование сильно упростит реализацию API слоя - главное понять нет ли проблем с производительностью и безопасностью. Хотя Rust не поддерживается из коробки - есть куча сторонних реализаций в гитхабе. ### 3.1. Протокол запуска инстанции приложения **Вызывается внешним актором.** Данный протокол описывает процедуру запуска экземпляра приложения сайнера, в рамках этого протокола генерируется ключ инстанции. Это интерактивный протокол, те требует присутствия стороны инициирующей запуск сервиса. ```thrift /init/serviceID/instanceID/ttl/sd_adr/signature ``` ### 3.2. Протокол регистрации/обновления регистрации в службе обнаружения сервисов > Служба обнаружения сервисов - это либо отдельный сервис-воркер в браузере, либо отдельная сервис[^SD] в облаке, хранящий конфиг всех доступных сервисов генерации подписи(сайнеров) и доступных ID ключей ими распологаемых. Данный протокол вызывается при генерации или импорте ключей для регистрации новых сервисов и распологаемых ими ключей в этой службе. **Используется только самим сайнером, как этап работы протокола 3, 5, 6 или 7; не предпологает вызов извне(пока).** ### Подумать - может след абзац лишний ? Для понимания: при старте работы сервиса пользователь(обладатель ключа, указанного в подписи экземпляра сайнера) инициирует сайнер: осущевствляет процедуру генерации ключа инстанции, генерирования идентификатора инстанции, ее подпись и регистрации инстанции сайнера в службе обнаружения сервисов[^SD]. > Дальше сайнер работает без прямого вмешательства пользователя. Ключ инстанции осущевствляется только для задач идентификации и безопасного обмена данными между разными инстанциями сервисов сайнера. ```api /create|add/ request = { serviceID : "L2", serviceAdress : uint256 userID : "devmanio", // идентификатор в сервисе, например devmanio userHint : "Savings Default Bitcoin Adress" // Краткое описание edsKeys = [ { serviceID : "bitcoin", serviceAdress : uint256, username : "devmanio", signature : tx }, ... ], instancetx : tx } ``` ### 3.3. Протокол генерации ключей ЭЦП **Вызывается внешним актором.** Сайнер может сгенерировать ключ ЭЦП по запросу пользователя. Сервис подразумевает разные протоколы генерации ключей, как следствие у сгенерированных ключей возникают разные свойства: * BIP39 - подразумевает возможность генерации hd ключей из единого зерна * BIP32 Используя мастер ключ или сущевствующий ключ BIP39 * **TBA** будет дополненно в ближайшее время /gen/bip39/serviceName /gen/bip32/serviceName/Adress/id ```api request = { serviceName : "bitcoin" CiphredPrivateKey : XXXX Policies[] importerSignature : tx } /export/ request = { serviceToExportID : string , // ID сервиса куда экспортировать TTL : // время жизни запроса, экспортировать не позднее чем. edsKeys = [ { serviceName : "bitcoin", publicAdress : uint256 , signature : tx } //подпись запроса с экспортируемого адреса ... ] exporterSignature : tx // Подпись сервиса, запросившего экспорт ``` ### 3.4. Протокол Импорта/Экспорта ключей ЭЦП **Вызывается внешним актором.** Сайнер может запросить или передать ключи другому сервису в зашифрованном виде. Мастер ключ используется для идентификации и шифрования передоваемых ключей сайнера в протоколе импорта/экспорта. ```api /import/ request = { serviceName : "bitcoin" CiphredPrivateKey : XXXX Policies[] importerSignature : tx } /export/ request = { serviceToExportID : string , // ID сервиса куда экспортировать TTL : // время жизни запроса, экспортировать не позднее чем. edsKeys = [ { serviceName : "bitcoin", publicAdress : uint256 , signature : tx } //подпись запроса с экспортируемого адреса ... ] exporterSignature : tx // Подпись сервиса, запросившего экспорт ``` ### 3.5. Протокол генерации ЭЦП(подписи/транзакции) **Вызывается внешним актором.** Основная задача сайнера - осущевствлять сервис ЭЦП по запросу сторонних сервисов. Как дополнительная защита при осущевствлении сервиса ЭЦП сайнер руководствуется политиками, записанными в сертификат. > **Под вопросом?** В рамках этого протокола предусмотрен как интерактивный режим, с выбором ключа, так и автономный(не интеравктивный) режим, возвращающий подпись в ответе на запрос. /sign/service/adress/body [^SD]: В облачной инфраструктуре используем службу обнаружения сервисов Hashicorp Consul https://www.consul.io, браузерного аналога еще нет. Хотя может у Thrift есть встроенное решение - надо исследовать. [^mac]: MAC, message authentication code или алгоритм контроля целостности - в простонароде алгоритм хэширования [^rpc]: Apache Thrift позволяет сильно упростить разработку API сервисов за счет генерации базового кода как сервера так и клиента, у нас нет опыта его использования, но в текущей ситуации можно пойти на риск. Актуальная версия лежит у фейсбука, хотя изначально проект был под эгидой Apache Fundation. Побробнее тут: https://www.ibm.com/developerworks/ru/library/os-cloud-apache-thrift/index.html https://webcache.googleusercontent.com/search?q=cache:Z2dzIdahOeYJ:https://ru.bmstu.wiki/Apache_Thrift+&cd=1&hl=ru&ct=clnk&gl=nl https://ru.wikibooks.org/wiki/Apache_Thrift [^aws_iam]: https://github.com/krishnasrinivas/wikinotes/wiki/IAM