Commit 470a17a2 authored by johan's avatar johan

Switch cache from XML to sqlite

- add binding ZID/sipURI in cache to prevent evil friend attack
- expose read/write cache API for external use(LIME)
- add callback to give information to user(cache mismatch only for now)
- stick to RFC 4.5.2 for exported key generation
  (but use the ZRTP Client ID to ensure backward compatibility)
- direct access to exported key computation(not anymore related to cache)
- add cache related tests

Note: no cache migration function provided
parent 556335fe
......@@ -27,6 +27,7 @@ project(bzrtp VERSION 1.0.5 LANGUAGES C CXX)
option(ENABLE_SHARED "Build shared library." YES)
option(ENABLE_STATIC "Build static library." YES)
option(ENABLE_ZIDCACHE "Turn on compilation of ZID cache, request sqlite" YES)
option(ENABLE_STRICT "Build with strict compile options." YES)
option(ENABLE_TESTS "Enable compilation of unit tests." NO)
......@@ -54,10 +55,8 @@ else()
find_package(BcToolbox 0.0.3 REQUIRED OPTIONAL_COMPONENTS tester)
endif()
find_package(XML2)
if(XML2_FOUND)
set(HAVE_LIBXML2 1)
if(ENABLE_ZIDCACHE)
find_package(Sqlite3 REQUIRED)
endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h)
......@@ -95,10 +94,14 @@ include_directories(
set(LIBS ${BCTOOLBOX_CORE_LIBRARIES})
if(XML2_FOUND)
include_directories(${XML2_INCLUDE_DIRS})
list(APPEND LIBS ${XML2_LIBRARIES})
if(SQLITE3_FOUND)
include_directories(${SQLITE3_INCLUDE_DIRS})
list(APPEND LIBS ${SQLITE3_LIBRARIES})
if(ENABLE_ZIDCACHE)
add_definitions("-DZIDCACHE_ENABLED")
endif()
endif()
if(MSVC)
include_directories(${MSVC_INCLUDE_DIR})
endif()
......
############################################################################
# FindXML2.txt
# Copyright (C) 2015 Belledonne Communications, Grenoble France
# FindSqlite3.cmake
# Copyright (C) 2014 Belledonne Communications, Grenoble France
#
############################################################################
#
......@@ -20,38 +20,38 @@
#
############################################################################
#
# - Find the libxml2 include file and library
# - Find the sqlite3 include file and library
#
# XML2_FOUND - system has libxml2
# XML2_INCLUDE_DIRS - the libxml2 include directory
# XML2_LIBRARIES - The libraries needed to use libxml2
# SQLITE3_FOUND - system has sqlite3
# SQLITE3_INCLUDE_DIRS - the sqlite3 include directory
# SQLITE3_LIBRARIES - The libraries needed to use sqlite3
if(APPLE AND NOT IOS)
set(XML2_HINTS "/usr")
set(SQLITE3_HINTS "/usr")
endif()
if(XML2_HINTS)
set(XML2_LIBRARIES_HINTS "${XML2_HINTS}/lib")
if(SQLITE3_HINTS)
set(SQLITE3_LIBRARIES_HINTS "${SQLITE3_HINTS}/lib")
endif()
find_path(XML2_INCLUDE_DIRS
NAMES libxml/xmlreader.h
HINTS "${XML2_HINTS}"
PATH_SUFFIXES include/libxml2
find_path(SQLITE3_INCLUDE_DIRS
NAMES sqlite3.h
HINTS "${SQLITE3_HINTS}"
PATH_SUFFIXES include
)
if(XML2_INCLUDE_DIRS)
set(HAVE_LIBXML_XMLREADER_H 1)
if(SQLITE3_INCLUDE_DIRS)
set(HAVE_SQLITE3_H 1)
endif()
find_library(XML2_LIBRARIES
NAMES xml2
HINTS "${XML2_LIBRARIES_HINTS}"
find_library(SQLITE3_LIBRARIES
NAMES sqlite3
HINTS "${SQLITE3_LIBRARIES_HINTS}"
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(XML2
find_package_handle_standard_args(Sqlite3
DEFAULT_MSG
XML2_INCLUDE_DIRS XML2_LIBRARIES
SQLITE3_INCLUDE_DIRS SQLITE3_LIBRARIES HAVE_SQLITE3_H
)
mark_as_advanced(XML2_INCLUDE_DIRS XML2_LIBRARIES)
mark_as_advanced(SQLITE3_INCLUDE_DIRS SQLITE3_LIBRARIES HAVE_SQLITE3_H)
......@@ -24,4 +24,4 @@
#cmakedefine HAVE_CU_GET_SUITE
#cmakedefine HAVE_CU_CURSES
#cmakedefine HAVE_LIBXML2
#cmakedefine ZIDCACHE_ENABLED
......@@ -25,6 +25,7 @@
#define BZRTP_H
#include <stdint.h>
#include "bctoolbox/crypto.h"
#ifdef _MSC_VER
#ifdef BZRTP_STATIC
......@@ -115,6 +116,16 @@ typedef struct bzrtpSrtpSecrets_struct {
uint8_t sasAlgo; /**< The SAS rendering algo selected during ZRTP negotiation */
} bzrtpSrtpSecrets_t;
/* define message levels */
#define BZRTP_MESSAGE_ERROR 0x00
#define BZRTP_MESSAGE_WARNING 0x01
#define BZRTP_MESSAGE_LOG 0x02
#define BZRTP_MESSAGE_DEBUG 0x03
/* define message codes */
#define BZRTP_MESSAGE_CACHEMISMATCH 0x01
/**
* Function pointer used by bzrtp to free memory allocated by callbacks.
**/
......@@ -123,9 +134,9 @@ typedef void (*zrtpFreeBuffer_callback)(void *);
* @brief All the callback functions provided by the client needed by the ZRTP engine
*/
typedef struct bzrtpCallbacks_struct {
/* cache related functions */
int (* bzrtp_loadCache)(void *ZIDCacheData, uint8_t **cacheBuffer, uint32_t *cacheBufferSize, zrtpFreeBuffer_callback *callback); /**< Cache related function : load the whole cache file in a buffer allocated by the function, return the buffer and its size in bytes */
int (* bzrtp_writeCache)(void *ZIDCacheData, const uint8_t *input, uint32_t size); /**< Cache related function : write size bytes to cache */
/* messaging status and warnings */
int (* bzrtp_statusMessage)(void *clientData, const uint8_t messageLevel, const uint8_t messageId); /**< Sending messages to caller: error, warnings, logs */
int bzrtp_messageLevel; /**< Filter calls to this callback to levels inferiors to this setting (BZRTP_MESSAGE_ERROR, BZRTP_MESSAGE_WARNING, BZRTP_MESSAGE_LOG, BZRTP_MESSAGE_DEBUG )*/
/* sending packets */
int (* bzrtp_sendData)(void *clientData, const uint8_t *packetString, uint16_t packetLength); /**< Send a ZRTP packet to peer. Shall return 0 on success */
......@@ -135,13 +146,18 @@ typedef struct bzrtpCallbacks_struct {
int (* bzrtp_startSrtpSession)(void *clientData, const bzrtpSrtpSecrets_t *srtpSecrets, int32_t verified); /**< ZRTP process ended well, client is given the SAS and informations about the crypto algo used during ZRTP negotiation. He may start his SRTP session if not done when calling srtpSecretsAvailable */
/* ready for exported keys */
int (* bzrtp_contextReadyForExportedKeys)(void *ZIDCacheData, void *clientData, uint8_t peerZID[12], uint8_t role); /**< Tell the client that this is the time to create and store in cache any exported keys, client is given the peerZID to adress the correct node in cache and current role which is needed to set a pair of keys for IM encryption */
int (* bzrtp_contextReadyForExportedKeys)(void *clientData, int zuid, uint8_t role); /**< Tell the client that this is the time to create any exported keys, s0 is erased just after the call to this callback. Callback is given the peerZID and zuid to adress the correct node in cache and current role which is needed to set a pair of keys for IM encryption */
} bzrtpCallbacks_t;
#define ZRTP_MAGIC_COOKIE 0x5a525450
#define ZRTP_VERSION "1.10"
#define ZRTP_CLIENT_IDENTIFIER "BZRTP"
/* Client identifier can contain up to 16 characters */
/* Use it to pass bzrtp version number to peer, is it part of Hello message */
/* custom Linphone Instant Messaging Encryption depends on bzrtp version */
/* Note: ZRTP_VERSION and BZrtp version are for now both at 1.1 but they are unrelated */
#define ZRTP_CLIENT_IDENTIFIERv1_1 "BZRTPv1.1"
#define ZRTP_CLIENT_IDENTIFIER ZRTP_CLIENT_IDENTIFIERv1_1
/* error code definition */
#define BZRTP_ERROR_INVALIDCALLBACKID 0x0001
......@@ -153,14 +169,23 @@ typedef struct bzrtpCallbacks_struct {
#define BZRTP_ERROR_OUTPUTBUFFER_LENGTH 0x0040
#define BZRTP_ERROR_HELLOHASH_MISMATCH 0x0080
#define BZRTP_ERROR_CHANNELALREADYSTARTED 0x0100
#define BZRTP_ERROR_CACHEDISABLED 0x0200
/* channel status definition */
#define BZRTP_CHANNEL_NOTFOUND 0x1000
#define BZRTP_CHANNEL_INITIALISED 0x1001
#define BZRTP_CHANNEL_ONGOING 0x1002
#define BZRTP_CHANNEL_SECURE 0x1004
#define BZRTP_CHANNEL_ERROR 0x1008
#define BZRTP_CHANNEL_ERROR 0x1008
/* role mapping */
#define BZRTP_ROLE_INITIATOR 0
#define BZRTP_ROLE_RESPONDER 1
/* cache related value */
#define BZRTP_CACHE_SETUP 0x2000
#define BZRTP_CACHE_UPDATE 0x2001
#define BZRTP_CACHE_DATA_NOTFOUND 0x2002
/**
* @brief bzrtpContext_t The ZRTP engine context
* Store current state, timers, HMAC and encryption keys
......@@ -176,13 +201,15 @@ typedef struct bzrtpContext_struct bzrtpContext_t;
BZRTP_EXPORT bzrtpContext_t *bzrtp_createBzrtpContext(void);
/**
* @brief Perform some initialisation which can't be done without some callback functions:
* @brief Perform initialisation which can't be done without ZIDcache acces
* - get ZID and create the first channel context
*
* @param[in] context The context to initialise
* @param[in] selfSSRC The SSRC given to the first channel context created within the zrtpContext
*
* @return 0 on success
*/
BZRTP_EXPORT void bzrtp_initBzrtpContext(bzrtpContext_t *context, uint32_t selfSSRC);
BZRTP_EXPORT int bzrtp_initBzrtpContext(bzrtpContext_t *context, uint32_t selfSSRC);
/**
* Free memory of context structure to a channel, if all channels are freed, free the global zrtp context
......@@ -203,17 +230,17 @@ BZRTP_EXPORT int bzrtp_destroyBzrtpContext(bzrtpContext_t *context, uint32_t sel
*/
BZRTP_EXPORT int bzrtp_setCallbacks(bzrtpContext_t *context, const bzrtpCallbacks_t *cbs);
/*
* @brief Set the ZID cache data pointer in the global zrtp context
* This pointer is returned to the client by the callbacks function linked to cache: bzrtp_loadCache, bzrtp_writeCache and bzrtp_contextReadyForExportedKeys
* @param[in/out] zrtpContext The ZRTP context we're dealing with
* @param[in] selfSSRC The SSRC identifying the channel to be linked to the client Data
* @param[in] ZIDCacheData The ZIDCacheData pointer, casted to a (void *)
/**
* @brief Set the pointer allowing cache access
*
* @return 0 on success
* @param[in] zidCachePointer Used by internal function to access cache: turn into a sqlite3 pointer if cache is enabled
* @param[in] selfURI Local URI used for this communication, needed to perform cache operation, NULL terminated string, duplicated by this function
* @param[in] peerURI Peer URI used for this communication, needed to perform cache operation, NULL terminated string, duplicated by this function
*
* @return 0 or BZRTP_CACHE_SETUP(if cache is populated by this call) on success, error code otherwise
*/
BZRTP_EXPORT int bzrtp_setZIDCacheData(bzrtpContext_t *zrtpContext, void *ZIDCacheData);
BZRTP_EXPORT int bzrtp_setZIDCache(bzrtpContext_t *context, void *zidCache, const char *selfURI, const char *peerURI);
/**
* @brief Set the client data pointer in a channel context
* This pointer is returned to the client by the callbacks function, used to store associated contexts (RTP session)
......@@ -360,41 +387,96 @@ BZRTP_EXPORT int bzrtp_getSelfHelloHash(bzrtpContext_t *zrtpContext, uint32_t se
*/
BZRTP_EXPORT int bzrtp_getChannelStatus(bzrtpContext_t *zrtpContext, uint32_t selfSSRC);
#define BZRTP_CUSTOMCACHE_USEKDF 1
#define BZRTP_CUSTOMCACHE_PLAINDATA 0
/*** Cache related functions ***/
/**
* @brief Check the given sqlite3 DB and create requested tables if needed
* Also manage DB schema upgrade
* @param[in/out] db Pointer to the sqlite3 db open connection
* Use a void * to keep this API when building cacheless
*
* @return 0 on succes, BZRTP_CACHE_SETUP if cache was empty, error code otherwise
*/
BZRTP_EXPORT int bzrtp_initCache(void *db);
#define BZRTP_CUSTOMCACHE_MULTIPLETAG_FORBID 0
#define BZRTP_CUSTOMCACHE_MULTIPLETAG_ALLOW 1
/**
* @brief : retrieve ZID from cache
* ZID is randomly generated if cache is empty or inexistant
* ZID is randomly generated in case of cacheless implementation(db argument is NULL)
*
* @param[in/out] db sqlite3 database(or NULL if we don't use cache at runtime)
* Use a void * to keep this API when building cacheless
* @param[in] selfURI the sip uri of local user, NULL terminated string
* @param[out] selfZID the ZID, retrieved from cache or randomly generated
* @param[in] RNGContext A RNG context used to generate ZID if needed
*
* @return 0 on success, or BZRTP_CACHE_DATA_NOTFOUND if no ZID matching the URI was found and no RNGContext is given to generate one
*/
BZRTP_EXPORT int bzrtp_getSelfZID(void *db, const char *selfURI, uint8_t selfZID[12], bctbx_rng_context_t *RNGContext);
#define BZRTP_CACHE_LOADFILE 0x01
#define BZRTP_CACHE_DONTLOADFILE 0x00
#define BZRTP_CACHE_WRITEFILE 0x10
#define BZRTP_CACHE_DONTWRITEFILE 0x00
/**
* @brief get the cache internal id used to bind local uri(hence local ZID associated to it)<->peer uri/peer ZID.
* Providing a valid local URI(already present in cache), a peer ZID and peer URI will return the zuid creating it if needed
* Any pair ZID/sipURI shall identify an account on a device.
*
* @param[in/out] db the opened sqlite database pointer
* @param[in] selfURI local URI, must be already associated to a ZID in the DB(association is performed by any call of getSelfZID on this URI)
* @param[in] peerURI peer URI
* @param[in] peerZID peer ZID
* @param[out] zuid the internal db reference to the data row matching this particular pair of correspondant
*
* @return 0 on success, error code otherwise
*/
BZRTP_EXPORT int bzrtp_cache_getZuid(void *dbPointer, const char *selfURI, const char *peerURI, const uint8_t peerZID[12], int *zuid);
/* role mapping */
#define INITIATOR 0
#define RESPONDER 1
/**
* @brief Allow client to write data in cache in the current <peer> tag.
* Data can be written directly or ciphered using the ZRTP Key Derivation Function and current s0.
* If useKDF flag is set but no s0 is available, nothing is written in cache and an error is returned
* @brief Write(insert or update) data in cache, adressing it by zuid (ZID/URI binding id used in cache)
* Get arrays of column names, values to be inserted, lengths of theses values
* All three arrays must be the same lenght: columnsCount
* If the row isn't present in the given table, it will be inserted
*
* @param[in/out] zrtpContext The ZRTP context we're dealing with
* @param[in] peerZID The ZID identifying the peer node we want to write into
* @param[in] tagName The name of the tag to be written
* @param[in] tagNameLength The length in bytes of the tagName
* @param[in] tagContent The content of the tag to be written(a string, if KDF is used the result will be turned into an hexa string)
* @param[in] tagContentLength The length in bytes of tagContent
* @param[in] derivedDataLength Used only in KDF mode, length in bytes of the derived data to use (max 32)
* @param[in] useKDF A flag, if set to 0, write data as it is provided, if set to 1, write KDF(s0, "tagContent", KDF_Context, negotiated hash length)
* @param[in] fileFlag Flag, if LOADFILE bit is set, reload the cache buffer from file before updating.
* if WRITEFILE bit is set, update the cache file
* @param[in] multipleTagFlag Flag, if set to MULTIPLETAG_FORBID, do not allow multiple tags with the same name under the <peer> tag, otherwise allow it
* Will be effective if new value is different from existing one.
* Has no effect if useKDF flag is on: no multiple tag allowed when storing KDF value
*
* @return 0 on success, errorcode otherwise
* @param[in/out] dbPointer Pointer to an already opened sqlite db
* @param[in] zuid The DB internal id to adress the correct row(binding between local uri and peer ZID+URI)
* @param[in] tableName The name of the table to write in the db, must already exists. Null terminated string
* @param[in] columns An array of null terminated strings containing the name of the columns to update
* @param[in] values An array of buffers containing the values to insert/update matching the order of columns array
* @param[in] lengths An array of integer containing the lengths of values array buffer matching the order of columns array
* @param[in] columnsCount length common to columns,values and lengths arrays
*
* @return 0 on succes, error code otherwise
*/
BZRTP_EXPORT int bzrtp_cache_write(void *dbPointer, int zuid, char *tableName, char **columns, uint8_t **values, size_t *lengths, uint8_t columnsCount);
/**
* @brief Read data from specified table/columns from cache adressing it by zuid (ZID/URI binding id used in cache)
* Get arrays of column names, values to be read, and the number of colums to be read
* Produce an array of values(uint8_t arrays) and a array of corresponding lengths
* Values memory is allocated by this function and must be freed by caller
*
* @param[in/out] dbPointer Pointer to an already opened sqlite db
* @param[in] zuid The DB internal id to adress the correct row(binding between local uri and peer ZID+URI)
* @param[in] tableName The name of the table to read in the db. Null terminated string
* @param[in] columns An array of null terminated strings containing the name of the columns to read, the array's length is columnsCount
* @param[out] values An array of uint8_t pointers, each one will be allocated to the read value and they must be freed by caller
* @param[out] lengths An array of integer containing the lengths of values array buffer read
* @param[in] columnsCount length common to columns,values and lengths arrays
*
* @return 0 on succes, error code otherwise
*/
BZRTP_EXPORT int bzrtp_cache_read(void *dbPointer, int zuid, char *tableName, char **columns, uint8_t **values, size_t *lengths, uint8_t columnsCount);
/*
* @brief Allow client to compute an exported according to RFC section 4.5.2
* Check the context is ready(we already have a master exported key and KDF context)
* and run KDF(master exported key, "Label", KDF_Context, negotiated hash Length)
*
* @param[in] zrtpContext The ZRTP context we're dealing with
* @param[in] label Label used in the KDF
* @param[in] labelLength Length of previous buffer
* @param[out] derivedKey Buffer to store the derived key
* @param[in/out] derivedKeyLength Length of previous buffer(updated to fit actual length of data produced if too long)
*
* @return 0 on succes, error code otherwise
*/
BZRTP_EXPORT int bzrtp_addCustomDataInCache(bzrtpContext_t *zrtpContext, uint8_t peerZID[12], uint8_t *tagName, uint16_t tagNameLength, uint8_t *tagContent, uint16_t tagContentLength, uint8_t derivedDataLength, uint8_t useKDF, uint8_t fileFlag, uint8_t multipleTagFlag);
int bzrtp_exportKey(bzrtpContext_t *zrtpContext, char *label, size_t labelLength, uint8_t *derivedKey, size_t *derivedKeyLength);
#endif /* ifndef BZRTP_H */
......@@ -37,22 +37,22 @@
#define BZRTP_PARSER_ERROR_OUTOFORDER 0xa004
#define BZRTP_PARSER_ERROR_INVALIDMESSAGE 0xa008
#define BZRTP_PARSER_ERROR_INVALIDCONTEXT 0xa010
#define BZRTP_PARSER_ERROR_UNMATCHINGCONFIRMMAC 0xa020
#define BZRTP_PARSER_ERROR_UNMATCHINGCONFIRMMAC 0xa020
#define BZRTP_PARSER_ERROR_UNMATCHINGSSRC 0xa040
#define BZRTP_PARSER_ERROR_UNMATCHINGHASHCHAIN 0xa080
#define BZRTP_PARSER_ERROR_UNMATCHINGHASHCHAIN 0xa080
#define BZRTP_PARSER_ERROR_UNMATCHINGMAC 0xa100
#define BZRTP_PARSER_ERROR_UNEXPECTEDMESSAGE 0xa200
#define BZRTP_PARSER_ERROR_UNEXPECTEDMESSAGE 0xa200
#define BZRTP_PARSER_ERROR_UNMATCHINGHVI 0xa400
#define BZRTP_BUILDER_ERROR_INVALIDPACKET 0x5001
#define BZRTP_BUILDER_ERROR_INVALIDMESSAGE 0x5002
#define BZRTP_BUILDER_ERROR_INVALIDMESSAGETYPE 0x5004
#define BZRTP_BUILDER_ERROR_UNKNOWN 0x5008
#define BZRTP_BUILDER_ERROR_INVALIDMESSAGETYPE 0x5004
#define BZRTP_BUILDER_ERROR_UNKNOWN 0x5008
#define BZRTP_BUILDER_ERROR_INVALIDCONTEXT 0x5010
#define BZRTP_CREATE_ERROR_INVALIDMESSAGETYPE 0x0a01
#define BZRTP_CREATE_ERROR_INVALIDMESSAGETYPE 0x0a01
#define BZRTP_CREATE_ERROR_UNABLETOCREATECRYPTOCONTEXT 0x0a02
#define BZRTP_CREATE_ERROR_INVALIDCONTEXT 0x0a04
#define BZRTP_CREATE_ERROR_INVALIDCONTEXT 0x0a04
/* map all message type to an uint8_t value */
#define MSGTYPE_INVALID 0x00
......
......@@ -40,11 +40,10 @@
#include <stdint.h>
/* some include needed for the cache */
#ifdef HAVE_LIBXML2
#include <libxml/tree.h>
#include <libxml/parser.h>
#endif
#ifdef ZIDCACHE_ENABLED
#include "sqlite3.h"
#endif /* ZIDCACHE_ENABLED */
typedef struct bzrtpChannelContext_struct bzrtpChannelContext_t;
......@@ -52,6 +51,10 @@ typedef struct bzrtpChannelContext_struct bzrtpChannelContext_t;
#include "packetParser.h"
#include "stateMachine.h"
/* logging */
#define BCTBX_LOG_DOMAIN "bzrtp"
#include "bctoolbox/logging.h"
#ifdef _WIN32
#define snprintf _snprintf
#endif
......@@ -185,9 +188,8 @@ struct bzrtpChannelContext_struct {
/**
* @brief structure of the ZRTP engine context
* Store current state, timers, HMAC and encryption keys
*/
*/
struct bzrtpContext_struct {
void *ZIDCacheData; /**< this is a pointer provided by the client which is then resent as a parameter of the ZID cache related callbacks functions. */
/* contexts */
bctbx_rng_context_t *RNGContext; /**< context for random number generation */
bctbx_DHMContext_t *DHMContext; /**< context for the Diffie-Hellman-Merkle operations. Only one DHM computation may be done during a call, so this belongs to the general context and not the channel one */
......@@ -218,11 +220,17 @@ struct bzrtpContext_struct {
uint8_t supportedSas[7]; /**< list of supported Sas representations mapped to uint8_t */
/* ZIDs and cache */
#ifdef HAVE_LIBXML2
xmlDocPtr cacheBuffer; /**< cache file is load in this buffer to be parsed/written */
#endif
#ifdef ZIDCACHE_ENABLED
sqlite3 *zidCache; /**< an sqlite3 db pointer to the zid cache **/
#else
void *zidCache; /**< an empty pointer always set to NULL when cache is disabled **/
#endif /* ZIDCACHE_ENABLED */
int zuid; /**< internal id used to address zid cache SIP/ZID pair binding **/
char *selfURI; /**< a null terminated string storing the local user URI **/
uint8_t selfZID[12]; /**< The ZRTP Identifier of this ZRTP end point - a random if running cache less */
char *peerURI; /**< a null terminated string storing the peer user URI **/
uint8_t peerZID[12]; /**< The ZRTP Identifier of the peer ZRTP end point - given by the Hello packet */
uint32_t peerBzrtpVersion; /**< The Bzrtp library version used by peer, retrieved from the peer Hello packet Client identifier and used for backward compatibility in exported key computation */
cachedSecrets_t cachedSecret; /**< the local cached secrets */
cachedSecretsHash_t initiatorCachedSecretHash; /**< The hash of cached secret from initiator side, computed as described in rfc section 4.3.1 */
cachedSecretsHash_t responderCachedSecretHash; /**< The hash of cached secret from responder side, computed as described in rfc section 4.3.1 */
......@@ -231,6 +239,8 @@ struct bzrtpContext_struct {
/* keys */
uint8_t *ZRTPSess; /**< ZRTP session key as described in rfc section 4.5.2 */
uint8_t ZRTPSessLength; /**< length of ZRTP session key depends on agreed hash algorithm */
uint8_t *exportedKey; /**< computed as in rfc section 4.5.2 only if needed */
uint8_t exportedKeyLength; /**< length of previous buffer, shall be channel[0]->hashLength */
};
......
......@@ -8,7 +8,7 @@
@author Johan Pascal
@copyright Copyright (C) 2014 Belledonne Communications, Grenoble, France
@copyright Copyright (C) 2017 Belledonne Communications, Grenoble, France
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
......@@ -29,62 +29,22 @@
#include "typedef.h"
#define ZRTP_ZIDCACHE_INVALID_CONTEXT 0x2001
#define ZRTP_ZIDCACHE_INVALID_CACHE 0x2002
#define ZRTP_ZIDCACHE_UNABLETOUPDATE 0x2003
/**
* @brief : retrieve ZID from cache
* ZID is randomly generated if cache is empty or inexistant
* ZID is randamly generated in case of cacheless implementation
*
* @param[in] context The current zrpt context, used to access the random function if needed
* and the ZID cache access function
* @param[out] selfZID The ZID, retrieved from cache or randomly generated
*
* @return 0 on success
*/
int bzrtp_getSelfZID(bzrtpContext_t *context, uint8_t selfZID[12]);
#define BZRTP_ZIDCACHE_INVALID_CONTEXT 0x2001
#define BZRTP_ZIDCACHE_INVALID_CACHE 0x2002
#define BZRTP_ZIDCACHE_UNABLETOUPDATE 0x2003
#define BZRTP_ZIDCACHE_UNABLETOREAD 0x2004
#define BZRTP_ZIDCACHE_BADINPUTDATA 0x2005
#define BZRTP_ZIDCACHE_RUNTIME_CACHELESS 0x2010
/**
* @brief Parse the cache to find secrets associated to the given ZID, set them and their length in the context if they are found
* Note: this function also retrieve zuid(set in the context) wich allow successive calls to cache operation to be faster.
*
* @param[in/out] context the current context, used to get the negotiated Hash algorithm and cache access functions and store result
* @param[in/out] context the current context, used to get the cache db pointer, self and peer URI and store results
* @param[in] peerZID a byte array of the peer ZID
*
* return 0 on succes, error code otherwise
*/
BZRTP_EXPORT int bzrtp_getPeerAssociatedSecretsHash(bzrtpContext_t *context, uint8_t peerZID[12]);
/* Define for write peer flags */
/* define positions for bit into the flags */
#define BZRTP_CACHE_ISSTRINGBIT 0x01
#define BZRTP_CACHE_MULTIPLETAGSBIT 0x10
#define BZRTP_CACHE_LOADFILEBIT 0x01
#define BZRTP_CACHE_WRITEFILEBIT 0x10
BZRTP_EXPORT int bzrtp_getPeerAssociatedSecrets(bzrtpContext_t *context, uint8_t peerZID[12]);
#define BZRTP_CACHE_TAGISSTRING 0x01
#define BZRTP_CACHE_TAGISBYTE 0x00
#define BZRTP_CACHE_ALLOWMULTIPLETAGS 0x10
#define BZRTP_CACHE_NOMULTIPLETAGS 0x00
/**
* @brief Write the given taf into peer Node, if the tag exists, content is replaced
* Cache file is locked(TODO), read and updated during this call
*
* @param[in/out] context the current context, used to get the negotiated Hash algorithm and cache access functions and store result
* @param[in] peerZID a byte array of the peer ZID
* @param[in] tagName the tagname of node to be written, it MUST be null terminated
* @param[in] tagNameLength the length of tagname (not including the null termination char)
* @param[in] tagContent the content of the node(a byte buffer which will be converted to hexa string)
* @param[in] tagContentLength the length of the content to be written(not including the null termination if it is a string)
* @param[in] nodeFlag Flag, if the ISSTRING bit is set write directly the value into the tag, otherwise convert the byte buffer to hexa string
* if the MULTIPLETAGS bit is set, allow multiple tags with the same name inside the peer node(only if their value differs)
* @param[in] fileFlag Flag, if LOADFILE bit is set, reload the cache buffer from file before updatin.
* if WRITEFILE bit is set, update the cache file
*
* return 0 on success, error code otherwise
*/
int bzrtp_writePeerNode(bzrtpContext_t *context, uint8_t peerZID[12], uint8_t *tagName, uint8_t tagNameLength, uint8_t *tagContent, uint32_t tagContentLength, uint8_t nodeFlag, uint8_t fileFlag);
#endif /* ZIDCACHE_H */
......@@ -64,8 +64,6 @@ bzrtpContext_t *bzrtp_createBzrtpContext(void) {
context->isInitialised = 0; /* will be set by bzrtp_initBzrtpContext */
/* set to NULL all callbacks pointer */
context->zrtpCallbacks.bzrtp_loadCache = NULL;
context->zrtpCallbacks.bzrtp_writeCache = NULL;
context->zrtpCallbacks.bzrtp_sendData = NULL;
context->zrtpCallbacks.bzrtp_srtpSecretsAvailable = NULL;
context->zrtpCallbacks.bzrtp_startSrtpSession = NULL;
......@@ -86,9 +84,10 @@ bzrtpContext_t *bzrtp_createBzrtpContext(void) {
context->sc = bzrtpUtils_getAvailableCryptoTypes(ZRTP_SAS_TYPE, context->supportedSas);
/* initialise cached secret buffer to null */
#ifdef HAVE_LIBXML2
context->cacheBuffer = NULL; /* this field is present only if cache is compiled*/
#endif
context->zidCache = NULL; /* a pointer to the sqlite3 db accessor, can be NULL if running cacheless */
context->zuid = 0;
context->peerBzrtpVersion = 0;
context->selfURI = NULL;
context->cachedSecret.rs1 = NULL;
context->cachedSecret.rs1Length = 0;
context->cachedSecret.rs2 = NULL;
......@@ -102,33 +101,64 @@ bzrtpContext_t *bzrtp_createBzrtpContext(void) {
/* initialise key buffers */
context->ZRTPSess = NULL;
context->ZRTPSessLength = 0;
context->exportedKey = NULL;
context->exportedKeyLength = 0;
return context;
}
/**
* @brief Set the pointer allowing cache access
*
* @param[in] zidCachePointer Used by internal function to access cache: turn into a sqlite3 pointer if cache is enabled
*
* @return 0 or BZRTP_CACHE_SETUP(if cache is populated by this call) on success, error code otherwise
*/
int bzrtp_setZIDCache(bzrtpContext_t *context, void *zidCache, const char *selfURI, const char *peerURI) {
#ifdef ZIDCACHE_ENABLED
/* is zrtp context valid */
if (context==NULL) {
return BZRTP_ERROR_INVALIDCONTEXT;
}
/* zidCache pointer is actually a pointer to sqlite3 db, store it in context */
context->zidCache = (sqlite3 *)zidCache;
context->selfURI = strdup(selfURI);
context->peerURI = strdup(peerURI);
/* and init the cache(create needed tables if they don't exist) */
return bzrtp_initCache(context->zidCache);
#else /* ZIDCACHE_ENABLED */
return BZRTP_ERROR_CACHEDISABLED;
#endif /* ZIDCACHE_ENABLED */
}
/**
* @brief Perform some initialisation which can't be done without some callback functions:
* This function is called once per session when the first channel is created.
* It must be called after the cache callback function have been set
* - load cache
* - get ZID from cache or generate it
*
* Initialise the first channel
* It must be called after the cache access pointer have been set
* - Get ZID from cache or generate a random ZID
* - Initialise the first channel
*
* @param[in] context The context to initialise
* #param[in] selfSSRC SSRC of the first channel
* @param[in] selfSSRC SSRC of the first channel
* @return 0 on success
*/
void bzrtp_initBzrtpContext(bzrtpContext_t *context, uint32_t selfSSRC) {
int bzrtp_initBzrtpContext(bzrtpContext_t *context, uint32_t selfSSRC) {
/* initialise ZID. Randomly generated if no ZID is found in cache or no cache found */
/* This call will load the cache or create it if the cache callback functions are not null*/
bzrtp_getSelfZID(context, context->selfZID);
/* is zrtp context valid */
if (context==NULL) {
return BZRTP_ERROR_INVALIDCONTEXT;
}
/* initialise self ZID. Randomly generated if no ZID is found in cache or no cache found */
bzrtp_getSelfZID(context->zidCache, context->selfURI, context->selfZID, context->RNGContext);
context->isInitialised = 1;
/* allocate 1 channel context, set all the others pointers to NULL */
context->channelContext[0] = (bzrtpChannelContext_t *)malloc(sizeof(bzrtpChannelContext_t));
memset(context->channelContext[0], 0, sizeof(bzrtpChannelContext_t));
bzrtp_initChannelContext(context, context->channelContext[0], selfSSRC, 1);
return bzrtp_initChannelContext(context, context->channelContext[0], selfSSRC, 1);
}
/*
......@@ -200,10 +230,14 @@ int bzrtp_destroyBzrtpContext(bzrtpContext_t *context, uint32_t selfSSRC) {
context->ZRTPSess=NULL;
}
#ifdef HAVE_LIBXML2
xmlFreeDoc(context->cacheBuffer);
context->cacheBuffer=NULL;
#endif
if (context->exportedKey!=NULL) {
bzrtp_DestroyKey(context->exportedKey, context->exportedKeyLength, context->RNGContext);
free(context->exportedKey);
context->ZRTPSess=NULL;
}
free(context->selfURI);
free(context->peerURI);
/* destroy the RNG context at the end because it may be needed to destroy some keys */
bctbx_rng_context_free(context->RNGContext);
......@@ -220,6 +254,11 @@ int bzrtp_destroyBzrtpContext(bzrtpContext_t *context, uint32_t selfSSRC) {
* @return 0 on success
*/
int bzrtp_setCallbacks(bzrtpContext_t *context, const bzrtpCallbacks_t *cbs) {
/* is zrtp context valid */
if (context==NULL) {
return BZRTP_ERROR_INVALIDCONTEXT;
}
context->zrtpCallbacks=*cbs;
return 0;
}
......@@ -370,26 +409,6 @@ int bzrtp_iterate(bzrtpContext_t *zrtpContext, uint32_t selfSSRC, uint64_t timeR
return 0;
}
/*
* @brief Set the ZID cache data pointer in the global zrtp context
* This pointer is returned to the client by the callbacks function linked to cache: bzrtp_loadCache, bzrtp_writeCache and bzrtp_contextReadyForExportedKeys
* @param[in/out] zrtpContext The ZRTP context we're dealing with
* @param[in] selfSSRC The SSRC identifying the channel to be linked to the client Data
* @param[in] ZIDCacheData The ZIDCacheData pointer, casted to a (void *)
*
* @return 0 on success
*
*/
int bzrtp_setZIDCacheData(bzrtpContext_t *zrtpContext, void *ZIDCacheData) {
if (zrtpContext == NULL) {
return BZRTP_ERROR_INVALIDCONTEXT;
}
zrtpContext->ZIDCacheData = ZIDCacheData;
return 0;
}
/*
* @brief Set the client data pointer in a channel context
* This pointer is returned to the client by the callbacks function, used to store associated contexts (RTP session)
......@@ -481,8 +500,7 @@ int bzrtp_processMessage(bzrtpContext_t *zrtpContext, uint32_t selfSSRC, uint8_t
event.zrtpContext = zrtpContext;
event.zrtpChannelContext = zrtpChannelContext;
retval = zrtpChannelContext->stateMachine(event);
return retval;
return zrtpChannelContext->stateMachine(event);
}
/*
......@@ -494,13 +512,16 @@ int bzrtp_processMessage(bzrtpContext_t *zrtpContext, uint32_t selfSSRC, uint8_t
void bzrtp_SASVerified(bzrtpContext_t *zrtpContext) {
if (zrtpContext != NULL) {
uint8_t pvsFlag = 1;
char *colNames[] = {"pvs"};
uint8_t *colValues[] = {&pvsFlag};
size_t colLength[] = {1};
/* check if we must update the cache(delayed until sas verified in case of cache mismatch) */
if (zrtpContext->cacheMismatchFlag == 1) {
zrtpContext->cacheMismatchFlag = 0;
bzrtp_updateCachedSecrets(zrtpContext, zrtpContext->channelContext[0]); /* channel[0] is the only one in DHM mode, so the only one able to have a cache mismatch */
}
bzrtp_writePeerNode(zrtpContext, zrtpContext->peerZID, (uint8_t *)"pvs", 3, &pvsFlag, 1, BZRTP_CACHE_TAGISBYTE|BZRTP_CACHE_NOMULTIPLETAGS, BZRTP_CACHE_LOADFILE|BZRTP_CACHE_WRITEFILE);
bzrtp_cache_write(zrtpContext->zidCache, zrtpContext->zuid, "zrtp", colNames, colValues, colLength, 1);
}
}
......@@ -513,64 +534,66 @@ void bzrtp_SASVerified(bzrtpContext_t *zrtpContext) {
void bzrtp_resetSASVerified(bzrtpContext_t *zrtpContext) {
if (zrtpContext != NULL) {
uint8_t pvsFlag = 0;
bzrtp_writePeerNode(zrtpContext, zrtpContext->peerZID, (uint8_t *)"pvs", 3, &pvsFlag, 1, BZRTP_CACHE_TAGISBYTE|BZRTP_CACHE_NOMULTIPLETAGS, BZRTP_CACHE_LOADFILE|BZRTP_CACHE_WRITEFILE);
char *colNames[] = {"pvs"};
uint8_t *colValues[] = {&pvsFlag};
size_t colLength[] = {1};
bzrtp_cache_write(zrtpContext->zidCache, zrtpContext->zuid, "zrtp", colNames, colValues, colLength, 1);
}
}
/*
* @brief Allow client to write data in cache in the current <peer> tag.
* Data can be written directly or ciphered using the ZRTP Key Derivation Function and current s0.
* If useKDF flag is set but no s0 is available, nothing is written in cache and an error is returned
* @brief Allow client to compute an exported according to RFC section 4.5.2
* Check the context is ready(we already have a master exported key and KDF context)
* and run KDF(master exported key, "Label", KDF_Context, negotiated hash Length)
*
* @param[in/out] zrtpContext The ZRTP context we're dealing with
* @param[in] peerZID The ZID identifying the peer node we want to write into
* @param[in] tagName The name of the tag to be written