From 2c06131396b0f03724c9207a71dc8258596d7b74 Mon Sep 17 00:00:00 2001 From: Marcin Czenko Date: Mon, 30 Jun 2025 14:19:35 +0200 Subject: [PATCH] updates encryption docs --- 10 Notes/BearSSL hashing.md | 58 +++++++++++++++++++ 10 Notes/Codex Encryption Basis.md | 4 +- 10 Notes/Codex Encryption Design.md | 20 +++++++ ...nter to a seq to pass it to a C library.md | 21 +++++++ 4 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 10 Notes/BearSSL hashing.md create mode 100644 10 Notes/How to get a pointer to a seq to pass it to a C library.md diff --git a/10 Notes/BearSSL hashing.md b/10 Notes/BearSSL hashing.md new file mode 100644 index 0000000..c1fb7af --- /dev/null +++ b/10 Notes/BearSSL hashing.md @@ -0,0 +1,58 @@ +Here is an example, of how to use hashing in [[BearSSL]]: + +```nim +import bearssl/hash +import stew/byteutils + +let data = "0123456789abcdef".toBytes +let buff = newSeq[byte](sha256SIZE) + +var sha256HashCtx = Sha256Context() +sha256Init(sha256HashCtx) +sha224Update(sha256HashCtx, addr data[0], data.len.uint) +sha256Out(sha256HashCtx, addr buff[0]) + +echo "Hash out: ", buff.toHex + +``` + +>[!note] +> notice that above we use `sha224Update` and not the expected `sha256update`. This is because of a bug, which is addressed here: https://github.com/status-im/nim-bearssl/pull/68 + +BearSSL also simulates an [OOP interface](https://bearssl.org/oop.html), which can be used to create a generic `hash` function which can then be reused for various hashing algorithms: + +```nim +import bearssl/hash + +proc hash(hashClass: ptr HashClass, data: openArray[byte]): seq[byte] = + var compatCtx = HashCompatContext() + let buffSize = (hashClass[].desc shr HASHDESC_OUT_OFF) and HASHDESC_OUT_MASK + result = newSeq[byte](buffSize) + + let hashClassPtrPtr: ConstPtrPtrHashClass = addr(compatCtx.vtable) + + hashClass[].init(hashClassPtrPtr) + hashClass[].update(hashClassPtrPtr, addr data[0], data.len.uint) + hashClass[].`out`(hashClassPtrPtr, addr result[0]) +``` + +Then such a hash function can be conveniently reused for various hashing functions: + +```nim +import stew/byteutils + +let data = "0123456789abcdef".toBytes + +var sha256HashCtx = Sha256Context() +sha256Init(sha256HashCtx) +echo "Hash out[sha256]: ", hash(sha256HashCtx.vtable, data).toHex + +var sha1HashCtx = Sha1Context() +sha1Init(sha1HashCtx) +echo "Hash out[sha1]: ", hash(sha1HashCtx.vtable, data).toHex + +var md5Ctx = Md5Context() +md5Init(md5Ctx) +echo "Hash out[md5]: ", hash(md5Ctx.vtable, data).toHex +``` + diff --git a/10 Notes/Codex Encryption Basis.md b/10 Notes/Codex Encryption Basis.md index c32c56b..ef9677b 100644 --- a/10 Notes/Codex Encryption Basis.md +++ b/10 Notes/Codex Encryption Basis.md @@ -20,7 +20,7 @@ I would argue that at the moment we should only do something very basic, that is We need to choose a [stream cipher](https://en.wikipedia.org/wiki/Stream_cipher). Which one to choose doesn't really matter as long as we don't fuck up the many subtle details. Some natural options: - something based on the [AES block cipher](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard) -- [Salsa20](https://en.wikipedia.org/wiki/Salsa20) or ChaCha +- [Salsa20](https://en.wikipedia.org/wiki/Salsa20) or ChaCha (newer) - just `XOR` with Shake or TurboShake (Shake is the SHA3 XOF) AES is usually a default choice because it's a standard, it's well-known (you don't have to explain the choice), and hardware-accelerated. However, there are many details allowing you to shoot yourself in the foot. @@ -77,6 +77,8 @@ A common mitigation strategy is to include additional entropy based for example ## A proposal +^b2e265 + Based on the above, I have a very simple proposal: - use a freshly generated random master key (at least 256 bits) per dataset (generated and kept on the user's machine) diff --git a/10 Notes/Codex Encryption Design.md b/10 Notes/Codex Encryption Design.md index b288933..edc6780 100644 --- a/10 Notes/Codex Encryption Design.md +++ b/10 Notes/Codex Encryption Design.md @@ -1,7 +1,27 @@ --- related-to: - "[[Codex Encryption Basis]]" + - "[[How to get a pointer to a seq to pass it to a C library]]" --- +As summarized in [[Codex Encryption Basis#^b2e265|the proposal]], we: + +- use a freshly generated random master key (at least 256 bits) per dataset (generated and kept on the user's machine) +- derive a new encryption key and also an IV for each block from the master key and the block index +- use for example AES192-CBC + +For example, we could have + + key = SHA256( MASTER_KEY || block_index ), truncated to 192 bits + IV = SHA256( MASTER_IV || block_index ), truncated to 128 bits + +where both `MASTER_KEY` and `MASTER_IV` are 256 bit random numbers, and `||` denotes concatenation. + +If storing 512 bits (as opposed to a 256 bit minimum) of key material is a problem, we could derive both by the same key, for example as + + key' = SHA256( MASTER_KEY || 0x01 || block_index ), truncated to 192 bits + IV' = SHA256( MASTER_KEY || 0x02 || block_index ), truncated to 128 bits + + Some context info: - [bearssl](https://bearssl.org/) diff --git a/10 Notes/How to get a pointer to a seq to pass it to a C library.md b/10 Notes/How to get a pointer to a seq to pass it to a C library.md new file mode 100644 index 0000000..8983512 --- /dev/null +++ b/10 Notes/How to get a pointer to a seq to pass it to a C library.md @@ -0,0 +1,21 @@ +Having a Nim sequence: + +```nim +var buff = newSeq[byte](buffSize) +``` + +if you want to get a pointer to the underlying data buffer that is suitable for being passed to a C library, you can simply use: + +```nim +sha256Update(ctx, addr buff[0], input[i].len.uint) +``` + +If your input is a `string`, you can use the following: + +```nim +let str = "abcdefghijklmnopqrstuvwxyz" +sha256Update(ctx, str.cstring, input[i].len.uint) +``` + +Here we use `sha256Update` from [[BearSSL]] library. See [[BearSSL hashing]] for more complete examples. +See also [Accessing seq pointer](https://forum.nim-lang.org/t/1489) and [Use cstring for C binding](https://forum.nim-lang.org/t/8179) on Nim forum.