08. Unforgeable fingerprints
November 7, 2017 ยท View on GitHub
Solution to problem 07
// bank.js
Phew, now we are just getting started trying to grok basic cryptography. In the previous exercise we made sure that our banking application can verify if there has been any corruption of it's transaction log using hashing.
However for our previous scheme to be secure it would require some operational measures that may not be desirable. If the latest hash is distributed securely to competing parties (customers, staff, directors) and they have the opportunity to verify it at their discretion, this scheme is actually pretty safe. This is the same idea behind distributing SHASUM file for binary releases, like how Node.js does it. You download the SHASUM file from an official source, here nodejs.org, and then you can download the binaries from any source and verify that the SHA sums match.
In practise, there are still some major flaws present, since it is too hard to enforce the above scheme for a bank where the hash changes all the time. A very practical attack vector is for an adversary to simply modify some values in the transaction log and then re-generate another valid hash chain. Then the bank will never know it has been attacked! Let's try to fix this.
Basically we need a way for the bank to verify that it was the one that wrote the hashes of the transaction log in the first place. That way an attacker couldn't simply rewrite the transaction log and generate another hash-chain. This property is called non-repudiation in crypto-lingo.
Digital signatures and Key-pairs
A cryptographic technique for achieving non-repudiation is digital signatures.
A digital signature is a cryptographic primitive that has two operations, sign
and verify, and works just like a real-world signature, but without any
practical uncertainty.
To be able to create signatures you need what is called a key-pair. If you have an account on Github you probably already have a SSH key pair, which is the same concept but based on some very different mathematics. A key-pair consists of a public key and a secret key. These are also sometimes known as the identity and private key. The idea is that you distribute your public key to everyone, and they use this key to verify any signature that you've made. The secret key is never to be revealed as this is like a pen used to make signatures. If anyone steals your secret key, they can impersonate you and it's game over.
Signing with libsodium and sodium-native
sodium-native exposes digital signatures under the crypto_sign_* namespace.
The most important ones are:
sodium.crypto_sign_keypair(publicKey, secretKey)Generates a key pair, wherepublicKeyandsecretKeyare the outputBuffers. They need to be exactlysodium.crypto_sign_PUBLICKEYBYTESandsodium.crypto_sign_SECRETKEYBYTESlong.sodium.crypto_sign_detached(signature, message, secretKey)The detached API writes the digital signature tosignatureBuffer, formessageusingsecretKey. The signatureBufferneeds to becrypto_sign_BYTESlong. Like with hashing, this signature may contain non-printable bytes, so it is a good idea to convert tohexorbase64if you want to work with it outsideBuffers.var bool = sodium.crypto_sign_verify_detached(signature, message, publicKey)To verify a signature, you pass insignatureBuffer, the originalmessageBufferand the correspondingpublicKey. It will returnboolwhether the signature could be verified or not.
This primitive will allow us to upgrade our bank's transaction log with an unforgeable fingerprint (or signature) so we can verify that only the bank is updating it. This will prevent anyone without access to the private key from modifying the transaction log in any way.
Let's first do a basic exercise to get a bit more familiar with signing.
Problem
Use the crypto_sign APIs to make two new programs sign.js and verify.js.
sign.js should generate a key-pair, sign a message from the command line and
print out the message, public key and signature. verify.js should accept the
public key, message and signature and verify that it is correct.
Testing
Try generating a few messages and keys and verify them. Then try tampering with the signature and messages and verify that the verify script rejects them.
For extra credit try sending your signed messages to other people in the workshop and have them verify it. You may also think about the security around how to establish trust in a public key, ie. how do you know the public key belongs to the person you think owns it.