Normally steem memos are sent as plain text, this article describes how to format your memos for encryption.
CLI Wallet
When using the cli wallet, if you include '#' as the first character of your memo will be encrypted.
unlocked >>> transfer from to "1.000 STEEM" "#encrypted memo" false
{
"operations": [[ "transfer",{
"from": "steemit",
"to": "steem",
"amount": "1.000 STEEM",
"memo": "#CqojqJfFwawzgnYSFUWokj...xAfynekJtySMsqK8A9doD4gHA5fDKgQTEfUKZgAm8Dx"
} ] ]
}
When the wallet is unlocked and get_account_history is called, the "memo" field will be replaced with "encrypted memo" without the initial '#'. This means that exchanges that upgrade to the latest CLI wallet can transparently support encrypted memos.
Algorithm
The algorithm for encrypting the memo involves the following steps:
1. Generate a memo_data struct containing:
public_key from
public_key to
uint64_t nonce
uint32_t check
vector<char> encrypted
- Calculate the AES encryption key as:
shared_secret = from_private_key.get_shared_secret( to );
/// concatenate nonce and shared secret (binary)
encryption_key = sha512( nonce + shared_secret )
///< check is first 64 bit of sha256 hash treated as uint64_t truncated to 32 bits.
check = sha256( encryption_key )._hash[0]
/// pack the memo as a length-prefixed string, length is serialized as varint
plain_text = pack( memo_text)
encrypted = aes_encrypt( encryption_key, plain_text )
- Serialize memo_data
Now convert the memo data into a vector and then convert it to base58
string result = '#' + to_base58( pack( memodata ) );
The to and from keys should be fetched as the memo_key property on the respective accounts.
This is a random nonce with a time-based component. It returns a unique 64 bit unsigned number string.
uniqueNonce() { if(unique_nonce_entropy === null) { const b = secureRandom.randomUint8Array(2) unique_nonce_entropy = parseInt(b[0] << 8 | b[1], 10) } let long = Long.fromNumber(Date.now()) const entropy = ++unique_nonce_entropy % 0xFFFF long = long.shiftLeft(16).or(Long.fromNumber(entropy)); return long.toString() } let unique_nonce_entropy = null
npm libraries used:
long
andsecure-random
The purpose of the 'nonce' field is to generate a unique encryption key for every transfer between two accounts. I it should be a random number.
Is this working properly? As far as I can tell the same key is used for every transfer. See test (both used plaintext of "abc"): https://steemd.com/@aaa