Sunday, June 12, 2022

Introduction to Digital Signature for the Working Developer

In this post, we would be looking at digital signatures. It will be a quick, straight-to-the-point overview of some of the essential things a developer should know about digital signatures. How to think about them and what problems they solve. It is targeted at the working developer who needs to be familiar enough with digital signatures to use them, but who does not need to know the gory details of how they are implemented, or how they work internally.

This post contains the following sections: 

  1. What is a digital signature 
  2. Components of Digital Signature Schemes 
  3. Digital Signature Algorithms 
  4. Last note: Digital Signatures versus Message Authenticated Codes.

What is a digital signature

Signatures in real life are used to ascertain that a piece of document originated and is authorized by the signer.

Similarly, in the digital world, digital signatures are cryptographic primitives used for the same purpose. That is, to ascertain that some digital data came from, and has been authorized by an entity that claims to have originated it.

The digital data could be in the form of an email, a PDF document, blockchain transactions, etc. the point of digital signature remains the same: ascertain that some digital data is legit by ascertaining it originated from the right origin. This function is called origin authentication.

Apart from origin authentication, digital signatures can also be used to ascertain that the data signed has not been tampered with. This is called message authentication or Integrity.

It is also worth mentioning that digital signatures are cryptographic primitives that fall under the asymmetric cryptography category because they make use of two key pairs: private and public keys.

Let's now take a look at the components of a digital signature scheme.

Components of Digital Signature Schemes

Digital signature schemes are usually made up of three distinct components or algorithms:

  • Key generation: This component of the scheme consists of the algorithm that takes care of generating the key pair (private/public) that the signer needs to generate the digital signature.
  • Signing Algorithm: This component is the actual algorithm that generates the digital signature. It takes the message to be signed and uses the private key generated by the key generation algorithm, to create a digital signature.
  • Verifying Algorithm: This checks the authenticity of digital signatures. It takes the public key generated via a key generation algorithm, together with the signature, generated by the signing algorithm, and returns either success if the signature is valid or an error if invalid.

Having listed the three main broad components of digital signatures, let's now take a look at some of the popularly known digital signature schemes. For completeness' sake, it would also mention schemes that were once used but are no longer recommended due to security reasons.

Digital Signature Algorithms.


RSA (RSA-PKCS1-v1_5 and RSA-PSS)

RSA (Ron Rivest, Adi Shamir, and Leonard Adleman - last names of the cryptographers who proposed the scheme) was the first digital signatures scheme to be articulated in 1977.

RSA was introduced as a scheme for encryption in Introduction to Asymmetric Encryption for the Working Developer. It is asymmetric cryptography primitive, hence uses private/public key pair. When used for encryption, the public key is used to encrypt a message that is then sent to the owner of the corresponding private key. The private key can then be used to decrypt the message.

When RSA is used for digital signature, the signing algorithm involves taking a hash of the message to be signed, and then encrypting the hash to create a ciphertext that serves as the signature. The message and the signature (ciphertext of the message) are then shared.

The verifying part of the scheme involves two steps. The first step is to use the same hashing algorithm to hash the message received from the signer. The second step is to use the corresponding public key to the private key of the signer to decrypt the received signature (that is the ciphertext the signer published with the message). If the hash value generated by the verifier is the same as the one revealed after using the public key to decrypt the signature, then the signature is valid. If it is not equal, then the signature is invalid.

As mentioned in Introduction to Asymmetric Encryption for the Working Developer RSA is usually used with a padding scheme to make it more secure. The first padding scheme that can be used with RSA to create digital signatures is PKCS1-v1_5. This gives rise to the RSA-PKCS1-v1_5 digital signature scheme. This scheme is no longer recommended due to security vulnerability.

A newer standard RSA-PSS which makes use of PSS encoding algorithm is the preferred scheme to use when using RSA for digital signatures.

Schnorr Signature

Schnorr Signature is another digital signature scheme that was proposed in 1990 by Claus P. Schnorr. It is efficient and secure, with no known vulnerability. Although historically, It is not widely used because it was covered by a patent. The patent has since expired in 2008 so adoption will probably see an uptick. This is observed already has Bitcoin recently added Schnorr signatures as part of the digital signature it supports.

DSA and ECDSA

In 1991, the National Institute of Standard and Technology NIST proposed the Digital Signature Algorithm (DSA). For all intent and purposes, DSA is a variant of the Schnoor signature. The sole reason why it was proposed, was to avoid Shnoor's patent restriction. It has no known vulnerability and enjoyed great adoption, although it has since been replaced with the elliptic curve implementation of the scheme, which is known as the Elliptic Curve Digital Signature Algorithm (ECDSA)

EdDSA

EdDSA, which stands for Edwards-curve Digital Signature Algorithm, was proposed in 2008 by Daniel J. Bernstein. It takes inspiration from Schnoor's signature it is also an elliptic curve-based scheme since it makes use of the Edward curve

It is worth mentioning that these schemes mentioned above are by far not the only digital signature schemes in existence. Although, arguably, they are the most common. There exist other schemes like ElGamal signature scheme, the Rabin signature algorithm, or NTRUsign just to mention a few.

Code Examples using RSA-PSS

The code snippets below show how to use RSA-PSS using Deno's implementation of Web Crypto API. Three methods will be used: generateKey, sign, and verify. Which corresponds to the three components of digital signature schemes.

// Key Generation
const keyParams = {
    name: "RSA-PSS",
    // The length in bits of the RSA modulus
    modulusLength: 2048,
    // The public component e. Recommended value is 65537
    publicExponent: new Uint8Array([1, 0, 1]),
    // hash function required by PSS
    hash: "SHA-256",
};

const keyPair = await crypto.subtle.generateKey(keyParams, true, ["sign", "verify"])

// Signing

const message = new TextEncoder().encode("increase interest rate by 2%")

const signParams = {
    name: "RSA-PSS",
    // A long integer representing the length of the random salt to use, in bytes
    // Value can be either 0 or the length of the output of the key's digest algorithm. 
    // For example, if you use SHA-256 as the digest algorithm, this could be 32
    saltLength: 32
}
const signature = await crypto.subtle.sign(signParams, keyPair.privateKey, message)


// Verification

const verifyParams = {
    name: "RSA-PSS",
    // A long integer representing the length of the random salt to use, in bytes
    // Value can be either 0 or the length of the output of the key's digest algorithm. 
    // For example, if you use SHA-256 as the digest algorithm, this could be 32
    saltLength: 32
}

const verificationResult = await crypto.subtle.verify(verifyParams, keyPair.publicKey, signature, message)

console.log(`Signature verification is: ${verificationResult}`);


// Confirm if message is different verification fails

const differentMessage = new TextEncoder().encode("increase interest rate by 20%")

const failedVerification = await crypto.subtle.verify(verifyParams, keyPair.publicKey, signature, differentMessage)

console.log(`Signature verification for different message is: ${failedVerification}`);

Having the above code snippet in index.js file and running via the following command deno run index.js would print out the following:

Signature verification is: true
Signature verification for different message is: false

Last note: Digital Signatures versus Message Authenticated Codes

In Hash Function in Action: Message Authentication Codes I introduced Message Authenticated Codes (MAC). Which involves two parties making use of a shared secret key to ascertain the integrity of messages.

It is worth mentioning that digital signatures are the asymmetric version of message authentication code, with the extra convenience that it does not require parties to perform any key exchange.


No comments: