Commit c5f42196 authored by johan's avatar johan

Manage encrypt to device without keys on the X3DH server

If keys are not found for a recipient device, set its status to fail
in the recipient list but encrypt for the other devices in the list
parent 10df643d
......@@ -52,7 +52,8 @@ namespace lime {
untrusted=0, /**< we know this device but do not trust it, that information shall be displayed to the end user, a colour code shall be enough */
trusted=1, /**< this peer device already got its public identity key validated, that information shall be displayed to the end user too */
unsafe=2, /**< this status is a helper for the library user. It is used only by the peerDeviceStatus accessor functions */
fail, /**< this status is returned by decrypt operation only, when we could not decrypt the incoming message */
fail, /**< when returned by decrypt : we could not decrypt the incoming message
when returned by encrypt in the peerStatus: we could not encrypt to this recipient(probably because it does not published keys on the X3DH° server) */
unknown /**< when returned after encryption or decryption, means it is the first time we communicate with this device (and thus create a DR session with it),
when returned by a get_peerDeviceStatus: this device is not in localStorage */
};
......@@ -170,11 +171,17 @@ namespace lime {
*
* In all cases, the identified source of the message will be the localDeviceId
*
* Note: nearly all parameters are shared pointers as the process being asynchronous, the ownership will be taken internally exempting caller to manage the buffers.
* If the X3DH server can't provide keys for a peer device, its status is set to fail and its DRmessage is empty. Other devices get their encrypted message
* If no peer device could get encrypted for all of them are missing keys on the X3DH server, the callback will still be called with success exit status
*
* @note nearly all parameters are shared pointers as the process being asynchronous, the ownership will be taken internally exempting caller to manage the buffers.
*
* @param[in] localDeviceId used to identify which local acount to use and also as the identified source of the message, shall be the GRUU
* @param[in] recipientUserId the Id of intended recipient, shall be a sip:uri of user or conference, is used as associated data to ensure no-one can mess with intended recipient
* @param[in,out] recipients a list of RecipientData holding: the recipient device Id (GRUU) and an empty buffer to store the DRmessage which must then be routed to that recipient
* @param[in,out] recipients a list of RecipientData holding:
* - the recipient device Id(GRUU)
* - an empty buffer to store the DRmessage which must then be routed to that recipient
* - the peer Status. If peerStatus is set to fail, this entry is ignored otherwise the peerStatus is set by the encrypt, see PeerDeviceStatus definition for details
* @param[in] plainMessage a buffer holding the message to encrypt, can be text or data.
* @param[out] cipherMessage points to the buffer to store the encrypted message which must be routed to all recipients(if one is produced, depends on encryption policy)
* @param[in] callback Performing encryption may involve the X3DH server and is thus asynchronous, when the operation is completed,
......@@ -295,7 +302,7 @@ namespace lime {
lime::PeerDeviceStatus get_peerDeviceStatus(const std::string &peerDeviceId);
/**
* @delete a peerDevice from local storage
* @brief delete a peerDevice from local storage
*
* @param[in] peerDeviceId The device Id to be removed from local storage, shall be its GRUU
*
......
......@@ -164,19 +164,24 @@ namespace lime {
std::vector<std::string> missingPeers; /* vector of deviceId(GRUU) which are requested to perform X3DH before the encryption can occurs */
/* Create the appropriate recipient infos and fill it with sessions found in cache */
// internal_recipients is a vector duplicating the recipients one in the same order(to allow fast copying of relevant information back to recipients when encryption is completed)
// internal_recipients is a vector duplicating the recipients one in the same order (ignoring the one with peerStatus set to fail)
// This allows fast copying of relevant information back to recipients when encryption is completed
std::vector<RecipientInfos<Curve>> internal_recipients{};
for (const auto &recipient : *recipients) {
auto sessionElem = m_DR_sessions_cache.find(recipient.deviceId);
if (sessionElem != m_DR_sessions_cache.end()) { // session is in cache
if (sessionElem->second->isActive()) { // the session in cache is active
internal_recipients.emplace_back(recipient.deviceId, sessionElem->second);
} else { // session in cache is not active(may append if last encryption reach sending chain symmetric ratchet usage)
// if the input recipient peerStatus is fail we must ignore it
// most likely: we're in a call after a key bundle fetch and this peer device does not have keys on the X3DH server
if (recipient.peerStatus != lime::PeerDeviceStatus::fail) {
auto sessionElem = m_DR_sessions_cache.find(recipient.deviceId);
if (sessionElem != m_DR_sessions_cache.end()) { // session is in cache
if (sessionElem->second->isActive()) { // the session in cache is active
internal_recipients.emplace_back(recipient.deviceId, sessionElem->second);
} else { // session in cache is not active(may append if last encryption reach sending chain symmetric ratchet usage)
internal_recipients.emplace_back(recipient.deviceId);
m_DR_sessions_cache.erase(recipient.deviceId); // remove unactive session from cache
}
} else { // session is not in cache, just create it and the session ptr will be a nullptr
internal_recipients.emplace_back(recipient.deviceId);
m_DR_sessions_cache.erase(recipient.deviceId); // remove unactive session from cache
}
} else { // session is not in cache, just create it and the session ptr will be a nullptr
internal_recipients.emplace_back(recipient.deviceId);
}
}
......@@ -200,10 +205,15 @@ namespace lime {
postToX3DHServer(userData, X3DHmessage);
} else { // got everyone, encrypt
encryptMessage(internal_recipients, *plainMessage, *recipientUserId, m_selfDeviceId, *cipherMessage, encryptionPolicy);
// move DR messages to the input/output structure
for (size_t i=0; i<recipients->size(); i++) {
(*recipients)[i].DRmessage = std::move(internal_recipients[i].DRmessage);
(*recipients)[i].peerStatus = internal_recipients[i].peerStatus;
// move DR messages to the input/output structure, ignoring again the input with peerStatus set to fail
// so the index on the internal_recipients still matches the way we created it from recipients
size_t i=0;
for (auto &recipient : *recipients) {
if (recipient.peerStatus != lime::PeerDeviceStatus::fail) {
recipient.DRmessage = std::move(internal_recipients[i].DRmessage);
recipient.peerStatus = internal_recipients[i].peerStatus;
i++;
}
}
if (callback) callback(lime::CallbackReturn::success, "");
// is there no one in an asynchronous encryption process and do we have something in encryption queue to process
......
......@@ -34,6 +34,7 @@ namespace lime {
// Encrypt/Decrypt
/**
* @brief Encrypt a buffer(text or file) for a given list of recipient devices
* if specified localDeviceId is not found in local Storage, throw an exception
*
* Clarification on recipients:
*
......@@ -50,10 +51,16 @@ namespace lime {
*
* In all cases, the identified source of the message will be the localDeviceId
*
* Note: all parameters are shared pointers as the process being asynchronous, the ownership will be taken internally exempting caller to manage the buffers.
* If the X3DH server can't provide keys for a peer device, its status is set to fail and its DRmessage is empty. Other devices get their encrypted message
* If no peer device could get encrypted for all of them are missing keys on the X3DH server, the callback will still be called with success exit status
*
* @note all parameters are shared pointers as the process being asynchronous, the ownership will be taken internally exempting caller to manage the buffers.
*
* @param[in] recipientUserId the Id of intended recipient, shall be a sip:uri of user or conference, is used as associated data to ensure no-one can mess with intended recipient
* @param[in,out] recipients a list of RecipientData holding: the recipient device Id(GRUU) and an empty buffer to store the DRmessage which must then be routed to that recipient
* @param[in,out] recipients a list of RecipientData holding:
* - the recipient device Id(GRUU)
* - an empty buffer to store the DRmessage which must then be routed to that recipient
* - the peer Status. If peerStatus is set to fail, this entry is ignored otherwise the peerStatus is set by the encrypt, see PeerDeviceStatus definition for details
* @param[in] plainMessage a buffer holding the message to encrypt, can be text or data.
* @param[in] encryptionPolicy select how to manage the encryption: direct use of Double Ratchet message or encrypt in the cipher message and use the DR message to share the cipher message key
* @param[out] cipherMessage points to the buffer to store the encrypted message which must be routed to all recipients(if one is produced, depends on encryption policy)
......
......@@ -35,6 +35,10 @@ namespace lime {
template <typename Curve>
void Lime<Curve>::X3DH_init_sender_session(const std::vector<X3DH_peerBundle<Curve>> &peersBundle) {
for (const auto &peerBundle : peersBundle) {
// do we have a key bundle to build this message from ?
if (peerBundle.bundleFlag == lime::X3DHKeyBundleFlag::noBundle) {
continue;
}
// Verifify SPk_signature, throw an exception if it fails
auto SPkVerify = make_Signature<Curve>();
SPkVerify->set_public(peerBundle.Ik);
......@@ -81,7 +85,7 @@ namespace lime {
HKDF_input_index += 2*DH_out.size();
// Compute DH4 = DH(Ek, peer OPk) (if any OPk in bundle)
if (peerBundle.haveOPk) {
if (peerBundle.bundleFlag == lime::X3DHKeyBundleFlag::OPk) {
DH->set_peerPublic(peerBundle.OPk);
DH->computeSharedSecret();
DH_out = DH->get_sharedSecret();
......@@ -97,7 +101,7 @@ namespace lime {
// Generate X3DH init message: as in X3DH spec section 3.3:
std::vector<uint8_t> X3DH_initMessage{};
double_ratchet_protocol::buildMessage_X3DHinit(X3DH_initMessage, m_Ik.publicKey(), DH->get_selfPublic(), peerBundle.SPk_id, peerBundle.haveOPk?peerBundle.OPk_id:0, peerBundle.haveOPk);
double_ratchet_protocol::buildMessage_X3DHinit(X3DH_initMessage, m_Ik.publicKey(), DH->get_selfPublic(), peerBundle.SPk_id, peerBundle.OPk_id, (peerBundle.bundleFlag == lime::X3DHKeyBundleFlag::OPk));
DH = nullptr; // be sure to destroy and clean the keyExchange object as soon as we do not need it anymore
......@@ -114,7 +118,7 @@ namespace lime {
// in that case just keep on building our new session so the peer device knows it must get rid of the OPk, sessions will eventually converge into only one when messages
// stop crossing themselves on the network.
// If the fetch bundle doesn't hold OPk, just ignore our newly built session, and use existing one
if (peerBundle.haveOPk) {
if (peerBundle.bundleFlag == lime::X3DHKeyBundleFlag::OPk) {
m_DR_sessions_cache.erase(peerBundle.deviceId); // will just do nothing if this peerDeviceId is not in cache
}
......
......@@ -468,7 +468,7 @@ namespace lime {
*
* peerBundle : bundle Count < 2 bytes unsigned Big Endian> ||
* ( deviceId Size < 2 bytes unsigned Big Endian > || deviceId
* Flag<1 byte: 0 if no OPK in bundle, 1 if present> ||
* Flag<1 byte: 0 if no OPK in bundle, 1 if present, 2 no key bundle found on server> ||
* Ik <EDDSA Public Key Length> ||
* SPk <ECDH Public Key Length> || SPK id <4 bytes>
* SPk_sig <Signature Length> ||
......@@ -505,6 +505,7 @@ namespace lime {
for (auto i=0; i<peersBundleCount; i++) {
if (body.size() < index + 2) { // check we have at least a device size to read
peersBundle.clear();
LIME_LOGE<<"Invalid message: size is not what expected, discard without parsing";
return false;
}
......@@ -512,18 +513,45 @@ namespace lime {
uint16_t deviceIdSize = (static_cast<uint16_t>(body[index]))<<8|body[index+1];
index += 2;
if (body.size() < index + deviceIdSize + 1) { // check we have at enough data to read: device size and the following OPk flag
if (body.size() < index + deviceIdSize + 1) { // check we have at enough data to read: device size and the following flag
peersBundle.clear();
LIME_LOGE<<"Invalid message: size is not what expected, discard without parsing";
return false;
}
std::string deviceId{body.cbegin()+index, body.cbegin()+index+deviceIdSize};
index += deviceIdSize;
// check if we have an OPk
bool haveOPk = (body[index]==0)?false:true;
// check if we have a key bundle and an OPk. Possible flag values: 0 no OPk, 1 OPk, 2 no key bundle at all
// parse the key bundle flag
lime::X3DHKeyBundleFlag keyBundleFlag = lime::X3DHKeyBundleFlag::noBundle;
switch (body[index]) {
case static_cast<uint8_t>(lime::X3DHKeyBundleFlag::noBundle):
keyBundleFlag = lime::X3DHKeyBundleFlag::noBundle;
break;
case static_cast<uint8_t>(lime::X3DHKeyBundleFlag::noOPk):
keyBundleFlag = lime::X3DHKeyBundleFlag::noOPk;
break;
case static_cast<uint8_t>(lime::X3DHKeyBundleFlag::OPk):
keyBundleFlag = lime::X3DHKeyBundleFlag::OPk;
break;
default:
LIME_LOGE<<"Invalid X3DH message: unexpected flag value "<<body[index]<<" in "<<deviceId<<" key bundle";
peersBundle.clear();
return false;
}
// if there is no bundle, just skip to the next one
if (keyBundleFlag == lime::X3DHKeyBundleFlag::noBundle) {
// add device Id (and its size) to the trace
message_trace << endl << dec << " Device Id ("<<static_cast<unsigned int>(deviceIdSize)<<" bytes): "<<deviceId<<" has no key bundle"<<endl;
peersBundle.emplace_back(std::move(deviceId));
continue; // skip to next one
}
bool haveOPk = (keyBundleFlag == lime::X3DHKeyBundleFlag::OPk);
index += 1;
// add device Id (and its size) and OPk flag to the trace
// add device Id (and its size) and flag to the trace
message_trace << endl << dec << " Device Id ("<<static_cast<unsigned int>(deviceIdSize)<<" bytes): "<<deviceId<<(haveOPk?" has ":" does not have ")<<"OPk"<<endl<<" Ik: ";
if (body.size() < index + DSA<Curve, lime::DSAtype::publicKey>::ssize() + X<Curve, lime::Xtype::publicKey>::ssize() + DSA<Curve, lime::DSAtype::signature>::ssize() + 4 + (haveOPk?(X<Curve, lime::Xtype::publicKey>::ssize()+4):0) ) {
......@@ -768,6 +796,19 @@ namespace lime {
return;
}
// tweak the userData->recipients to set to fail those wo didn't get a key bundle
for (const auto &peerBundle:peersBundle) {
// get all the bundless peer Devices
if (peerBundle.bundleFlag == lime::X3DHKeyBundleFlag::noBundle) {
for (auto &recipient:*(userData->recipients)) {
// and set their recipient status to fail so the encrypt function would ignore them
if (recipient.deviceId == peerBundle.deviceId) {
recipient.peerStatus = lime::PeerDeviceStatus::fail;
}
}
}
}
// call the encrypt function again, it will call the callback when done, encryption queue won't be processed as still locked by the m_ongoing_encryption member
encrypt(userData->recipientUserId, userData->recipients, userData->plainMessage, userData->encryptionPolicy, userData->cipherMessage, callback);
......
......@@ -24,19 +24,28 @@
namespace lime {
/** Set possible values for a flag in the keyBundle X3DH packet
* so do not modify values or we'll loose sync with existing X3DH server
*/
enum class X3DHKeyBundleFlag : uint8_t {
noOPk=0, /**< This bundle does not contain an OPk */
OPk=1, /**< This bundle contains an OPk */
noBundle=2}; /**< This bundle is empty(just a deviceId) as this user was not found on X3DH server */
/**
* Holds everything found in a key bundle received from X3DH server
* @note Data members are set once by constructor and then the object is used to pass this data around, so they all are const
*/
template <typename Curve>
struct X3DH_peerBundle {
std::string deviceId; /**< peer device Id */
DSA<Curve, lime::DSAtype::publicKey> Ik; /**< peer device public identity key */
X<Curve, lime::Xtype::publicKey> SPk; /**< peer device current public pre-signed key */
uint32_t SPk_id; /**< id of the peer device current public pre-signed key */
DSA<Curve, lime::DSAtype::signature> SPk_sig; /**< signature of the peer device current public pre-signed key */
bool haveOPk; /**< flag: is this bundle hold a One Time preKey */
X<Curve, lime::Xtype::publicKey> OPk; /**< peer device One Time preKey */
uint32_t OPk_id; /**< id of the peer device current public pre-signed key */
const std::string deviceId; /**< peer device Id */
const DSA<Curve, lime::DSAtype::publicKey> Ik; /**< peer device public identity key */
const X<Curve, lime::Xtype::publicKey> SPk; /**< peer device current public pre-signed key */
const uint32_t SPk_id; /**< id of the peer device current public pre-signed key */
const DSA<Curve, lime::DSAtype::signature> SPk_sig; /**< signature of the peer device current public pre-signed key */
const lime::X3DHKeyBundleFlag bundleFlag; /**< Flag this bundle as empty and if not if it holds an OPk, possible values */
const X<Curve, lime::Xtype::publicKey> OPk; /**< peer device One Time preKey */
const uint32_t OPk_id; /**< id of the peer device current public pre-signed key */
/**
* Constructor gets vector<uint8_t> iterators to all needed data and copy them into correct data types
......@@ -50,14 +59,19 @@ namespace lime {
* @param[in] OPk_id id of the One-time Pre-key - this parameter is optionnal
*/
X3DH_peerBundle(std::string &&deviceId, std::vector<uint8_t>::const_iterator Ik, std::vector<uint8_t>::const_iterator SPk, uint32_t SPk_id, std::vector<uint8_t>::const_iterator SPk_sig, std::vector<uint8_t>::const_iterator OPk, uint32_t OPk_id) :
deviceId{deviceId}, Ik{Ik}, SPk{SPk}, SPk_id{SPk_id}, SPk_sig{SPk_sig}, haveOPk{true}, OPk{OPk}, OPk_id{OPk_id} {};
deviceId{deviceId}, Ik{Ik}, SPk{SPk}, SPk_id{SPk_id}, SPk_sig{SPk_sig}, bundleFlag{lime::X3DHKeyBundleFlag::OPk}, OPk{OPk}, OPk_id{OPk_id} {};
/**
* @overload
* construct without OPk when not present in the parsed bundle
*/
X3DH_peerBundle(std::string &&deviceId, std::vector<uint8_t>::const_iterator Ik, std::vector<uint8_t>::const_iterator SPk, uint32_t SPk_id, std::vector<uint8_t>::const_iterator SPk_sig) :
deviceId{deviceId}, Ik{Ik}, SPk{SPk}, SPk_id{SPk_id}, SPk_sig{SPk_sig}, haveOPk{false}, OPk{}, OPk_id{0} {};
deviceId{deviceId}, Ik{Ik}, SPk{SPk}, SPk_id{SPk_id}, SPk_sig{SPk_sig}, bundleFlag{lime::X3DHKeyBundleFlag::noOPk}, OPk{}, OPk_id{0} {};
/**
* @overload
* construct without bundle when not present in the parsed server response
*/
X3DH_peerBundle(std::string &&deviceId) :
deviceId{deviceId}, Ik{}, SPk{}, SPk_id{0}, SPk_sig{}, bundleFlag{lime::X3DHKeyBundleFlag::noBundle}, OPk{}, OPk_id{0} {};
};
namespace x3dh_protocol {
......
This diff is collapsed.
......@@ -122,6 +122,12 @@ const enum_errorCodes = {
bad_request : 0x08
};
const enum_keyBundleFlag = {
noOPk : 0x00,
OPk : 0x01,
noBundle : 0x02
};
const keySizes = { // 1 is for enum_curveId.CURVE25519, 2 is for enum_curveId.CURVE448
1 : {X_pub : 32, X_priv : 32, ED_pub : 32, ED_priv : 32, Sig : 64},
2 : {X_pub : 56, X_priv : 56, ED_pub : 57, ED_priv : 57, Sig : 114}
......@@ -421,7 +427,7 @@ https.createServer(options, (req, res) => {
/* peerBundle : bundle Count < 2 bytes unsigned Big Endian> |
* ( deviceId Size < 2 bytes unsigned Big Endian > | deviceId
* Flag<1 byte: 0 if no OPK in bundle, 1 if present> |
* Flag<1 byte: 0 if no OPK in bundle, 1 if present | 2 no key bundle is present> |
* Ik <EDDSA Public Key Length> |
* SPk <ECDH Public Key Length> | SPK id <4 bytes>
* SPk_sig <Signature Length> |
......@@ -440,16 +446,26 @@ https.createServer(options, (req, res) => {
let userId = peersBundle[i][0];
let haveOPk = (peersBundle[i].length>5)?1:0; // elements in peersBundle[i] are : deviceId, Ik, SPk, SPk_id, SPk_sig, [OPk, OPk_id] last 2 are optionnal
let peerBundle = Buffer.allocUnsafe(2);
peerBundle.writeUInt16BE(userId.length, 0); // peers bundle count on 2 bytes in Big Endian
peerBundle.writeUInt16BE(userId.length, 0); // device Id size on 2 bytes in Big Endian
let flagBuffer = Buffer.allocUnsafe(1);
flagBuffer.writeUInt8(haveOPk,0);
let SPk_idBuffer = Buffer.allocUnsafe(4);
SPk_idBuffer.writeUInt32BE(peersBundle[i][3], 0); // SPk id on 4 bytes in Big Endian
peerBundle = Buffer.concat([peerBundle, Buffer.from(userId), flagBuffer, peersBundle[i][1], peersBundle[i][2], SPk_idBuffer, peersBundle[i][4]]);
if (haveOPk) {
let OPk_idBuffer = Buffer.allocUnsafe(4);
OPk_idBuffer.writeUInt32BE(peersBundle[i][6], 0); // OPk id on 4 bytes in Big Endian
peerBundle = Buffer.concat([peerBundle, peersBundle[i][5], OPk_idBuffer]);
if (peersBundle[i].length==1) { // We do not have any key bundle, user was not found
flagBuffer.writeUInt8(enum_keyBundleFlag.noBundle,0);
peerBundle = Buffer.concat([peerBundle, Buffer.from(userId), flagBuffer]); // bundle is just the id and the flag set to 2
} else { /* we do have a peer bundle, insert it*/
/* set the flag */
if (haveOPk) {
flagBuffer.writeUInt8(enum_keyBundleFlag.OPk,0);
} else {
flagBuffer.writeUInt8(enum_keyBundleFlag.noOPk,0);
}
let SPk_idBuffer = Buffer.allocUnsafe(4);
SPk_idBuffer.writeUInt32BE(peersBundle[i][3], 0); // SPk id on 4 bytes in Big Endian
peerBundle = Buffer.concat([peerBundle, Buffer.from(userId), flagBuffer, peersBundle[i][1], peersBundle[i][2], SPk_idBuffer, peersBundle[i][4]]);
if (haveOPk) {
let OPk_idBuffer = Buffer.allocUnsafe(4);
OPk_idBuffer.writeUInt32BE(peersBundle[i][6], 0); // OPk id on 4 bytes in Big Endian
peerBundle = Buffer.concat([peerBundle, peersBundle[i][5], OPk_idBuffer]);
}
}
peersBundleBuffer = Buffer.concat([peersBundleBuffer, peerBundle]);
}
......@@ -496,7 +512,8 @@ https.createServer(options, (req, res) => {
return;
}
if (row == undefined) { // user not found in DB
queries_err_message +=' ## bundle not found for user '+peersBundle[i];
console.log("user "+peersBundle[i][0]+" not found");
queries_success++;
} else {
console.log("Ok push on peersBundle array "+i);
console.dir(peersBundle[i]);
......
......@@ -105,6 +105,14 @@ abstract class ErrorCodes {
const server_failure = 0x09;
};
// emulate simple enumeration with abstract class
abstract class KeyBundleFlag {
const noOPk = 0x00;
const OPk = 0x01;
const noBundle = 0x02;
}
// define keys and signature size in bytes based on the curve used
const keySizes = array(
......@@ -390,7 +398,7 @@ function x3dh_process_request($userId) {
/* peerBundle : bundle Count < 2 bytes unsigned Big Endian> |
* ( deviceId Size < 2 bytes unsigned Big Endian > | deviceId
* Flag<1 byte: 0 if no OPK in bundle, 1 if present> |
* Flag<1 byte: 0 if no OPK in bundle, 1 if OPk is present, 2 when no bundle was found for this device> |
* Ik <EDDSA Public Key Length> |
* SPk <ECDH Public Key Length> | SPK id <4 bytes>
* SPk_sig <Signature Length> |
......@@ -451,7 +459,7 @@ function x3dh_process_request($userId) {
$stmt->execute();
$row = $stmt->get_result()->fetch_assoc();
if (!$row) {
$keyBundleErrors .=' ## bundle not found for user '.$peersBundle[$i][0];
x3dh_log(LogLevel::DEBUG, "User ".$peersBundle[$i][0]." not found");
} else {
array_push($peersBundle[$i], $row['Ik'], $row['SPk'], $row['SPk_id'], $row['SPk_sig']);
if ($row['OPk']) { // there is an OPk
......@@ -477,17 +485,22 @@ function x3dh_process_request($userId) {
$db->query("UNLOCK TABLES");
$peersBundleMessage = pack("C3n",X3DH_protocolVersion, MessageTypes::peerBundle, curveId, $peersCount); // build the X3DH response header
foreach ($peersBundle as $bundle) {
$flagOPk = (count($bundle)>5)?1:0; // elements in bundle array are : deviceId, Ik, SPk, SPk_id, SPk_sig, [OPk, OPk_id] last 2 are optionnal
// elements in bundle array are : deviceId, [Ik, SPk, SPk_id, SPk_sig, [OPk, OPk_id]]
$peersBundleMessage .= pack("n", strlen($bundle[0])); // size of peer Device Id, 2 bytes in big endian
$peersBundleMessage .= $bundle[0]; // peer Device Id
$peersBundleMessage .= pack("C", $flagOPk); // 1 byte : the flag for OPk presence
$peersBundleMessage .= $bundle[1]; // Ik
$peersBundleMessage .= $bundle[2]; // SPk
$peersBundleMessage .= pack("N", $bundle[3]); // SPk Id : 4 bytes in big endian
$peersBundleMessage .= $bundle[4]; // SPk Signature
if ($flagOPk == 1) {
$peersBundleMessage .= $bundle[5]; // OPk
$peersBundleMessage .= pack("N", $bundle[6]); // OPk Id : 4 bytes in big endian
if (count($bundle)==1) { // we have no keys for this device
$peersBundleMessage .= pack("C", KeyBundleFlag::noBundle); // 1 byte : the key bundle flag
} else {
$flagOPk = (count($bundle)>5)?(KeyBundleFlag::OPk):(KeyBundleFlag::noOPk); // does the bundle hold an OPk?
$peersBundleMessage .= pack("C", $flagOPk); // 1 byte : the key bundle flag
$peersBundleMessage .= $bundle[1]; // Ik
$peersBundleMessage .= $bundle[2]; // SPk
$peersBundleMessage .= pack("N", $bundle[3]); // SPk Id : 4 bytes in big endian
$peersBundleMessage .= $bundle[4]; // SPk Signature
if ($flagOPk == KeyBundleFlag::OPk) {
$peersBundleMessage .= $bundle[5]; // OPk
$peersBundleMessage .= pack("N", $bundle[6]); // OPk Id : 4 bytes in big endian
}
}
}
returnOk($peersBundleMessage);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment