/* -*-c-*-
*
- * $Id: key-pass.c,v 1.2 2000/06/17 11:26:35 mdw Exp $
+ * $Id$
*
* Encrypting keys with passphrases
*
* MA 02111-1307, USA.
*/
-/*----- Revision history --------------------------------------------------*
- *
- * $Log: key-pass.c,v $
- * Revision 1.2 2000/06/17 11:26:35 mdw
- * `rand_getgood' is deprecated.
- *
- * Revision 1.1 1999/12/22 15:47:48 mdw
- * Major key-management revision.
- *
- */
-
/*----- Header files ------------------------------------------------------*/
#include <mLib/dstr.h>
#include "blowfish-cbc.h"
#include "rmd160.h"
+#include "rmd160-mgf.h"
+#include "rmd160-hmac.h"
/*----- Main code ---------------------------------------------------------*/
-/* --- @key_plock@ --- *
+/* --- Format --- *
+ *
+ * Choose a random 160-bit string %$R$%. Take the passphrase %$P$%, and
+ * the message %$m$%. Now, compute %$K_E \cat K_T = H(R \cat P)$%,
+ * %$y_0 = E_{K_E}(m)$% and %$\tau = T_{K_T}(y_0)$%. The ciphertext is
+ * %$y = N \cat \tau \cat y_0$%.
+ *
+ * This is not the original format. The original format was insecure, and
+ * has been replaced incompatibly.
+ */
+
+/* --- @key_lock@ --- *
*
- * Arguments: @const char *tag@ = tag to use for passphrase
- * @key_data *k@ = source key data block
- * @key_data *kt@ = target key data block
+ * Arguments: @key_data **kt@ = where to store the destination pointer
+ * @key_data *k@ = source key data block or null to use @*kt@
+ * @const void *e@ = secret to encrypt key with
+ * @size_t esz@ = size of the secret
*
- * Returns: Zero if successful, nonzero if there was a problem.
+ * Returns: ---
*
- * Use: Locks a key by encrypting it with a passphrase.
+ * Use: Encrypts a key data block using a secret.
*/
-int key_plock(const char *tag, key_data *k, key_data *kt)
+void key_lock(key_data **kt, key_data *k, const void *e, size_t esz)
{
dstr d = DSTR_INIT;
- octet hash[RMD160_HASHSZ];
- char buf[256];
+ octet b[RMD160_HASHSZ * 2];
+ octet *m;
+ size_t msz;
+ rmd160_mgfctx r;
+ blowfish_cbcctx c;
+ rmd160_mackey mk;
+ rmd160_macctx mc;
/* --- Sanity check --- */
+ if (k) key_incref(k); else k = *kt;
assert(((void)"Key data is already encrypted",
(k->e & KF_ENCMASK) != KENC_ENCRYPT));
- /* --- Read the passphrase --- */
+ /* --- Format the stuff in the buffer --- */
- if (passphrase_read(tag, PMODE_VERIFY, buf, sizeof(buf)))
- return (-1);
-
- /* --- Extract the key data --- *
- *
- * On the front, put four random bytes to act as a passphrase salt (which
- * remain in the clear), and four zero bytes to be able to spot duff
- * passphrases when unlocking.
- */
-
- DENSURE(&d, 8);
- rand_get(RAND_GLOBAL, d.buf, 4);
- memset(d.buf + 4, 0, 4);
- d.len += 8;
+ DENSURE(&d, RMD160_HASHSZ * 2);
+ rand_get(RAND_GLOBAL, d.buf, RMD160_HASHSZ);
+ d.len += RMD160_HASHSZ * 2;
key_encode(k, &d, 0);
- if (k == kt)
- key_destroy(k);
-
- /* --- Hash the passphrase into a key --- */
-
- {
- rmd160_ctx h;
- rmd160_init(&h);
- rmd160_hash(&h, d.buf, 4);
- rmd160_hash(&h, buf, strlen(buf));
- rmd160_done(&h, hash);
- BURN(h);
- BURN(buf);
- }
+ m = (octet *)d.buf + RMD160_HASHSZ * 2;
+ msz = d.len - RMD160_HASHSZ * 2;
- /* --- Encrypt the key data --- */
+ /* --- Hash the passphrase to make a key --- */
- {
- blowfish_cbcctx c;
- blowfish_cbcinit(&c, hash, sizeof(hash), 0);
- blowfish_cbcencrypt(&c, d.buf + 4, d.buf + 4, d.len - 4);
- BURN(hash);
- BURN(c);
- }
+ rmd160_mgfkeybegin(&r);
+ rmd160_mgfkeyadd(&r, d.buf, RMD160_HASHSZ);
+ rmd160_mgfkeyadd(&r, e, esz);
+ rmd160_mgfencrypt(&r, 0, b, sizeof(b));
+ BURN(r);
+
+ /* --- Encrypt the plaintext --- */
- /* --- Put the key data back in the key --- */
+ blowfish_cbcinit(&c, b, RMD160_HASHSZ, 0);
+ blowfish_cbcencrypt(&c, m, m, msz);
+ BURN(c);
- key_encrypted(kt, d.buf, d.len);
+ /* --- MAC the ciphertext --- */
+
+ rmd160_hmacinit(&mk, b + RMD160_HASHSZ, RMD160_HASHSZ);
+ rmd160_macinit(&mc, &mk);
+ rmd160_machash(&mc, m, msz);
+ rmd160_macdone(&mc, d.buf + RMD160_HASHSZ);
+ BURN(mk);
+ BURN(mc);
+
+ /* --- Done --- */
+
+ BURN(b);
+ *kt = key_newencrypted(0, d.buf, d.len);
+ key_drop(k);
dstr_destroy(&d);
- return (0);
}
-/* --- @key_punlock@ --- *
+/* --- @key_unlock@ --- *
*
- * Arguments: @const char *tag@ = tag to use for passphrase
- * @key_data *k@ = source key data block
- * @key_data *kt@ = target key data block
+ * Arguments: @key_data **kt@ = where to store the destination pointer
+ * @key_data *k@ = source key data block or null to use @*kt@
+ * @const void *e@ = secret to decrypt the block with
+ * @size_t esz@ = size of the secret
*
- * Returns: Zero if it worked, nonzero if it didn't.
+ * Returns: Zero for success, or a @KERR_@ error code.
*
- * Use: Unlocks a passphrase-locked key.
+ * Use: Unlocks a key using a secret.
*/
-int key_punlock(const char *tag, key_data *k, key_data *kt)
+int key_unlock(key_data **kt, key_data *k, const void *e, size_t esz)
{
- octet hash[RMD160_HASHSZ];
- char buf[256];
- octet *p;
+ octet b[RMD160_HASHSZ * 2];
+ octet *p = 0;
+ int rc;
+ int drop = 0;
+ key_data *kd;
+ rmd160_mgfctx r;
+ blowfish_cbcctx c;
+ rmd160_mackey mk;
+ rmd160_macctx mc;
size_t sz;
/* --- Sanity check --- */
+ if (!k) { k = *kt; drop = 1; }
assert(((void)"Key data isn't encrypted",
(k->e & KF_ENCMASK) == KENC_ENCRYPT));
- /* --- Fetch the passphrase --- */
+ /* --- Check the size --- */
- if (passphrase_read(tag, PMODE_READ, buf, sizeof(buf)))
- goto fail_0;
-
- /* --- Allocate a destination buffer --- *
- *
- * Minimum size for a key is four bytes salt, four bytes zeroes, two bytes
- * type, and two bytes length, making a total of twelve.
- */
-
- if (k->u.k.sz < 12)
- goto fail_0;
- sz = k->u.k.sz - 4;
- p = xmalloc(k->u.k.sz);
-
- /* --- Hash the passphrase --- */
-
- {
- rmd160_ctx h;
- rmd160_init(&h);
- rmd160_hash(&h, k->u.k.k, 4);
- rmd160_hash(&h, buf, strlen(buf));
- rmd160_done(&h, hash);
- BURN(h);
- BURN(buf);
+ if (k->u.k.sz < RMD160_HASHSZ * 2)
+ return (KERR_MALFORMED);
+ sz = k->u.k.sz - RMD160_HASHSZ * 2;
+
+ /* --- Hash the passphrase to make a key --- */
+
+ rmd160_mgfkeybegin(&r);
+ rmd160_mgfkeyadd(&r, k->u.k.k, RMD160_HASHSZ);
+ rmd160_mgfkeyadd(&r, e, esz);
+ rmd160_mgfencrypt(&r, 0, b, sizeof(b));
+ BURN(r);
+
+ /* --- Verify the MAC --- */
+
+ rmd160_hmacinit(&mk, b + RMD160_HASHSZ, RMD160_HASHSZ);
+ rmd160_macinit(&mc, &mk);
+ rmd160_machash(&mc, k->u.k.k + RMD160_HASHSZ * 2, sz);
+ rmd160_macdone(&mc, b + RMD160_HASHSZ);
+ if (memcmp(b + RMD160_HASHSZ, k->u.k.k + RMD160_HASHSZ,
+ RMD160_HASHSZ) != 0) {
+ rc = KERR_BADPASS;
+ goto fail;
}
+ BURN(mk);
+ BURN(mc);
+
+ /* --- Allocate a destination buffer --- */
+
+ p = xmalloc(sz);
/* --- Decrypt the key data --- */
- {
- blowfish_cbcctx c;
- blowfish_cbcinit(&c, hash, sizeof(hash), 0);
- blowfish_cbcdecrypt(&c, k->u.k.k + 4, p, sz);
- BURN(hash);
- BURN(c);
- if (LOAD32(p) != 0) {
- passphrase_cancel(tag);
- goto fail_1;
- }
- }
+ blowfish_cbcinit(&c, b, RMD160_HASHSZ, 0);
+ blowfish_cbcdecrypt(&c, k->u.k.k + RMD160_HASHSZ * 2, p, sz);
+ BURN(c);
/* --- Decode the key data into the destination buffer --- */
- if (k == kt) {
- key_destroy(k);
- passphrase_cancel(tag);
+ if ((kd = key_decode(p, sz)) == 0) {
+ rc = KERR_MALFORMED;
+ goto fail;
}
- if (key_decode(p + 4, sz - 4, kt))
- goto fail_1;
+ *kt = kd;
/* --- Done --- */
- free(p);
+ xfree(p);
+ if (drop) key_drop(k);
return (0);
/* --- Tidy up if things went wrong --- */
-fail_1:
- free(p);
-fail_0:
- return (-1);
+fail:
+ BURN(b);
+ xfree(p);
+ return (rc);
+}
+
+/* --- @key_plock@ --- *
+ *
+ * Arguments: @key_data **kt@ = where to store the destination pointer
+ * @key_data *k@ = source key data block or null to use @*kt@
+ * @const char *tag@ = tag to use for passphrase
+ *
+ * Returns: Zero if successful, a @KERR@ error code on failure.
+ *
+ * Use: Locks a key by encrypting it with a passphrase.
+ */
+
+int key_plock(key_data **kt, key_data *k, const char *tag)
+{
+ char buf[256];
+
+ if (passphrase_read(tag, PMODE_VERIFY, buf, sizeof(buf)))
+ return (KERR_IO);
+ key_lock(kt, k, buf, strlen(buf));
+ BURN(buf);
+ return (0);
+}
+
+/* --- @key_punlock@ --- *
+ *
+ * Arguments: @key_data **kt@ = where to store the destination pointer
+ * @key_data *k@ = source key data block or null to use @*kt@
+ * @const char *tag@ = tag to use for passphrase
+ *
+ * Returns: Zero if it worked, a @KERR_@ error code on failure.
+ *
+ * Use: Unlocks a passphrase-locked key.
+ */
+
+int key_punlock(key_data **kt, key_data *k, const char *tag)
+{
+ char buf[256];
+ int rc;
+
+ if (passphrase_read(tag, PMODE_READ, buf, sizeof(buf)))
+ return (KERR_IO);
+ rc = key_unlock(kt, k, buf, strlen(buf));
+ BURN(buf);
+ if (rc == KERR_BADPASS || !k)
+ passphrase_cancel(tag);
+ return (rc);
}
/*----- That's all, folks -------------------------------------------------*/