Оригинал: https://medium.com/coinmonks/how-to-secure-messages-on-eos-ebb869a459ea
Блокчейн - это распределенная книга, и каждый блок данных общедоступен для всеобщего обозрения. Возможно, вам интересно, что делать, если вы хотите зашифровать сообщения на блокчейне, в частности, на EOS?
Как обеспечивается безопасность сообщений на Блокчейне
Мы можем использовать NuCypher. NuCypher помогает разработчикам dApp хранить, совместно использовать и управлять личными данными в общедоступных блокчейнах с децентрализованным повторным шифрованием прокси в качестве службы.
Вот как это работает:
- У Алисы есть конфиденциальные данные, на которые она хочет иметь возможность делегировать доступ.
- Алиса шифрует свои данные с помощью собственного публичного ключа и сохраняет его в облаке или децентрализованном хранилище.
- Алиса делегирует доступ к Бобу. Данные переустановлены на ключ Боба в хранилище.
- Боб загружает данные и расшифровывает их своим приватным ключом.
Защита сообщения в EOS
Мы начнем со сценария, в котором у Алисы и Боба есть доступ к приватному ключу, и Алиса хочет отправить важные данные Бобу, а также использовать pyUmbral, который является наилучшей реализацией NuCypher’s для Umbral (split-key threshold - proxy re-encryption).
Давайте сначала создадим очередь сообщений смарт-контрактов на EOS.
Создадим одну таблицу, называемую сообщениями, которая содержит uint64_t msg_id, account_name from, account_name to, строковый зашифрованный текст, и строчную капсулу. msg_id - первичный ключ.
Здесь предусматриваются два действия: sendmsg
и deletemsg
. sendmsg
требует account_name from и to, msg_id, шифртекст и капсулу. Шифрованный текст - это зашифрованное сообщение, а капсула - это концепция в Umbral, которая генерируется с использованием pyUmbral. Deletemsg
в основном принимает msg_id, проверяет to
имя учетной записи, а затем удаляет запись.
public:
using contract::contract;
//@abi action
void sendmsg( account_name from, account_name to, uint64_t msg_id, const std::string & ciphertext, const std::string & capsule) {
require_auth( from );
messages_index messages( _self, _self );
auto itr = messages.find(msg_id);
if (itr != messages.end()) {
std::string error = "msg_id already exists: " + std::to_string(msg_id);
eosio_assert(false, error.c_str());
}
messages.emplace( from, [&](auto& msg){
msg.msg_id = msg_id;
msg.from = from;
msg.to = to;
msg.ciphertext = ciphertext;
msg.capsule = capsule;
});
}
// @abi action
void deletemsg( account_name to, uint64_t msg_id) {
require_auth( to );
messages_index messages(_self, _self);
auto itr = messages.find(msg_id);
if ( itr == messages.end() ) {
std::string error = "msg_id does not exist: " + std::to_string(msg_id);
eosio_assert(false, error.c_str());
}
if ( itr->to != to ) {
std::string error = "Receipient not correct: " + eosio::name{itr->to}.to_string();
eosio_assert( false, error.c_str());
}
messages.erase(itr);
}
private:
//@abi table messages i64
struct messages {
uint64_t msg_id;
account_name from;
account_name to;
std::string ciphertext;
std::string capsule;
uint64_t primary_key() const { return msg_id;}
};
typedef eosio::multi_index<N(messages), messages> messages_index;
};
EOSIO_ABI( queue, (sendmsg)(deletemsg) )
Скомпилируйте его:
eosiocpp -g queue.abi queue.cpp
Создайте учетную запись, чтобы загрузить смарт-контракт:
executed transaction: a767af2c66857... 200 bytes 3309 us
# eosio <= eosio::newaccount {"creator":"eosio","name":"queue","owner":{"threshold":1,"keys":[{"key":"EOS5aEqZf22dfThTR8GGMnD8oFv...
$ cleos set contract queue ../queue
Reading WAST/WASM from ../queue/queue.wasm...
Using already assembled WASM...
Publishing contract...
executed transaction: 38e94741c... 13824 bytes 9561 us
# eosio <= eosio::setcode {"account":"queue","vmtype":0,"vmversion":0,"code":"00617ee7e...
# eosio <= eosio::setabi {"account":"queue","abi":"0e656f73696f3a3a6162692f9640675...
Создайте тестовые учетные записи Алисе и Бобу:
executed transaction: f0c42065f6d9fc... 200 bytes 243 us
# eosio <= eosio::newaccount {"creator":"eosio","name":"alice","owner":{"threshold":1,"keys":[{"key":"EOS6NU3XEvosgRVEbhrBHrkbYVt...
$ cleos create account eosio bob EOS7cX17CZ8V7yFobaVejAN7sMG39iiC5BmFk7b1NB1NNYcrEu1Go
executed transaction: 51d45916fa252e... 200 bytes 194 us
# eosio <= eosio::newaccount {"creator":"eosio","name":"bob","owner":{"threshold":1,"keys":[{"key":"EOS7cX17CZ8V7yFobaVejAN7sMG39...
Давайте перейдем к созданию шифрования / дешифрования сообщения и взаимодействия со смарт-контрактом для пользователя.
- Создайте приватный ключ и запишите в файл.
config.set_default_curve(SECP256K1)
private_key = keys.UmbralPrivateKey.gen_key()
f = open('priv.key', 'wb')
f.write(private_key.to_bytes())
f.close()
- Сконструируйте синтаксический анализатор.
parser = argparse.ArgumentParser(description = 'messenger')
parser.add_argument('--private-key-file', type = str, dest = 'private_key_file', required = False, help = 'Path to the private key file.')
parser.add_argument('--send-msg-id', type = str, dest = 'send_msg_id', required = False, help = 'Send a message, msg_id')
parser.add_argument('--send-msg-from', type = str, dest = 'send_msg_from', required = False, help = 'Send a message, from which EOS account')
parser.add_argument('--send-msg-to', type = str, dest = 'send_msg_to', required = False, help = 'Send a message, to which EOS account')
parser.add_argument('--send-msg', type = str, dest = 'send_msg', required = False, help = 'Message to be sent')
parser.add_argument('--read-msg-to', type = str, dest = 'read_msg_to', required = False, help = 'Read a message, to which EOS account')
return parser
parser = create_parser()
args = parser.parse_args(argv)
- Прочтите файл privkey.
if os.path.exists(privkey_file):
with open(privkey_file, 'rb') as f:
try:
privkey = f.read()
except Exception as e:
print("Cannot read: {}".format(e))
sys.exit(1)
f.close()
return privkey
else:
print('Cannot find file: {}'.format(privkey_file))
sys.exit(1)
- Зашифруйте сообщение и отправьте его.
privkey_bytes = read_privkey_file(private_key_file)
privkey = keys.UmbralPrivateKey.from_bytes(privkey_bytes)
pubkey = privkey.get_pubkey()
plaintext = msg.encode()
ciphertext, capsule = pre.encrypt(pubkey, plaintext)
data = '{"from":"' + send_msg_from + '", "to":"' + send_msg_to + '", "msg_id":"' + send_msg_id + '", "ciphertext": "' + ciphertext.hex() + '", "capsule": "' + capsule.to_bytes().hex() + '"}'
subprocess.call(['cleos', 'push', 'action', DEFAULT_ACCOUNT, 'sendmsg', data , '-p', send_msg_from])
- Прочитайте из
messages
таблицы, еслиto
совпаденияread_msg_to
, расшифруйте сообщение и затем удалите его.
privkey_bytes = read_privkey_file(private_key_file)
privkey = keys.UmbralPrivateKey.from_bytes(privkey_bytes)
payload = '{"scope":"' + DEFAULT_ACCOUNT + '","code":"' + DEFAULT_ACCOUNT + '","table": "' + DEFAULT_TABLE+ '", "json":"true"}'
response = requests.request("POST", DEFAULT_URL, data=payload)
found = False
for msg in response.json()['rows']:
if msg['to'] == read_msg_to:
ciphertext = msg['ciphertext']
capsule = msg['capsule']
msg_id = msg['msg_id']
found = True
break
if found:
capule = pre.Capsule.from_bytes(bytes.fromhex(capsule), privkey.params)
cleartext = pre.decrypt(
ciphertext = bytes.fromhex(ciphertext),
capsule = capule,
decrypting_key = privkey)
print('Cleartext: {}'.format(cleartext))
print('Deleting msg_id: {}'.format(msg_id))
data = '{"to":"' + read_msg_to + '", "msg_id":"' + str(msg_id) + '"}'
subprocess.call(['cleos', 'push', 'action', DEFAULT_ACCOUNT, 'deletemsg', data , '-p', read_msg_to])
- Значения по умолчанию.
DEFAULT_TABLE = 'messages'
DEFAULT_URL= "http://127.0.0.1:8888/v1/chain/get_table_rows"
Давайте завершим их и протестируем!
executed transaction: dfe17144e105c54d192... 392 bytes 648 us
# queue <= queue::sendmsg {"from":"alice","to":"bob","msg_id":1,"ciphertext":"8c53a656c9...
Проверьте messages
таблицу.
{
"rows": [{
"msg_id": 1,
"from": "alice",
"to": "bob",
"ciphertext": "8c8aa24196152b53da35d9fbf9e3c4d8e10f6b7153a656c9921cb440cc69a782c2ba0c2cf2",
"capsule": "028c4e8279c0919bdec4ea98b4251f15a74868d2ea7554ab796230af8f88e62cd9031c07e90c6183152e140c43a370f2c12526b6d62269f8673a35e6a19fc6ff78ac2f3a28dbed868858ec71228644f277c8ecfb691aa41dab863072e6f39c55d294"
}
],
"more": false
}
Зашифрованное сообщение есть. Тогда давайте прочитаем.
Cleartext: b'hello,bob'
Deleting msg_id: 1
executed transaction: 6cdad7694f3fe6c8...112 bytes 566 us
# queue <= queue::deletemsg {"to":"bob","msg_id":1}
Проверьте messages
таблицу, сообщение исчезло.
{
"rows": [],
"more": false
}
Таким образом, мы изучили, как защищать сообщения от EOS. Двигаясь вперед, мы можем работать над сценарием, где у Алисы и Боба есть свои собственные ключи, или отправлять сообщения нескольким пользователям.
Перевод CryptoLions