aboutsummaryrefslogtreecommitdiff
path: root/crypto.c
diff options
context:
space:
mode:
authorTslil Clingman <hiato1@gmail.com>2015-06-25 02:51:02 +0200
committerTslil Clingman <hiato1@gmail.com>2015-06-25 02:51:02 +0200
commit26d1f221761ba264906f215ed53f4f57ab0f9d06 (patch)
tree7abd32ed3449e43d0b17ccee51e6b843bf63d759 /crypto.c
Init
Diffstat (limited to 'crypto.c')
-rw-r--r--crypto.c160
1 files changed, 160 insertions, 0 deletions
diff --git a/crypto.c b/crypto.c
new file mode 100644
index 0000000..799cba3
--- /dev/null
+++ b/crypto.c
@@ -0,0 +1,160 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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;k<KEY_ID_WIDTH;k++) sk->key_id[k] ^= outp[k];
+ for (k=0;k<SEC_KEY_WIDTH;k++) sk->secret_key[k] ^= outp[k+KEY_ID_WIDTH];
+ for (k=0;k<CHK_WIDTH;k++) sk->checksum[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;
+}