Skip to content

Commit

Permalink
Add problems 09 and 10
Browse files Browse the repository at this point in the history
  • Loading branch information
emilbayes committed Nov 7, 2017
1 parent ea351c9 commit 2bb156a
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 32 deletions.
64 changes: 55 additions & 9 deletions problems/09.md
@@ -1,20 +1,66 @@
# 09. Signing the transaction log

Now that we have a basic understanding on how digital signatures work we can start securing the transaction log with this new primitive.
<details>
<summary>Solution to problem 08</summary>

Remember, since we already have a hash-chain of all transactions the only thing missing is making sure we, the bank, are the only ones that are generating the hash-chain.
```js
// sign.js
var sodium = require('sodium-native')

var publicKey = Buffer.alloc(sodium.crypto_sign_PUBLICKEYBYTES)
var secretKey = Buffer.alloc(sodium.crypto_sign_SECRETKEYBYTES)
sodium.crypto_sign_keypair(publicKey, secretKey)

var message = Buffer.from('Hello world!')
var signature = Buffer.alloc(sodium.crypto_sign_BYTES)

sodium.crypto_sign_detached(signature, message, privateKey)

console.log('Public key:': publicKey.toString('hex'))
console.log('Message:': message.toString())
console.log('Signature:': signature.toString('hex'))
```

```js
// verify.js

```

</details>

Now that we have a basic understanding on how digital signatures work we can
start securing the transaction log with this new primitive.

Remember, since we already have a hash chain of all transactions the only thing
missing is making sure we, the bank, are the only ones that are generating the
hash chain.

## Problem

Using the signature APIs you learned about in the previous excercise, extend the bank to:
Using the signature APIs you learned about in the previous exercise, extend the
bank to:

1. Check if a previous keypair is stored on disk, if so load it.
1. If not, generate a new keypair and store it.
1. When you generate a new hash for a transaction, sign it using the secret key and store the signature as a new property `signature`, next to the `hash` and `value` property.
1. When loading the transaction log remember to verify the last signature to see if the log has been tampered with.
1. Check if a existing key-pair is stored on disk, if so load it.
2. If not, generate a new key-pair and store it.
3. When you generate a new hash for a transaction, sign it using the secret key
and store the signature as a new property `signature`, next to the `hash` and
`value` property.
4. When loading the transaction log extend verification to validate the
signatures in addition to the hashes.

Note that in a real life application, we ofcourse wouldn't store the keypair on disk but instead on an external device like a usb stick or a hardware backed secure vault.
You might be thinking that storing the key-pair right next to the transaction
log is not very safe, but this is much more of an operational problem (which
also has technical mitigations), but for the purpose of our workshop it will
suffice. In a real bank you might use a Hardware Secure Module (HSM), which is a
logical separate computing unit in the server, from which the secret key never
leaves. As of current, [AWS has a CloudHSM Classic](https://aws.amazon.com/cloudhsm/pricing-classic/)
product which gives you a dedicated machine with it's own HSM, however the
upfront cost is $5,000 USD and around $1,500 USD monthly maintenance cost.

## Testing

Make sure your bank works the same way as before. Then stop the bank and try tampering with the log. The bank should reject the bad transaction log, even is the hashes are correct.
Make sure your bank works the same way as before. Then stop the bank and try
tampering with the log. The bank should reject the bad transaction log, even if
the hashes are correct.

[Continue to problem 10](10.md)
69 changes: 46 additions & 23 deletions problems/10.md
@@ -1,36 +1,59 @@
# 10. Updating the threat model

By now our bank is actually pretty secure when looking at our original threat model.
The transaction log is signed and contains integrity checks, meaning you have to access to a well guarded secret key in order to modify the banks state.

Let's update our threat model with another requirement.

In this age of data leaks, we want to make sure that if an agent from a big government spy agency breaks into our bank server and steals the transaction log, as little data is leaked as possible about how much money the bank is storing etc.

To support this, let's introduce a new cryptographic primitive, symmetric encryption.

Symmetric encryption is a scheme where if you have another secret key (unrelated to the signing secret key from before) and a random nonce (a short single used key), you can encrypt a message securely.

You don't even need to keep the nonce secret, only the secret key.

Using `sodium-native` this functionality is exposed through the `crypto_secretbox` APIs
By now our bank has mitigated the original threat model to a degree where the
threat has shifted from the transaction log itself, and unto the key-pair. This
means that we have centralised security onto something that is easier to reason
about and has a much smaller attack surface, albeit being even more sensitive
to the operation of our bank. However, this problem is now more a question of
operations and policy than cryptography.

This also means that our priorities shift as we now have other threats that
pose higher risk. In this age of data leaks, we want to make sure that if an
adversary, eg. a three letter gov't agency, breaks into our bank server at night
and steals the transaction log, they stand to learn very little about the banks
business.

To achieve this, we need to introduce a new cryptographic primitive, symmetric
encryption.

Symmetric crypto dates all the way back to at least Julius Caesar, who used it
to communicate securely with his generals. Much has happened since then, but the
basic idea is the same, you have a key that is used for both the `encrypt` and
`decrypt` operations. In modern schemes you often also need a `nonce` which is
often a random piece of data, that is not required to be secret, but protects
against a several classes of attacks.

Using `sodium-native` this functionality is exposed through the
`crypto_secretbox` APIs:

* `sodium.crypto_secretbox_easy(cipher, message, nonce, secretKey)`
* `var valid = sodium.crypto_secretbox_open_easy(message, cipher, nonce, secretKey)`

The `crypto_secretbox_easy` api will encrypt a message buffer into the cipher buffer using the nonce and secretKey. The `secretKey` should be a `sodium.crypto_secretbox_KEYBYTES` bytes long buffer containing very random data. You can generate a secure one using the `sodium.randombytes_buf` API. `cipher` is the buffer where the encrypted message is saved. This buffer should be `message.length + crypto_secretbox_MACBYTES` long, and finally `nonce` should be another random buffer of size `crypto_secretbox_NONCEBYTES`. It is important that you never re-use a nonce to encrypt more than a single message.
Encrypt `message` `Buffer` into `cipher` `Buffer` with `nonce` and
`secretKey`. The secret key must be `sodium.crypto_secretbox_KEYBYTES` and is
best generated using the `sodium.randombytes_buf` API. This key must be
persisted somehow. `nonce` should be another random buffer of size
`crypto_secretbox_NONCEBYTES`. The `cipher` `Buffer` should be
`message.length + crypto_secretbox_MACBYTES` long. It is important that you
never re-use a nonce to encrypt more than a single message.
* `var bool = sodium.crypto_secretbox_open_easy(message, cipher, nonce, secretKey)`
Decrypt `cipher` `Buffer` into `message` `Buffer` using `nonce` and `secretKey`.
Will return a `boolean` depending on whether the cipher text could be decrypted.

## Problem

Use the APIs described above to make three new programs, `secret-key.js` `encrypt.js` and `decrypt.js`.
Use the APIs described above to make three new programs, `secret-key.js`
`encrypt.js` and `decrypt.js`.

* `secret-key.js` should generate a secret key using the randombytes_buf api of the correct length
* `encrypt.js` should accept a secret key and a message and print out the encrypted message and the random nonce used to encrypt it.
* `decrypt.js` should accept the encrypted message, secret key and nonce and print out the plaintext message if valid.
* `secret-key.js` should generate a secret key using the `randombytes_buf` api of
the correct length
* `encrypt.js` should accept a secret key and a message and print out the
encrypted message and the random nonce used to encrypt it.
* `decrypt.js` should accept the encrypted message, secret key and nonce and
print out the plaintext message if valid.

## Testing

Try running a couple of test messages like `Hello, World` through your encrypter and try decrypting them to see that it works. Then try tampering with some of the encrypted messages to see that decryption fails.
Try running a couple of test messages like `Hello, World` through your encrypter
and try decrypting them to see that it works. Then try tampering with some of
the encrypted messages to see that decryption fails.

[Continue to problem 11](11.md)

0 comments on commit 2bb156a

Please sign in to comment.