Public Key Cryptography

Symmetric key cryptography

Symmetric key cryptography is a type of security protocol whereby the sender and receiver of encrypted data share the same cryptographic key for both encryption and decryption. The sender utilizes the key to encrypt the message before sending it, and the receiver then uses the same key to decrypt the message, thus returning it to its original form. This enables secure communication to take place over an otherwise unsecured channel.

A simple encryption algorithm could involve shifting each character in a string by one position in the alphabet. For instance, if the string is "abc", the encrypted string would be "bcd". Unfortunately, this encryption method is easy to crack, especially once its secret is discovered. Therefore, it is not recommended to use this algorithm when attempting to secure sensitive data.

The Caesar cypher is an example of a type of encryption that uses a secret key to encode messages. This is done by shifting the characters in the message by the number that the secret key denotes. For instance, if the secret key is "321", the word "Bob" will be encrypted to "Eqc". To decrypt the message, the recipient shifts the characters back by the same key, thus revealing the original message.

One major disadvantage of symmetric key cryptography is that both parties must have the secret key beforehand to communicate securely. This can be especially difficult when attempting to communicate with a stranger over the Internet since it is difficult to securely send the key without the risk of it being intercepted. However, if it is possible to meet up with the other party beforehand, then the keys can be exchanged in person, eliminating the possibility of interception.

Asymmetric key cryptography

Asymmetric key cryptography, otherwise known as public key cryptography, is a form of cryptography that requires two distinct keys for the encryption and decryption of data. Unlike symmetric key cryptography, which uses one key for both encryption and decryption, asymmetric key cryptography utilizes two separate keys: a public key and a private key. The public key is made available to the public, and the private key is kept secure and confidential. By utilizing two different keys, asymmetric key cryptography ensures secure communication between two parties and provides greater security than symmetric key cryptography.

First use case: public key encryption

Asymmetric key cryptography is a method of securely transmitting data between two parties. The sender encrypts the message using the receiver's public key, so only the receiver can decrypt it using their private key. This ensures that the receiver is the only one who can read the message, even if the public key is known. Even if someone intercepts this message, they cannot read it, as the data is encrypted. This provides a secure way of communication that cannot be accessed by anyone other than the intended recipient.

Second use case: public key authentication

Public key authentication is a secure form of authentication that utilizes asymmetric key cryptography. It requires using two distinct keys, a public key and a private key, to verify and authenticate the user's identity. The public key is disseminated to the public and encodes messages, whereas the private key is kept secret and is used to decrypt messages. Using this methodology ensures that only the intended recipient can read the message's contents.

In this instance, Alice has employed her private key to sign a financial transaction, thereby authorizing it digitally. The resulting signed transaction is then propagated throughout the blockchain network, enabling the blockchain nodes to extract Alice's address from the linked signature. This process demonstrates the importance of private keys, as they are necessary for digitally signing and verifying documents and authorizing financial transactions.

How to sign a message?

Hash the message

The first step in using a private key to sign a message, such as "Approve for this change", is to take a cryptographic hash of the message. This is done prior to applying the signature algorithm. The output of the hashing process is a unique value known as a message digest, which is then used as input to the signature algorithm.

Once we have the message hash, we can use our private key to sign it, demonstrating that a particular address has voted yes for this change. This process is similar to signing a message through web3 by connecting your wallet and signing the message or approving a transaction. In the latter case, a hashed transaction representation is signed before it is sent to the blockchain node.

By using the ethereum-cryptography library, it provides a wide range of cryptographic functions. For instance, we can use the keccak256 hash and utf8ToBytes function to generate a message hash.

const { keccak256 } = require('ethereum-cryptography/keccak')
const { utf8ToBytes } = require('ethereum-cryptography/utils')

function hashMessage(message) {
  const bytes = utf8ToBytes(message)
  const hash = keccak256(bytes)
  return hash
}

Sign message

After that, we can take the message we would like to sign and use our private key to create an encrypted signature which can be used to prove the authenticity of the message. This is done by taking the message and then applying a mathematical algorithm to it, with the private key serving as the input to the algorithm. The result is an encrypted signature that can prove the message was signed with the associated private key.

const secp = require('ethereum-cryptography/secp256k1')

const PRIVATE_KEY = '05cf1f9c1e4cdcb6702ed2c978d55beff5e178b206b4ec7935aa18d77ad5e056'

async function signMessage(msg) {
  const messageHash = hashMessage(msg)
  const signature = secp.sign(messageHash, PRIVATE_KEY, { recovered: true })
  return signature
}

Recover the public key and user's address

The signature signs the hash of the transaction, which is sufficient to authenticate the action fully. Once the signature is provided with all of its components, the public key can be recovered. This implies that blockchain nodes can identify the signatory of the transaction sent to them. The blockchain network then validates this signature, ensuring that the transaction is legitimate and the signatory is authorized.

Finally, we use the public key to generate a specific address. It is essential to understand that the address is distinct from the public key, yet the address can always be derived from the public key. Bitcoin's address is created by incorporating a checksum and Base58 encoding, whereas Ethereum's address is derived from the last 20 bytes of the hash of the public key.

const secp = require('ethereum-cryptography/secp256k1')
const { keccak256 } = require('ethereum-cryptography/keccak')

async function recoverKey(message, signature, recoveryBit) {
  const messageHash = hashMessage(message)
  const publicKey = secp.recoverPublicKey(messageHash, signature, recoveryBit)
  return publicKey
}

function getAddress(publicKey) {
  const address = keccak256(publicKey.slice(1)).slice(-20)
  return address
}