Poco has crypto module and here is a tutorial of cryptography using Poco.
Symmetric encryption is encrypt and decrypt using a key known by two player. Simple but hard to make key to be safe.
#include "Poco/Crypto/Cipher.h"
#include "Poco/Crypto/CipherKey.h"
#include "Poco/Crypto/CipherFactory.h"
auto& factory = CipherFactory::defaultFactory();
string transmission;
{
Cipher* alice = factory.createCipher( CipherKey(
"aes-256-ecb", "open sesame"));
transmission = alice->encryptString(
"Tall tree, Spy-glass shoulder, ... Skeleton Island ESE and by E");
}
{
Cipher* bob = factory.createCipher( CipherKey(
"aes-256-ecb", "open sesame"));
string decrypted = bob->decryptString(transmission);
assert(decrypted ==
"Tall tree, Spy-glass shoulder, ... Skeleton Island ESE and by E");
}
To remove this unsafe key, asymmetric encryption can be used. To do it, first priavate and public key should be generated. OpenSSL can be used like below.
>openssl.exe genrsa -des3 -out private_bob.pem 2048
Generating RSA private key, 2048 bit long modulus
...................+++
.................................................................+++
unable to write 'random state'
e is 65537 (0x10001)
Enter pass phrase for private_bob.pem:
Verifying - Enter pass phrase for private_bob.pem:
>openssl.exe rsa -in private_bob.pem -outform PEM -pubout -out public_bob.pem
Enter pass phrase for private_bob.pem:
writing RSA key
Then Alice can encode message using Bob's public key. Then only Bob can decode it using his private key.
#include "Poco/Crypto/RSAKey.h"
#include "Poco/Crypto/RSADigestEngine.h“
auto& factory = CipherFactory::defaultFactory();
string transmission;
{
Cipher* alice = factory.createCipher(RSAKey( "public_bob.pem"));
transmission = alice->encryptString(
"Tall tree, Spy-glass shoulder, ... Skeleton Island ESE and by E");
}
{
Cipher* bob = factory.createCipher(RSAKey( "", "private_bob.pem", "bobbob"));
string decrypted = bob->decryptString(transmission);
assert(decrypted == "Tall tree, Spy-glass shoulder, ... Skeleton Island ESE and by E");
}
The assymetric encryption can be extended to make contract between two players which is called digital signature.
Here, Alice can verify Bob's digital signature with the contract he promised.
// Poco::DigestEngine::Digest is std::vector
tuple transmission;
{
string contract =
"Alice will give Bob $100 and Bob will give Alice two magic mushrooms";
RSADigestEngine bob(RSAKey("", "private_bob.pem", "bobbob"));
bob.update(contract);
transmission = make_tuple(contract, bob.signature());
}
{
RSADigestEngine alice(RSAKey("public_bob.pem"));
alice.update(get<0>(transmission));
bool isValid = alice.verify(get<1>(transmission));
assert(isValid == true);
}
Imagine, Alice get greedy and forged the contract, then Bob's signature doesn't work anymore.
// Poco::DigestEngine::Digest is std::vector
tuple transmission;
{
string contract =
"Alice will give Bob $100 and Bob will give Alice two magic mushrooms";
RSADigestEngine bob(RSAKey("", "private_bob.pem", "bobbob"));
bob.update(contract);
transmission = make_tuple(contract, bob.signature());
}
{
RSADigestEngine alice(RSAKey("public_bob.pem"));
alice.update(
"Alice will give Bob $100 and Bob will give Alice three magic mushrooms");
bool isValid = alice.verify(get<1>(transmission));
assert(isValid == false);
}
This digital signature can be extended further to digital coin. First chained transaction should be established. Here is a diagram from Satoshi Nakamoto's paper which shows the concept.
Here, Queen generate coin and give it to Alice; Queen use Alice's public key to generate transaction hash and sign the hash using majesty private key.
struct Transaction
{
string Hash;
vector Signature;
};
Transaction queenToAlice;
{
// Queen
RSAKey alicePublic("public_alice.pem");
stringstream alicePublickey;
alicePublic.save(&alicePublickey);
Crypto::DigestEngine hasher("SHA256");
hasher.update("Queen's own right to create coin");
hasher.update(alicePublickey.str());
string hash = Crypto::DigestEngine::digestToHex(
hasher.digest());
RSADigestEngine queen(RSAKey("", "private_queen.pem", "queenqueen"));
queen.update(hash);
queenToAlice = Transaction{ hash, queen.signature() };
}
Now, Alice can verify Queen's benevolence using Queen's public key.
{
// Alice
RSADigestEngine alice(RSAKey("public_queen.pem"));
alice.update( queenToAlice.Hash );
assert( true == alice.verify(queenToAlice.Signature));
}
Then Alice send the coin to Bob; Update the transaction 's hash with Bob's public key and sign it with Alice's private key.
Transaction aliceToBob;
{
// Alice
RSAKey bobPublic("public_bob.pem");
stringstream bobPublickey;
bobPublic.save(&bobPublickey);
Crypto::DigestEngine hasher("SHA256");
hasher.update(queenToAlice.Hash);
hasher.update(queenToAlice.Signature.data(),
queenToAlice.Signature.size());
hasher.update(bobPublickey.str());
string hash = Crypto::DigestEngine::digestToHex(
hasher.digest());
RSADigestEngine alice(RSAKey("",
"private_alice.pem", "alicealice"));
alice.update(hash);
aliceToBob = Transaction{ hash, alice.signature() };
}
And Bob can verfiy Alice's coin given to himself.
{
// Bob
RSADigestEngine bob(RSAKey("public_alice.pem"));
bob.update(aliceToBob.Hash);
assert(true == bob.verify(aliceToBob.Signature));
}
The genuine idea of Bitcoin is BlockChain which verify the transaction in decentralized way. Here is another excerpt from the paper.
It says there should be proof-of-work ; which is basically finding a additional string - nounce - that make hash to start with number of '0' characters. Finding nounce is hard but verifying it is easy. Here is how to verify nounce.
struct Transaction { string Hash, Signature; };
struct Block {
string PrevHash, Nounce;
vector Transactions;
};
Block t0 = {
"Hello, world!", "4250", { Transaction{ "", "" } }
};
// verfiy t0 block's transactions and proof-of-work
hasher.update(t0.PrevHash);
for (auto tx : t0.Transactions) {
hasher.update(tx.Hash); hasher.update(tx.Signature);
}
hasher.update(t0.Nounce);
t1.PrevHash = Crypto::DigestEngine::digestToHex(hasher.digest());
assert(t1.PrevHash.substr(0, 4) == "0000");
Here is a way to finding nounce by brute-force.
// gather transactions
t1.Transactions = {
Transaction{ "ab", "cd" },
Transaction{ "12", "34" },
Transaction{ "56", "78" } };
// race for the nounce
for (int nounce = 0;; ++nounce)
{
hasher.reset();
hasher.update(t1.PrevHash);
for (auto tx : t1.Transactions) {
hasher.update(tx.Hash); hasher.update(tx.Signature);
}
hasher.update(to_string(nounce));
string hash = Crypto::DigestEngine::digestToHex(hasher.digest());
if (hash.substr(0, 4) == "0000") {
t1.Nounce = to_string(nounce);
break;
}
}
// broadcast t1 block to world