diff options
| author | fiatjaf <fiatjaf@gmail.com> | 2025-05-04 07:00:09 -0300 |
|---|---|---|
| committer | fiatjaf <fiatjaf@gmail.com> | 2025-05-04 07:00:09 -0300 |
| commit | 4de6a69931f918b6608f60d7d566805d06ba2b18 (patch) | |
| tree | 4dbf712abfd38101d61c308cb7d9433b312d0947 | |
| parent | 5b7d3382009332f737bca8fdffce291e8d7935b9 (diff) | |
allow NIP-44 to encrypt more than 65535 bytes.
| -rw-r--r-- | 44.md | 31 |
1 files changed, 22 insertions, 9 deletions
| @@ -84,10 +84,12 @@ NIP-44 version 2 has the following design characteristics: | |||
| 84 | - Slice 76-byte HKDF output into: `chacha_key` (bytes 0..32), `chacha_nonce` (bytes 32..44), `hmac_key` (bytes 44..76) | 84 | - Slice 76-byte HKDF output into: `chacha_key` (bytes 0..32), `chacha_nonce` (bytes 32..44), `hmac_key` (bytes 44..76) |
| 85 | 4. Add padding | 85 | 4. Add padding |
| 86 | - Content must be encoded from UTF-8 into byte array | 86 | - Content must be encoded from UTF-8 into byte array |
| 87 | - Validate plaintext length. Minimum is 1 byte, maximum is 65535 bytes | 87 | - Validate plaintext length. Minimum is 1 byte, maximum is 4294967296 bytes |
| 88 | - Padding format is: `[plaintext_length: u16][plaintext][zero_bytes]` | 88 | - Padding format is: `[plaintext_length: u16][plaintext][zero_bytes]` |
| 89 | - Padding algorithm is related to powers-of-two, with min padded msg size of 32 bytes | 89 | - Padding algorithm is related to powers-of-two, with min padded msg size of 32 bytes |
| 90 | - Plaintext length is encoded in big-endian as first 2 bytes of the padded blob | 90 | - Plaintext length is encoded in big-endian: |
| 91 | - if smaller than 65536, as a u16 in the first 2 bytes of the padded blob; | ||
| 92 | - if greater than 65536, the first the first 6 bytes of the padded blob, the first 2 being zero and the other 4 being the actual encoded length as u32 | ||
| 91 | 5. Encrypt padded content | 93 | 5. Encrypt padded content |
| 92 | - Use ChaCha20, with key and nonce from step 3 | 94 | - Use ChaCha20, with key and nonce from step 3 |
| 93 | 6. Calculate MAC (message authentication code) | 95 | 6. Calculate MAC (message authentication code) |
| @@ -124,7 +126,9 @@ validation rules, refer to BIP-340. | |||
| 124 | 6. Decrypt ciphertext | 126 | 6. Decrypt ciphertext |
| 125 | - Use ChaCha20 with key and nonce from step 3 | 127 | - Use ChaCha20 with key and nonce from step 3 |
| 126 | 7. Remove padding | 128 | 7. Remove padding |
| 127 | - Read the first two BE bytes of plaintext that correspond to plaintext length | 129 | - Read the first 2 bytes, |
| 130 | - if they're zero, read the next 4 bytes as the u32 big-endian plaintext length; | ||
| 131 | - otherwise interpret those 2 bytes as the u16 plaintext length | ||
| 128 | - Verify that the length of sliced plaintext matches the value of the two BE bytes | 132 | - Verify that the length of sliced plaintext matches the value of the two BE bytes |
| 129 | - Verify that calculated padding from step 3 of the [encryption](#Encryption) process matches the actual padding | 133 | - Verify that calculated padding from step 3 of the [encryption](#Encryption) process matches the actual padding |
| 130 | 134 | ||
| @@ -148,8 +152,6 @@ validation rules, refer to BIP-340. | |||
| 148 | - `x[i:j]`, where `x` is a byte array and `i, j <= 0` returns a `(j - i)`-byte array with a copy of the | 152 | - `x[i:j]`, where `x` is a byte array and `i, j <= 0` returns a `(j - i)`-byte array with a copy of the |
| 149 | `i`-th byte (inclusive) to the `j`-th byte (exclusive) of `x`. | 153 | `i`-th byte (inclusive) to the `j`-th byte (exclusive) of `x`. |
| 150 | - Constants `c`: | 154 | - Constants `c`: |
| 151 | - `min_plaintext_size` is 1. 1 byte msg is padded to 32 bytes. | ||
| 152 | - `max_plaintext_size` is 65535 (64kB - 1). It is padded to 65536 bytes. | ||
| 153 | - Functions | 155 | - Functions |
| 154 | - `base64_encode(string)` and `base64_decode(bytes)` are Base64 ([RFC 4648](https://datatracker.ietf.org/doc/html/rfc4648), with padding) | 156 | - `base64_encode(string)` and `base64_decode(bytes)` are Base64 ([RFC 4648](https://datatracker.ietf.org/doc/html/rfc4648), with padding) |
| 155 | - `concat` refers to byte array concatenation | 157 | - `concat` refers to byte array concatenation |
| @@ -182,16 +184,27 @@ def calc_padded_len(unpadded_len): | |||
| 182 | def pad(plaintext): | 184 | def pad(plaintext): |
| 183 | unpadded = utf8_encode(plaintext) | 185 | unpadded = utf8_encode(plaintext) |
| 184 | unpadded_len = len(plaintext) | 186 | unpadded_len = len(plaintext) |
| 185 | if (unpadded_len < c.min_plaintext_size or | 187 | if (unpadded_len < 1 or |
| 186 | unpadded_len > c.max_plaintext_size): raise Exception('invalid plaintext length') | 188 | unpadded_len > 4294967295): raise Exception('invalid plaintext length') |
| 187 | prefix = write_u16_be(unpadded_len) | 189 | if unpadded_len > 65536: |
| 190 | prefix = concat( | ||
| 191 | [0, 0], | ||
| 192 | write_u32_be(unpadded_len), | ||
| 193 | ) | ||
| 194 | else: | ||
| 195 | prefix = write_u16_be(unpadded_len) | ||
| 188 | suffix = zeros(calc_padded_len(unpadded_len) - unpadded_len) | 196 | suffix = zeros(calc_padded_len(unpadded_len) - unpadded_len) |
| 189 | return concat(prefix, unpadded, suffix) | 197 | return concat(prefix, unpadded, suffix) |
| 190 | 198 | ||
| 191 | # Converts padded bytearray to unpadded plaintext | 199 | # Converts padded bytearray to unpadded plaintext |
| 192 | def unpad(padded): | 200 | def unpad(padded): |
| 193 | unpadded_len = read_uint16_be(padded[0:2]) | 201 | unpadded_len = read_uint16_be(padded[0:2]) |
| 194 | unpadded = padded[2:2+unpadded_len] | 202 | if unpadded_len == 0: |
| 203 | unpadded_len = read_uint32_be(padded[2:6]) | ||
| 204 | unpadded = padded[6:6+unpadded_len] | ||
| 205 | else: | ||
| 206 | unpadded = padded[2:2+unpadded_len] | ||
| 207 | |||
| 195 | if (unpadded_len == 0 or | 208 | if (unpadded_len == 0 or |
| 196 | len(unpadded) != unpadded_len or | 209 | len(unpadded) != unpadded_len or |
| 197 | len(padded) != 2 + calc_padded_len(unpadded_len)): raise Exception('invalid padding') | 210 | len(padded) != 2 + calc_padded_len(unpadded_len)): raise Exception('invalid padding') |