Chapter 4
Cryptography

Initial version: 2024-02-14
Last update: 2024-04-10

Hashing is the way to identify and distinguish data. That was a topic discussed in previous chapter. However with hash there is no way to prove that the data you say is yours, is really yours. The tool you need to complete this is a cryptography.

In this chapter you will get basic knowledge related to cryptography and acquire practical skills how to use it.

Table of contents


A short introduction to cryptography


Basic terminology


Before I dive into cryptography practical part I want to clarify terminology related to this topic. If you are familiar with this topic you can skip this section and jump right now to practice.

In cryptography a plain text is a text which you can read without any problems, a text without any transformation, changes and obscure procedures. Simply speaking a text you write on a paper with a pen, on a computer with a keyboard or any other "classical" way. Anybody can read it, and if only it is written in some common language, anybody can understand it. What is worth you to note is a word "text". In cryptography anything you process is called a text, is treated as a text. In computer science we do exactly the same, however you treat everything as a sequence of bytes; particularly sequence of text is also a sequence of bytes. Even though bytes are used instead of letters the name persists and you call any data you want to process a plain text.

The main goal of cryptography is to turn plain text into incomprehensible sequence of characters so nobody, even a text creator, can read it or infer what is hidden behind the random-like mass of characters. This process is called encryption. Data after applying to it encryption procedure is called encrypted data or cypher text. The reverse process is called decryption resulting decrypted data.

Decrypted cypher text is identical and indistinguishable to the original data that have been encrypted. What is important in modern cryptography, even if you know the procedure which was used by someone to make this transformation from something readable to something unreadable, you shouldn't be able to get plain text as long as you don't provide some secret data – the password or more generally: the (cryptographic) key.

If you put all of this together you will get the following view on encryption-decryption cycle:

  1. You begin with some data.
  2. You use encryption to encrypt your original data with a key and produce cypher text.
  3. Now it is safe to store your data, send it or do whatever you want.
  4. To recover original data from encrypted data you use decryption to decrypt it with a key and produce plain text.



[IMG: encryption_decryption]


Symmetric cryptography


For centuries people utilized methods of cryptography where the identical key is used to do both the encrypting and decrypting of data. Everyone who is able to encrypt data with a key is automatically able to decrypt cypher text created with that key as well. Since the identical key is used for both processes, this method is called symmetric cryptography.


[IMG: cryptography_symmetric]
The main problem with symmetric cryptography is related to key distribution. Because you use the same key to encrypt and decrypt data, the key point is how to pass the key so recipient may restore plain text. You have to keep the key in secret because anybody who poses it has a full access to your data: may read them then change them, encrypt again and send it pretending to be you.

Asymmetric cryptography


In asymmetric cryptography you always use two different keys, we say two complementary keys. Complementary because they are complement: cypher text created with one of these keys can only be decrypted with the other key and vice versa. You can never decrypt cypher text with the key that was used to create it.


[IMG: cryptography_asymmetric]
The decision on which key to use for encryption and which to use for decryption is up to you. You can switch the roles of the keys as you like for every new piece of data you want to encrypt, but you always have to keep both keys for doing both encryption and decryption.

If you have only one of the keys, you can:
  • create cypher text by applying your key to plain text (however you can not decrypt it);
  • decrypt cypher text by applying your key to encrypted data that was created with the corresponding complementary key.
Asymmetry in this type of cryptography has one undeniable advantage: you don't have to keep both keys, as a pair, secret. You have to keep one of them in secret while the other may be freely distributed to anybody. It allows you to separate the group of people who are able to create cypher text from those who can decrypt it. What is more important, they don't have to know each other. Thanks to this key maintenance simplifies a lot.

Creating and distributing the keys


As I wrote at the end of previous section, when using asymmetric cryptography you have to keep one of them in secret while the other may be freely distributed to anybody. To highlight this feature you give the two keys specific names: typically these keys are called the private key and public key. For that reason, asymmetric cryptography is called public-private-key cryptography. However, from theoretical point of view, there are no such things as the private key and public key in asymmetric cryptography per se because it is up to you which of them you designate to each role – encrypting and decrypting (however, I will stress it once again, you should be consequent: key for encrypting can not be used for decrypting). It is the role that you assign to these keys that makes them private or public. You may give the public key to everyone, regardless of their trustworthiness. However the private key you should hide deep in your pocket and never ever show, even to your best friend.

I can summary this public-private roles of the keys as follow:

  1. Create a pair of two complementary keys.
  2. Give one of the keys the name public key.
  3. Give the other, complement key, the name private key.
  4. Keep the private key hidden and to be used ONLY by you.
  5. Give your public key to everyone else.


Ways you use private and public key


The ways you use private and public key very often are not discussed openly as they are a consequence of roles assigned to each key, however it is good to realize this as it systemize what can be done, when and by whom.

There are two general ways (I will refer them as modes) you can use the pair of keys, which differ in the direction to which the data flows:
  • public to private mode,
  • private to public mode.
Public to private mode

In this mode flow od data and keys usage resembles letters and mailboxes. Everyone who know your address can send you a letter, everyone can put letter into your mailbox, but only you – the owner – can open the mailbox and take out the letters.

By using the keys in this way, the information flows from the public key, where it is encrypted, to the private key, where it is decrypted. Everyone can create cypher text with your public key you freely distribute, but only you – the owner of the private key – can decrypt the cypher text and read the message.

Private to public mode

In this mode flow od data and keys usage resembles web page with tutorials and articles published on it. Everyone who know address of my web page can read freely contents published on it, but only I – the owner – can change this contents.

By using the keys in this way, the information flows from the private key, where it is encrypted, to the public key, where it is decrypted. Only the owner of the private key can create cypher text with its private key, but everyone can decrypt the cypher text and read the message using owner's public key it freely distributed.

If you look into this mode carefully you will come to an important conclusion, from the way how blockchain works, that using asymmetric cryptography in a private to public mode one can prove authorship. The fact that having public key one can only decrypt cypher text created with the corresponding private key serves as proof that the owner of this private key has encrypted the message as it is the only person who has an access to this key.

The practical side of cryptography


Symmetric cryptography


Import required libraries:


import nacl.secret
import nacl.utils
Importing nacl you will use PyNaCl which is a Python binding to libsodium – a fork of the Networking and Cryptography library. These libraries have a stated goal of improving usability, security and speed.

First you need a key:


messageText = "..."
messageTextBytes = messageText.encode('utf-8')

key = nacl.utils.random(nacl.secret.SecretBox.KEY_SIZE)
Both you (sender) and recipient must use the same key. It's your responsibility to transfer the key to correct person and be sure that only both of you use it. Symmetric cryptography doesn't provide any method to control this process.


Now you can encrypt your message:


box = nacl.secret.SecretBox(key)
encrypted = box.encrypt(messageTextBytes) 

# or use explicit nonce
# nonce = nacl.utils.random(nacl.secret.SecretBox.NONCE_SIZE)
# encrypted = box.encrypt(message, nonce)
As I mentioned in a previous part, salt (nonce) is not considered secret and may be freely transmitted or stored in plaintext alongside the cipher text (see: chapter 3: Data fingerprinting, section: Salting hashes). A nonce does not need to be random or unpredictable, nor does the method of generating them need to be secret but should be unique – you should never reuse it. A nonce could simply be a counter incremented with each message encrypted.


Regardless of how the nonce was generated, recipient decrypts message the same way:


boxDecrypt = nacl.secret.SecretBox(key)
plainTextBytes = boxDecrypt.decrypt(encrypted)
plainText = plainTextBytes.decode('utf-8')
Below you have all steps put together:


import nacl.secret
import nacl.utils

messageText = "Test text"
messageTextBytes = messageText.encode('utf-8')

key = nacl.utils.random(nacl.secret.SecretBox.KEY_SIZE)

box = nacl.secret.SecretBox(key)
nonce = nacl.utils.random(nacl.secret.SecretBox.NONCE_SIZE)
encrypted = box.encrypt(messageTextBytes, nonce)

boxDecrypt = nacl.secret.SecretBox(key)
plainTextBytes = boxDecrypt.decrypt(encrypted)
plainText = plainTextBytes.decode('utf-8')

print(nacl.secret.SecretBox.NONCE_SIZE)
print(nonce.hex())
print(encrypted.hex())
print(plainText)
After execution you will see:


24
34631ffab7027fac9d5f49f0432aeb9abd9cefeb799dbf95
34631ffab7027fac9d5f49f0432aeb9abd9cefeb799dbf959c0ae6a2e2ad3000e3ecb3b36253fe5ae397d705aaa51bab37
Test text
Please note the initial part of encypted data – as you can see, it is exactly the nonce: 346...f95 and only after this sequence there is the actual encrypted data 9c0...b37.

Asymmetric cryptography


Asymmetric cryptography in a public to private way: many can create a message but only one can read it.

The flow of actions in this case is as follow:
  1. Import required libraries.
  2. Generate a pair of keys.
  3. Every person who has your public key may now encrypt a text and send it to you.
  4. Decrypt every message you receive with your private key.
Import required libraries:


from nacl.public import PrivateKey, SealedBox
Generate a pair of keys:


keyPrivate = PrivateKey.generate()
keyPublic = keyPrivate.public_key
and publish your public key. Now anyone with the public key can encrypt the message and send it to you (the owner of singlePersonKeyPrivate):


messageText = "Test text"
messageTextBytes = messageText.encode('utf-8')

box = SealedBox(keyPublic)
encrypted = box.encrypt(messageTextBytes)
The only person who may decrypt cipher text is the owner of singlePersonKeyPrivate:


boxDecrypt = SealedBox(keyPrivate)
plainTextBytes = boxDecrypt.decrypt(encrypted)
plainText = plainTextBytes.decode('utf-8')
print(plainText)
After execution you will see:


Test text
By design the sender is not able to decrypt the cipher text it creates. Only the recipient can decrypt these messages, using its private key. The recipient, in turn, have no means to trace the cipher text to a known author – the recipient can verify the integrity of the message, it cannot verify the identity of the sender.

Sealed boxes are designed to anonymously send messages to a recipient given its public key.

Asymmetric cryptography in a private to public way: one can create a message but many can read it.

The flow of actions in this case is as follow:
  1. Import required libraries.
  2. Generate a pair of keys.
  3. Encrypt your message using your private key.
  4. Every person who has your public key may now decrypt your cipher text.
Unfortunately in PyNaCl there is no option to work according to this schema. If you try to encrypt your data with your private key:


import nacl.encoding
from nacl.public import PrivateKey, SealedBox

# Generate keys
keyPrivate = PrivateKey.generate()
keyPublic = keyPrivate.public_key


messageText = "Test text"
messageTextBytes = messageText.encode('utf-8')

# Encrypt text
box = SealedBox(keyPrivate)
encrypted = box.encrypt(messageTextBytes)

# Decrypt text
boxDecrypt = SealedBox(singlePersonKeyPublic)
plainTextByte = boxDecrypt.decrypt(encrypted)
plainText = plainTextByte.decode('utf-8')

print(plainText)
you will get an error during decryption:


TypeError                                 Traceback (most recent call last)
 in ()
      1 boxDecrypt = SealedBox(singlePersonKeyPublic)
----> 2 plainTextByte = boxDecrypt.decrypt(encrypted)
      3 plainText = plainTextByte.decode('utf-8')
[... cut ...]

TypeError: SealedBoxes created with a public key cannot decrypt


Mutual authentication with asymmetric cryptography


Python's PyNaCl library offers you a way for mutual authentication.

Import required libraries:


from nacl.public import PrivateKey, Box
First person generates its private key, which must be kept secret and public key which can be given to anyone wishing to send this person an encrypted message:


keyPrivateFirst = PrivateKey.generate()
keyPublicFirst = keyPrivateFirst.public_key
Other person does the same as the first person:


keyPrivateSecond = PrivateKey.generate()
keyPublicSecond = keyPrivateSecond.public_key
Now both persons exchange public keys.

The person who wants to send an encrypted message must make a Box with its private key and other person's public key (here first person sends message to second):


box = Box(keyPrivateFirst, keyPublicSecond)
encrypted = bobs_box.encrypt("Secret message".encode('utf-8'))
Recipient creates a second box with Recipient's private key and sender's public and decrypt the message:


boxRecipient = Box(keyPrivateSecond, keyPublicFirst)
plainTextBytes = boxRecipient.decrypt(encrypted)
plainText = plainTextBytes.decode('utf-8')


How blockchain uses asymmetric cryptography


The blockchain uses asymmetric cryptography in order to achieve two goals:
  • identify accounts,
  • authorize transactions.
Identify accounts

From one side the blockchain needs to uniquely identify users while from the other some kind of privacy level is required so no one could map blockchain users to real persons. At least you want to be able to prove that gold you pay for is yours, but you don't want other people to know how much gold (and all other goods) you have.

These are two opposite demands and the blockchain uses the public-to-private approach of asymmetric cryptography to fulfil them anyway. Account numbers in the blockchain are actually public cryptographic keys. During transaction people use the public cryptographic keys for identifying the accounts involved in the transfer of ownership. You can easily prove that an account is yours because you have a complementary key, the private key. On the other side there is no way to associate person with an account.

This situation is very similar to situation when people send mail to post office box. They know the address, which is publicly available, but they don't know the person who will receive it. On the other hand recipient, whoever it is and if only permitted, can easily take out all letters and read them.

Authorize transactions

Any transaction data always have to include data that serves as proof that the owner of the account who transfers ownership indeed agrees with it. This type of information flow agrees with the private-to-public approach of asymmetric cryptography. The owner of the account specify what and to whom it transfer and then it protect such a declaration with its private key. All others can verify this proof of agreement by using the corresponding public cryptographic key.

The details of this procedure, which is called digital signature, will be explained in more detail in the next chapter to identify accounts and authorize transactions.

Summary


In this chapter you learned basic terminology related to cryptography. You know also how to use it in practice. In result you know one another tool which is indispensable implementing blockchain.