Commit e1b40134 authored by cypherpunk's avatar cypherpunk
Browse files

All-new implementation of version 2 AKE.

parent bcc90e32
This diff is collapsed.
......@@ -179,7 +179,7 @@ Here are the six programs in the toolkit:
- otr_parse
- Parse OTR messages given on stdin, showing the values of all the
fields in OTR Key Exchange Messages and OTR Data Messages.
fields in OTR protocol messages.
- otr_sesskeys our_privkey their_pubkey
- Shows our public key, the session id, two AES and two MAC keys
......
......@@ -3,11 +3,11 @@ INCLUDES = @LIBGCRYPT_CFLAGS@
lib_LTLIBRARIES = libotr.la
libotr_la_SOURCES = privkey.c context.c proto.c b64.c dh.c mem.c message.c \
userstate.c tlv.c
userstate.c tlv.c auth.c
libotr_la_LDFLAGS = -version-info @LIBOTR_LIBTOOL_VERSION@ @LIBS@ @LIBGCRYPT_LIBS@
otrincdir = $(includedir)/libotr
otrinc_HEADERS = b64.h context.h dh.h mem.h message.h privkey.h proto.h \
version.h userstate.h tlv.h
version.h userstate.h tlv.h serial.h auth.h privkey-t.h
This diff is collapsed.
/*
* Off-the-Record Messaging library
* Copyright (C) 2004-2005 Nikita Borisov and Ian Goldberg
* <otr@cypherpunks.ca>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of version 2.1 of the GNU Lesser General
* Public License as published by the Free Software Foundation.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __AUTH_H__
#define __AUTH_H__
#include <gcrypt.h>
#include "dh.h"
typedef enum {
OTRL_AUTHSTATE_NONE,
OTRL_AUTHSTATE_AWAITING_DHKEY,
OTRL_AUTHSTATE_AWAITING_REVEALSIG,
OTRL_AUTHSTATE_AWAITING_SIG,
OTRL_AUTHSTATE_V1_SETUP
} OtrlAuthState;
typedef struct {
OtrlAuthState authstate; /* Our state */
DH_keypair our_dh; /* Our D-H key */
unsigned int our_keyid; /* ...and its keyid */
unsigned char *encgx; /* The encrypted value of g^x */
size_t encgx_len; /* ...and its length */
unsigned char r[16]; /* The encryption key */
unsigned char hashgx[32]; /* SHA256(g^x) */
gcry_mpi_t their_pub; /* Their D-H public key */
unsigned int their_keyid; /* ...and its keyid */
gcry_cipher_hd_t enc_c, enc_cp; /* c and c' encryption keys */
gcry_md_hd_t mac_m1, mac_m1p; /* m1 and m1' MAC keys */
gcry_md_hd_t mac_m2, mac_m2p; /* m2 and m2' MAC keys */
unsigned char their_fingerprint[20]; /* The fingerprint of their
long-term signing key */
int initiated; /* Did we initiate this
authentication? */
unsigned int protocol_version; /* The protocol version number
used to authenticate. */
unsigned char secure_session_id[20]; /* The secure session id */
size_t secure_session_id_len; /* And its actual length,
which may be either 20 (for
v1) or 8 (for v2) */
OtrlSessionIdHalf session_id_half; /* Which half of the session
id gets shown in bold */
char *lastauthmsg; /* The last auth message
(base-64 encoded) we sent,
in case we need to
retransmit it. */
} OtrlAuthInfo;
#include "privkey-t.h"
/*
* Initialize the fields of an OtrlAuthInfo (already allocated).
*/
void otrl_auth_new(OtrlAuthInfo *auth);
/*
* Clear the fields of an OtrlAuthInfo (but leave it allocated).
*/
void otrl_auth_clear(OtrlAuthInfo *auth);
/*
* Start a fresh AKE (version 2) using the given OtrlAuthInfo. If
* our_dh is NULL, generate a fresh DH keypair to use. Otherwise, use a
* copy of the one passed (with the given keyid). If no error is
* returned, the message to transmit will be contained in
* auth->lastauthmsg.
*/
gcry_error_t otrl_auth_start_v2(OtrlAuthInfo *auth, DH_keypair *our_dh,
unsigned int our_keyid);
/*
* Handle an incoming D-H Commit Message. If no error is returned, the
* message to send will be left in auth->lastauthmsg. If non-NULL, use
* a copy of the given D-H keypair, with the given keyid.
*/
gcry_error_t otrl_auth_handle_commit(OtrlAuthInfo *auth, const char *commitmsg,
DH_keypair *our_dh, unsigned int our_keyid);
/*
* Handle an incoming D-H Key Message. If no error is returned, and
* *havemsgp is 1, the message to sent will be left in auth->lastauthmsg.
* Use the given private authentication key to sign messages.
*/
gcry_error_t otrl_auth_handle_key(OtrlAuthInfo *auth, const char *keymsg,
int *havemsgp, OtrlPrivKey *privkey);
/*
* Handle an incoming Reveal Signature Message. If no error is
* returned, and *havemsgp is 1, the message to be sent will be left in
* auth->lastauthmsg. Use the given private authentication key to sign
* messages. Call the auth_succeeded callback if authentication is
* successful.
*/
gcry_error_t otrl_auth_handle_revealsig(OtrlAuthInfo *auth,
const char *revealmsg, int *havemsgp, OtrlPrivKey *privkey,
gcry_error_t (*auth_succeeded)(const OtrlAuthInfo *auth, void *asdata),
void *asdata);
/*
* Handle an incoming Signature Message. If no error is returned, and
* *havemsgp is 1, the message to be sent will be left in
* auth->lastauthmsg. Call the auth_succeeded callback if
* authentication is successful.
*/
gcry_error_t otrl_auth_handle_signature(OtrlAuthInfo *auth,
const char *sigmsg, int *havemsgp,
gcry_error_t (*auth_succeeded)(const OtrlAuthInfo *auth, void *asdata),
void *asdata);
/*
* Start a fresh AKE (version 1) using the given OtrlAuthInfo. If
* our_dh is NULL, generate a fresh DH keypair to use. Otherwise, use a
* copy of the one passed (with the given keyid). Use the given private
* key to sign the message. If no error is returned, the message to
* transmit will be contained in auth->lastauthmsg.
*/
gcry_error_t otrl_auth_start_v1(OtrlAuthInfo *auth, DH_keypair *our_dh,
unsigned int our_keyid, OtrlPrivKey *privkey);
/*
* Handle an incoming v1 Key Exchange Message. If no error is returned,
* and *havemsgp is 1, the message to be sent will be left in
* auth->lastauthmsg. Use the given private authentication key to sign
* messages. Call the auth_secceeded callback if authentication is
* successful. If non-NULL, use a copy of the given D-H keypair, with
* the given keyid.
*/
gcry_error_t otrl_auth_handle_v1_key_exchange(OtrlAuthInfo *auth,
const char *keyexchmsg, int *havemsgp, OtrlPrivKey *privkey,
DH_keypair *our_dh, unsigned int our_keyid,
gcry_error_t (*auth_succeeded)(const OtrlAuthInfo *auth, void *asdata),
void *asdata);
#endif
......@@ -56,6 +56,7 @@ VERSION HISTORY:
/* system headers */
#include <stdlib.h>
#include <string.h>
/* libotr headers */
#include "b64.h"
......@@ -185,3 +186,65 @@ size_t otrl_base64_decode(char *data, const unsigned char *base64data,
return datalen;
}
/*
* Base64-encode a block of data, stick "?OTR:" and "." around it, and
* return the result, or NULL in the event of a memory error. The
* caller must free() the return value.
*/
char *otrl_base64_otr_encode(const unsigned char *buf, size_t buflen)
{
char *base64buf;
size_t base64len;
/* Make the base64-encoding. */
base64len = ((buflen + 2) / 3) * 4;
base64buf = malloc(5 + base64len + 1 + 1);
if (base64buf == NULL) {
return NULL;
}
memmove(base64buf, "?OTR:", 5);
otrl_base64_encode(base64buf+5, buf, buflen);
base64buf[5 + base64len] = '.';
base64buf[5 + base64len + 1] = '\0';
return base64buf;
}
/*
* Base64-decode the portion of the given message between "?OTR:" and
* ".". Set *bufp to the decoded data, and set *lenp to its length.
* The caller must free() the result. Return 0 on success, -1 on a
* memory error, or -2 on invalid input.
*/
int otrl_base64_otr_decode(const char *msg, unsigned char **bufp,
size_t *lenp)
{
char *otrtag, *endtag;
size_t msglen, rawlen;
unsigned char *rawmsg;
otrtag = strstr(msg, "?OTR:");
if (!otrtag) {
return -2;
}
endtag = strchr(otrtag, '.');
if (endtag) {
msglen = endtag-otrtag;
} else {
return -2;
}
/* Base64-decode the message */
rawlen = ((msglen-5) / 4) * 3; /* maximum possible */
rawmsg = malloc(rawlen);
if (!rawmsg && rawlen > 0) {
return -1;
}
rawlen = otrl_base64_decode(rawmsg, otrtag+5, msglen-5); /* actual size */
*bufp = rawmsg;
*lenp = rawlen;
return 0;
}
......@@ -39,4 +39,19 @@ size_t otrl_base64_encode(char *base64data, const unsigned char *data,
size_t otrl_base64_decode(char *data, const unsigned char *base64data,
size_t base64len);
/*
* Base64-encode a block of data, stick "?OTR:" and "." around it, and
* return the result, or NULL in the event of a memory error.
*/
char *otrl_base64_otr_encode(const unsigned char *buf, size_t buflen);
/*
* Base64-decode the portion of the given message between "?OTR:" and
* ".". Set *bufp to the decoded data, and set *lenp to its length.
* The caller must free() the result. Return 0 on success, -1 on a
* memory error, or -2 on invalid input.
*/
int otrl_base64_otr_decode(const char *msg, unsigned char **bufp,
size_t *lenp);
#endif
......@@ -27,10 +27,6 @@
/* libotr headers */
#include "context.h"
/* Strings describing the connection states */
const char *otrl_context_statestr[] =
{ "Not private", "Setting up", "Private" };
/* Create a new connection context. */
static ConnContext * new_context(const char * user, const char * accountname,
const char * protocol)
......@@ -45,7 +41,8 @@ static ConnContext * new_context(const char * user, const char * accountname,
context->fragment_len = 0;
context->fragment_n = 0;
context->fragment_k = 0;
context->state = CONN_UNCONNECTED;
context->msgstate = OTRL_MSGSTATE_PLAINTEXT;
otrl_auth_new(&(context->auth));
context->fingerprint_root.fingerprint = NULL;
context->fingerprint_root.context = context;
context->fingerprint_root.next = NULL;
......@@ -66,6 +63,7 @@ static ConnContext * new_context(const char * user, const char * accountname,
otrl_dh_session_blank(&(context->sesskeys[1][0]));
otrl_dh_session_blank(&(context->sesskeys[1][1]));
memset(context->sessionid, 0, 20);
context->sessionid_len = 0;
context->numsavedkeys = 0;
context->preshared_secret = NULL;
context->preshared_secret_len = 0;
......@@ -187,11 +185,11 @@ void otrl_context_set_preshared_secret(ConnContext *context,
}
}
/* Force a context into the CONN_SETUP state (so that it only has local
* DH keys). */
void otrl_context_force_setup(ConnContext *context)
/* Force a context into the OTRL_MSGSTATE_FINISHED state. */
void otrl_context_force_finished(ConnContext *context)
{
context->state = CONN_SETUP;
context->msgstate = OTRL_MSGSTATE_FINISHED;
otrl_auth_clear(&(context->auth));
free(context->fragment);
context->fragment = NULL;
context->fragment_len = 0;
......@@ -203,11 +201,15 @@ void otrl_context_force_setup(ConnContext *context)
context->their_y = NULL;
gcry_mpi_release(context->their_old_y);
context->their_old_y = NULL;
context->our_keyid = 0;
otrl_dh_keypair_free(&(context->our_dh_key));
otrl_dh_keypair_free(&(context->our_old_dh_key));
otrl_dh_session_free(&(context->sesskeys[0][0]));
otrl_dh_session_free(&(context->sesskeys[0][1]));
otrl_dh_session_free(&(context->sesskeys[1][0]));
otrl_dh_session_free(&(context->sesskeys[1][1]));
memset(context->sessionid, 0, 20);
context->sessionid_len = 0;
free(context->preshared_secret);
context->preshared_secret = NULL;
context->preshared_secret_len = 0;
......@@ -219,34 +221,32 @@ void otrl_context_force_setup(ConnContext *context)
context->may_retransmit = 0;
}
/* Force a context into the CONN_UNCONNECTED state. */
void otrl_context_force_disconnect(ConnContext *context)
/* Force a context into the OTRL_MSGSTATE_PLAINTEXT state. */
void otrl_context_force_plaintext(ConnContext *context)
{
/* First clean up everything we'd need to do for the SETUP state */
otrl_context_force_setup(context);
/* First clean up everything we'd need to do for the FINISHED state */
otrl_context_force_finished(context);
/* Now clean up our pubkeys, too */
context->state = CONN_UNCONNECTED;
context->our_keyid = 0;
otrl_dh_keypair_free(&(context->our_dh_key));
otrl_dh_keypair_free(&(context->our_old_dh_key));
/* And just set the state properly */
context->msgstate = OTRL_MSGSTATE_PLAINTEXT;
}
/* Forget a fingerprint (so long as it's not the active one. If it's a
* fingerprint_root, forget the whole context (as long as
* and_maybe_context is set, and it's UNCONNECTED). Also, if it's not
* and_maybe_context is set, and it's PLAINTEXT). Also, if it's not
* the fingerprint_root, but it's the only fingerprint, and we're
* UNCONNECTED, forget the whole context if and_maybe_context is set. */
* PLAINTEXT, forget the whole context if and_maybe_context is set. */
void otrl_context_forget_fingerprint(Fingerprint *fprint,
int and_maybe_context)
{
ConnContext *context = fprint->context;
if (fprint == &(context->fingerprint_root)) {
if (context->state == CONN_UNCONNECTED && and_maybe_context) {
if (context->msgstate == OTRL_MSGSTATE_PLAINTEXT &&
and_maybe_context) {
otrl_context_forget(context);
}
} else {
if (context->state != CONN_CONNECTED ||
if (context->msgstate != OTRL_MSGSTATE_PLAINTEXT ||
context->active_fingerprint != fprint) {
free(fprint->fingerprint);
free(fprint->trust);
......@@ -255,7 +255,7 @@ void otrl_context_forget_fingerprint(Fingerprint *fprint,
fprint->next->tous = fprint->tous;
}
free(fprint);
if (context->state == CONN_UNCONNECTED &&
if (context->msgstate == OTRL_MSGSTATE_PLAINTEXT &&
context->fingerprint_root.next == NULL &&
and_maybe_context) {
/* We just deleted the only fingerprint. Forget the
......@@ -266,14 +266,14 @@ void otrl_context_forget_fingerprint(Fingerprint *fprint,
}
}
/* Forget a whole context, so long as it's UNCONNECTED. */
/* Forget a whole context, so long as it's PLAINTEXT. */
void otrl_context_forget(ConnContext *context)
{
if (context->state != CONN_UNCONNECTED) return;
if (context->msgstate != OTRL_MSGSTATE_PLAINTEXT) return;
/* Just to be safe, force a disconnect. This also frees any
/* Just to be safe, force to plaintext. This also frees any
* extraneous data lying around. */
otrl_context_force_disconnect(context);
otrl_context_force_plaintext(context);
/* First free all the Fingerprints */
while(context->fingerprint_root.next) {
......@@ -302,12 +302,11 @@ void otrl_context_forget(ConnContext *context)
free(context);
}
/* Forget all the contexts in a given OtrlUserState, forcing them to
* UNCONNECTED. */
/* Forget all the contexts in a given OtrlUserState. */
void otrl_context_forget_all(OtrlUserState us)
{
while (us->context_root) {
otrl_context_force_disconnect(us->context_root);
otrl_context_force_plaintext(us->context_root);
otrl_context_forget(us->context_root);
}
}
......@@ -20,14 +20,23 @@
#ifndef __CONTEXT_H__
#define __CONTEXT_H__
#include "gcrypt.h"
#include <gcrypt.h>
#include "dh.h"
#include "auth.h"
typedef enum {
CONN_UNCONNECTED,
CONN_SETUP,
CONN_CONNECTED
} ConnectionState;
OTRL_MSGSTATE_PLAINTEXT, /* Not yet started an encrypted
conversation */
OTRL_MSGSTATE_ENCRYPTED, /* Currently in an encrypted
conversation */
OTRL_MSGSTATE_FINISHED /* The remote side has sent us a
notification that he has ended
his end of the encrypted
conversation; prevent any
further messages from being
sent to him. */
} OtrlMessageState;
typedef struct fingerprint {
struct fingerprint *next; /* The next fingerprint in the list */
......@@ -55,8 +64,11 @@ typedef struct context {
we've seen so far for this
message */
ConnectionState state; /* The state of our connection to this
user */
OtrlMessageState msgstate; /* The state of message disposition
with this user */
OtrlAuthInfo auth; /* The state of ongoing
authentication with this user */
Fingerprint fingerprint_root; /* The root of a linked list of
Fingerprints entries */
Fingerprint *active_fingerprint; /* Which fingerprint is in use now?
......@@ -76,9 +88,9 @@ typedef struct context {
derived from DH key[our_keyid-i]
and mpi Y[their_keyid-j] */
unsigned char sessionid[20]; /* The sessionid and direction */
SessionDirection sessiondir; /* determined when this private
connection was established. */
unsigned char sessionid[20]; /* The sessionid and bold half */
size_t sessionid_len; /* determined when this private */
OtrlSessionIdHalf sessionid_half; /* connection was established. */
unsigned char *preshared_secret; /* A secret you share with this
user, in order to do
......@@ -114,9 +126,6 @@ typedef struct context {
#include "userstate.h"
/* Strings describing the connection states */
extern const char *otrl_context_statestr[];
/* Look up a connection context by name/account/protocol from the given
* OtrlUserState. If add_if_missing is true, allocate and return a new
* context if one does not currently exist. In that event, call
......@@ -141,26 +150,24 @@ void otrl_context_set_trust(Fingerprint *fprint, const char *trust);
void otrl_context_set_preshared_secret(ConnContext *context,
const unsigned char *secret, size_t secret_len);
/* Force a context into the CONN_SETUP state (so that it only has local
* DH keys). */
void otrl_context_force_setup(ConnContext *context);
/* Force a context into the OTRL_MSGSTATE_FINISHED state. */
void otrl_context_force_finished(ConnContext *context);
/* Force a context into the CONN_UNCONNECTED state. */
void otrl_context_force_disconnect(ConnContext *context);
/* Force a context into the OTRL_MSGSTATE_PLAINTEXT state. */
void otrl_context_force_plaintext(ConnContext *context);
/* Forget a fingerprint (so long as it's not the active one. If it's a
* fingerprint_root, forget the whole context (as long as
* and_maybe_context is set, and it's UNCONNECTED). Also, if it's not
* and_maybe_context is set, and it's PLAINTEXT). Also, if it's not
* the fingerprint_root, but it's the only fingerprint, and we're
* UNCONNECTED, forget the whole context if and_maybe_context is set. */
* PLAINTEXT, forget the whole context if and_maybe_context is set. */
void otrl_context_forget_fingerprint(Fingerprint *fprint,
int and_maybe_context);
/* Forget a whole context, so long as it's UNCONNECTED. */
/* Forget a whole context, so long as it's PLAINTEXT. */
void otrl_context_forget(ConnContext *context);
/* Forget all the contexts in a given OtrlUserState, forcing them to
* UNCONNECTED. */
/* Forget all the contexts in a given OtrlUserState. */
void otrl_context_forget_all(OtrlUserState us);
#endif
......@@ -40,6 +40,7 @@ static const int DH1536_MOD_LEN_BITS = 1536;
static const int DH1536_MOD_LEN_BYTES = 192;
static gcry_mpi_t DH1536_MODULUS = NULL;
static gcry_mpi_t DH1536_MODULUS_MINUS_2 = NULL;
static gcry_mpi_t DH1536_GENERATOR = NULL;
/*
......@@ -51,6 +52,28 @@ void otrl_dh_init(void)
gcry_mpi_scan(&DH1536_MODULUS, GCRYMPI_FMT_HEX, DH1536_MODULUS_S, 0, NULL);
gcry_mpi_scan(&DH1536_GENERATOR, GCRYMPI_FMT_HEX, DH1536_GENERATOR_S,
0, NULL);
DH1536_MODULUS_MINUS_2 = gcry_mpi_new(DH1536_MOD_LEN_BITS);
gcry_mpi_sub_ui(DH1536_MODULUS_MINUS_2, DH1536_MODULUS, 2);
}
/*
* Initialize the fields of a DH keypair.
*/
void otrl_dh_keypair_init(DH_keypair *kp)
{
kp->groupid = 0;
kp->priv = NULL;
kp->pub = NULL;
}
/*
* Copy a DH_keypair.
*/
void otrl_dh_keypair_copy(DH_keypair *dst, const DH_keypair *src)
{
dst->groupid = src->groupid;
dst->priv = gcry_mpi_copy(src->priv);
dst->pub = gcry_mpi_copy(src->pub);
}
/*
......@@ -129,22 +152,17 @@ gcry_error_t otrl_dh_session(DH_sesskeys *sess, const DH_keypair *kp,
gcry_mpi_print(GCRYMPI_FMT_USG, gabdata+5, gablen, NULL, gab);
gcry_mpi_release(gab);
/* Calculate the session id */
hashdata = gcry_malloc_secure(20);
if (!hashdata) {
gcry_free(gabdata);
return gcry_error(GPG_ERR_ENOMEM);
}
gabdata[0] = 0x00;
gcry_md_hash_buffer(GCRY_MD_SHA1, sess->dhsecureid, gabdata, gablen+5);
/* Are we the "high" or "low" end of the connection? */
if ( gcry_mpi_cmp(kp->pub, y) > 0 ) {
sess->dir = SESS_DIR_HIGH;
sendbyte = 0x01;
rcvbyte = 0x02;
} else {
sess->dir = SESS_DIR_LOW;
sendbyte = 0x02;
rcvbyte = 0x01;
}
......@@ -192,6 +210,209 @@ err:
return err;
}
/*
* Compute the secure session id, two encryption keys, and four MAC keys
* given our DH key and their DH public key.
*/
gcry_error_t otrl_dh_compute_v2_auth_keys(const DH_keypair *our_dh,
gcry_mpi_t their_pub, unsigned char *sessionid, size_t *sessionidlenp,
gcry_cipher_hd_t *enc_c, gcry_cipher_hd_t *enc_cp,
gcry_md_hd_t *mac_m1, gcry_md_hd_t *mac_m1p,
gcry_md_hd_t *mac_m2, gcry_md_hd_t *mac_m2p)
{
gcry_mpi_t s;
size_t slen;
unsigned char *sdata;
unsigned char *hashdata;
unsigned char ctr[16];
gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
*enc_c = NULL;
*enc_cp = NULL;
*mac_m1 = NULL;
*mac_m1p = NULL;
*mac_m2 = NULL;
*mac_m2p = NULL;
memset(ctr, 0, 16);
if (our_dh->groupid != DH1536_GROUP_ID) {
/* Invalid group id */
return gcry_error(GPG_ERR_INV_VALUE);
}
/* Check that their_pub is in range */
if (gcry_mpi_cmp_ui(their_pub, 2) < 0 ||
gcry_mpi_cmp(their_pub, DH1536_MODULUS_MINUS_2) > 0) {
/* Invalid pubkey */
return gcry_error(GPG_ERR_INV_VALUE);
}
/* Calculate the shared secret MPI */
s = gcry_mpi_new(DH1536_MOD_LEN_BITS);
gcry_mpi_powm(s, their_pub, our_dh->priv, DH1536_MODULUS);
/* Output it in the right format */
gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &slen, s);
sdata = gcry_malloc_secure(slen + 5);
if (!sdata) {
gcry_mpi_release(s);