/* This file is part of msr. msr is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. msr is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Foobar. If not, see . */ #include "crypto.h" void compute_checksum_seckey(SecretKey sk, uint8_t *checksum) { // checksum = BLAKE2( ALG || KEY_ID || SECRET_KEY ) crypto_generichash_state st; crypto_generichash_init(&st, NULL, 0, CHK_WIDTH); crypto_generichash_update(&st, sk->sig_alg, SIG_ALG_WIDTH); crypto_generichash_update(&st, sk->key_id, KEY_ID_WIDTH); crypto_generichash_update(&st, sk->secret_key, SEC_KEY_WIDTH); crypto_generichash_final(&st, checksum, CHK_WIDTH); } Error generate_key_pair(SecretKey *sk, PublicKey *pk) { *sk = catch_smalloc(sizeof(struct secret_key_s), "Unable to securely allocate secrety key memory."); *pk = catch_smalloc(sizeof(struct public_key_s), "Unable to securely allocate public key memory."); // Initialise structures randomly, for hysterical raisons randombytes_buf((*sk),sizeof(struct secret_key_s)); randombytes_buf((*pk),sizeof(struct public_key_s)); // -- Public key init --- memcpy((*pk)->sig_alg, SIG_ALG, SIG_ALG_WIDTH); randombytes_buf((*pk)->key_id, KEY_ID_WIDTH); // --- Secret key init --- memcpy((*sk)->sig_alg, SIG_ALG, SIG_ALG_WIDTH); memcpy((*sk)->kdf_alg, KDF_ALG, KDF_ALG_WIDTH); memcpy((*sk)->chk_alg, CHK_ALG, CHK_ALG_WIDTH); (*sk)->kdf_memlimit = crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE; (*sk)->kdf_opslimit = crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE; memcpy((*sk)->key_id, (*pk)->key_id, KEY_ID_WIDTH); // Generate keypair crypto_sign_keypair((*pk)->public_key, (*sk)->secret_key); // Checksum on secret key to wrap-up generation compute_checksum_seckey((*sk),(*sk)->checksum); return SUCCESS; } Error is_valid_sigmsg(SignedMsg sm) { if (sm == NULL) return E_NULL; if (sodium_memcmp(sm->sig_alg, SIG_ALG, SIG_ALG_WIDTH) != 0) return E_ALG_CHOICE; return SUCCESS; } Error is_valid_pubkey(PublicKey pk) { if (pk == NULL) return E_NULL; // Same as before if (sodium_memcmp(pk->sig_alg, SIG_ALG, SIG_ALG_WIDTH) != 0) return E_ALG_CHOICE; return SUCCESS; } Error is_correct_options(SecretKey sk) { if (sk == NULL) return E_NULL; // For a secret key to be valid, it *must* use the same algorithms if ( (sodium_memcmp(sk->sig_alg, SIG_ALG, SIG_ALG_WIDTH) != 0) || (sodium_memcmp(sk->kdf_alg, KDF_ALG, KDF_ALG_WIDTH) != 0) || (sodium_memcmp(sk->chk_alg, CHK_ALG, CHK_ALG_WIDTH) != 0) ) return E_ALG_CHOICE; // If so, sucess return SUCCESS; } Error is_correct_checksum(SecretKey sk) { Error e; uint8_t checksum[CHK_WIDTH]; compute_checksum_seckey(sk,checksum); if (sodium_memcmp(sk->checksum, checksum, CHK_WIDTH) == 0) e = SUCCESS; else e = E_CHECKSUM; sodium_memzero(checksum,CHK_WIDTH); return e; } void xor_keynum_sk(SecretKey sk, const uint8_t outp[ENCRYPTED_KEY_WIDTH]) { // Xor correctly aligned to blocks size_t k; for (k=0;kkey_id[k] ^= outp[k]; for (k=0;ksecret_key[k] ^= outp[k+KEY_ID_WIDTH]; for (k=0;kchecksum[k] ^= outp[k+KEY_ID_WIDTH+SEC_KEY_WIDTH]; } Error alter_seckey(char encrypt, SecretKey sk, const uint8_t * const passwd, unsigned long long pwlen) { Error e; // Cannot decrypt invalid secret keys, or reencrypt encrypted and so on if ( (e = is_correct_options(sk)) != SUCCESS ) return e; e = is_correct_checksum(sk); if ( (encrypt != 0) && (e != SUCCESS) ) return E_ALREADY_ENCRYPTED; if ( (encrypt == 0) && (e == SUCCESS) ) return E_ALREADY_DECRYPTED; // Actual processing uint8_t *outp = catch_smalloc(ENCRYPTED_KEY_WIDTH, "Unable to allocate secure internal storage for KDF."); sodium_memzero(outp,ENCRYPTED_KEY_WIDTH); // Run the KDF if (crypto_pwhash_scryptsalsa208sha256(outp, ENCRYPTED_KEY_WIDTH, (char*)passwd, pwlen, sk->kdf_salt,sk->kdf_opslimit, sk->kdf_memlimit) != 0 ) return E_KDF_FAIL; // XOR into blocks appropriately xor_keynum_sk(sk,outp); if (!encrypt) { // If decrypting, check for correct password if ( is_correct_checksum(sk) == SUCCESS ) return SUCCESS; else { // Password was bad, undo what we did xor_keynum_sk(sk,outp); e = E_BAD_PASS; } // Otherwise done } else e = SUCCESS; // Wipe kdf output sodium_free(outp); return e; } Error verify_message(PublicKey pk, SignedMsg sm) { Error e; // Check validity of public key and signed message algo choice if ( (e = is_valid_pubkey(pk)) != SUCCESS ) return e; if ( (e = is_valid_sigmsg(sm)) != SUCCESS ) return e; // Ensure that the key id is correct if ( sodium_memcmp(pk->key_id,sm->key_id,KEY_ID_WIDTH) != 0) return E_WRONG_PUBKEY; // Verify if if ( crypto_sign_verify_detached(sm->sig, sm->msg, sm->msglen, pk->public_key) != 0 ) return E_VERIFY; return SUCCESS; } Error sign_message(SecretKey sk, SignedMsg sm) { Error e; // Ensure that SK uses correct algo choices and has ok checksum if ( (e = is_correct_options(sk)) != SUCCESS ) return e; if ( is_correct_checksum(sk) != SUCCESS ) return e; // Sign it if ( crypto_sign_detached(sm->sig, NULL, sm->msg, sm->msglen, sk->secret_key) != 0 ) return E_SIGN; return SUCCESS; }