Giter Site home page Giter Site logo

Minimal code example for us noobs? about charm HOT 5 OPEN

maxsei avatar maxsei commented on June 15, 2024 1
Minimal code example for us noobs?

from charm.

Comments (5)

HarryR avatar HarryR commented on June 15, 2024 1

What is the point of XOODOO_TAG_SIZE?

Constants should have names, otherwise I could abbreviate XOODOO_TAG_SIZE+XOODOO_IV_SIZE to 32 which could be confused with either XOODOO_KEY_SIZE or XOODOO_DIGEST_SIZE...

#ifndef XOODOO_ROUNDS
#define XOODOO_ROUNDS 12
#endif
#define XOODOO_STATE_SIZE 12
#define XOODOO_KEY_SIZE 32
#define XOODOO_DIGEST_SIZE 32
#define XOODOO_TAG_SIZE 16
#define XOODOO_IV_SIZE 16

How come it is acceptable to use a random key if none is provided?

The nonce and the tag is appended to the end of the encrypted file.

If a fixed nonce was used and two files encrypted with the same key, knowledge of unencrypted content of either could be used to partially decrypt the other. Encryption is just an xor of the content with a key stream, so knowing content you can xor against encrypted to get the keystream, xor same position in other encrypted file to get the plaintext.

There have been many exploits where a bad random entropy source was used for nonce/IV which caused accidental nonce reuse. Some signature schemes such as EdDSA use a deterministic nonce (a keyed hash of the input) to ensure that the same content encrypted with the same key will always use the same nonce, which ensures that a bad random source cannot lead to accidental nonce reuse (e.g. when many embedded computers first boot they have very low or predictable entropy)

Why are you memory mapping files for efficiency of writes instead of using malloc?

It makes file I/O much easier, instead of loop, read() etc. I can just use memcpy, read() may fail or return fewer bytes than requested so mmap makes code simpler.

It should also work on huge files, as the pages of data being encrypted/decrypted will be paged in & out of memory by the kernel on an as-needed basis - using memcpy would mean you can't encrypt or decrypt files bigger than the computers memory, with mmap you could encrypt e.g. a 4tb file.

Why is __LINE__ returned for usage error?

When program is under 255 lines, exiting with __LINE__ provides exact source location of error.

e.g. if program prints same error message in multiple places (idk why), you can use return code to get location:

echo $?

Bonus question

Why use 4 == ... or NULL == ... rather than ... == NULL?

Because if you type x = NULL by accident, it could compile without warnings and change the flow.

Whereas you can't assign a constant, so the compiler will error.

from charm.

jedisct1 avatar jedisct1 commented on June 15, 2024 1

Hi!

Adding a couple examples would indeed be useful, even If dsvpn is one.

A strong selling point is that the same state can be used for any sequence of operations, each of them automatically depending on the previous ones.

That effectively secures an entire session, not just individual operations. Examples should ideally show that.

The above example is a bit complicated to follow. Examples should remain simple to understand.
BTW for file encryption, it's better to encrypt one chunk after another rather than a possibly huge input as a single block. Decryption errors can be caught immediately, rather than after having loaded a huge amount of data that will eventually have to be discarded.

Maybe a version of encpipe using charm could be a better example of file encryption. That should be even easier and shorter to implement than with libhydrogen.

from charm.

HarryR avatar HarryR commented on June 15, 2024

Example:

dd if=/dev/urandom of=test bs=1M count=1
key=`charm encrypt test test.encrypted`
charm decrypt test.encrypted test.decrypted $key
sha256sum test*

Uses random IV, if no key specified generates random key & prints it when encrypting

#include <ctype.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include "_charm.c"
typedef struct { int fd; size_t size; uint8_t *map; } handle_t;
static inline uint8_t hexdigit( const char hex ) { return (hex <= '9') ? hex - '0' : toupper(hex) - 'A' + 10; }
static inline uint8_t hexbyte( const char *hex ) { return (hexdigit(*hex) << 4) | hexdigit(*(hex+1)); }
static inline void hexprint(const uint8_t *bytes, const size_t n) { for( size_t i = 0; i < n; i++ ) printf("%02x", bytes[i]); }
static inline void pexit(const char *msg, const int code) { perror(msg); exit(code); }
static inline void randombytes(void *buf, size_t len) { if ((size_t) syscall(SYS_getrandom, buf, (int) len, 0) != len) abort(); }
static inline void parse_hex(const char *hex_str, size_t len, uint8_t *out_bytes) {
    if( (2*len) != strlen(hex_str) ) { fprintf(stderr, "Error: need %ld hex encoded bytes\n", len); exit(__LINE__); }
    for( size_t b = 0; b < len; b++ ) out_bytes[b] = hexbyte(&hex_str[b * 2]);
}
static inline handle_t open_handle( const char *path, off_t truncate_to ) {
    struct stat sb;
    const int fd = open(path, truncate_to > 0 ? O_RDWR|O_CREAT|O_TRUNC : O_RDWR, 0644);
    if( -1 == fd || -1 == fstat(fd, &sb) ) { fprintf(stderr, "%s: ", path); pexit("error open/stat", __LINE__); }
    if( truncate_to > 0 ) {
        if( -1 == ftruncate(fd, truncate_to) ) { fprintf(stderr, "%s: ", path); pexit("error ftruncate", __LINE__); }
        sb.st_size = truncate_to;
    }
    uint8_t *map = mmap(NULL, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    if( map == NULL ) { printf("%s: ", path); pexit("map", __LINE__); }
    return (handle_t){fd, sb.st_size, map};
}
static inline void close_handle( const handle_t *handle ) {
    if( -1 == munmap(handle->map, handle->size) ) pexit("munmap infile error", __LINE__);
    if( -1 == close(handle->fd) ) pexit("close infile error", __LINE__);
}
static inline void charm_init( const char *key_hex, const char *nonce_str, uint32_t (*st)[XOODOO_STATE_SIZE], uint8_t (*key)[XOODOO_KEY_SIZE], uint8_t (*iv)[XOODOO_IV_SIZE], int rand_iv ) {    
    if( NULL != key_hex ) parse_hex(key_hex, XOODOO_KEY_SIZE, *key); else randombytes(key, XOODOO_KEY_SIZE);
    if( NULL != nonce_str ) parse_hex(nonce_str, XOODOO_IV_SIZE, *iv); else if (rand_iv ) randombytes(iv, XOODOO_IV_SIZE);
    uc_state_init(*st, *key, *iv);
}
static inline void main_hash( const char *infile, const char *nonce_str ) {
    const handle_t in = open_handle(infile, 0);
    uint32_t st[XOODOO_STATE_SIZE];
    uint8_t key[XOODOO_KEY_SIZE], iv[XOODOO_IV_SIZE], out_hash[XOODOO_DIGEST_SIZE];
    charm_init(NULL, nonce_str, &st, &key, &iv, 1);
    uc_hash(st, out_hash, in.map, in.size);
    hexprint(out_hash, XOODOO_DIGEST_SIZE); printf("\n");
    close_handle(&in);
    exit(0);
}
static inline void main_encdec(const int is_encrypt, const char *infile, const char *outfile, const char *key_hex, const char *nonce_str) {
    uint32_t st[XOODOO_STATE_SIZE];
    uint8_t key[XOODOO_KEY_SIZE], iv[XOODOO_IV_SIZE];
    const handle_t in = open_handle(infile, 0);
    handle_t out = open_handle(outfile, in.size + (is_encrypt ? (XOODOO_TAG_SIZE + XOODOO_IV_SIZE) : 0));
    memcpy(out.map, in.map, in.size);
    if( ! is_encrypt ) memcpy(iv, in.map+(in.size-XOODOO_IV_SIZE), XOODOO_IV_SIZE);
    charm_init(key_hex, nonce_str, &st, &key, &iv, is_encrypt);
    if( is_encrypt ) {
        hexprint(key, XOODOO_KEY_SIZE); printf("\n");
        uc_encrypt(st, out.map, in.size, out.map+in.size);
        memcpy(out.map+(in.size+XOODOO_TAG_SIZE), iv, XOODOO_IV_SIZE);
    } else {
        if( in.size < (1+XOODOO_TAG_SIZE+XOODOO_IV_SIZE) ) { fprintf(stderr, "Error: file too small, missing tag or IV\n"); exit(1); }
        if( 0 != uc_decrypt(st, out.map, in.size-XOODOO_TAG_SIZE-XOODOO_IV_SIZE, out.map+(in.size-XOODOO_TAG_SIZE-XOODOO_IV_SIZE), XOODOO_TAG_SIZE) )
        { fprintf(stderr, "Error: decrypt failed! Mismatched tag\n"); exit(1); }
    }
    close_handle(&in);
    if( ! is_encrypt && -1 == ftruncate(out.fd, in.size-XOODOO_TAG_SIZE-XOODOO_IV_SIZE)) pexit("ftruncate outfile error", __LINE__);
    close_handle(&out);
    exit(0);
}
int main( int argc, char **argv ) {
    if( argc >= 4 && argc <= 6 ) {
        const int is_encrypt = (0 == strcmp(argv[1], "encrypt"));
        if( is_encrypt || (0 == strcmp(argv[1], "decrypt")) ) main_encdec(is_encrypt, argv[2], argv[3], argv[4], 6 == argc ? argv[5] : NULL);
    }
    if( (3 == argc || 4 == argc) && 0 == strcmp(argv[1], "hash") ) main_hash(argv[2], 4 == argc ? argv[3] : NULL);
    fprintf(stderr, "Usage: %s hash <infile> [nonce]\n", argv[0]);
    fprintf(stderr, "   or...\n");
    fprintf(stderr, "Usage: %s encrypt|decrypt <infile> <outfile> [<256bit-key-hex> [128bit-nonce-hex]]\n", argv[0]);
    fprintf(stderr, "  e.g. %s encrypt %s %s.encrypted `od -vAn -N32 -tx1 /dev/urandom | tr -cd '[a-f0-9]'`\n", argv[0], argv[0], argv[0]);
    return __LINE__;
}

from charm.

maxsei avatar maxsei commented on June 15, 2024

@HarryR it's great to see a CLI that applies charm functionality to user input and files (also cool to see how you adapted earlier revisions of this post to make things more idiomatic for this application)! I have a few questions though:
Charm pertainant:

  • What is the point of XOODOO_TAG_SIZE?
  • How come it is acceptable to use a random key if none is provided?
    Trivial:
  • Why are you memory mapping files for efficiency of writes instead of using malloc?
  • Why is __LINE__ returned for usage error?

from charm.

HarryR avatar HarryR commented on June 15, 2024

(thank you for writing charm, I've used it in a few places as a replacement for tweetnacl)

The above example is a bit complicated to follow. Examples should remain simple to understand.

I got distracted by code golf, it is a deliberately ugly code monolith which only supports what I needed.

from charm.

Related Issues (3)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.