Skip to content
Snippets Groups Projects
Commit e2e9e39c authored by Cristian Tuns's avatar Cristian Tuns
Browse files

Backed out changeset 30517fbc6b05 (bug 1810790) for causing multiple failures CLOSED TREE

parent 5ee3b0e7
No related branches found
No related tags found
No related merge requests found
Showing
with 1666 additions and 0 deletions
......@@ -22,6 +22,7 @@ if not CONFIG["MOZ_SYSTEM_JPEG"]:
external_dirs += ["media/libjpeg"]
DIRS += [
"/third_party/prio",
"/third_party/msgpack",
"/third_party/sipcc",
]
......
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
[ChromeOnly, Exposed=Window]
namespace PrioEncoder {
[Throws, NewObject]
PrioEncodedData encode(ByteString batchID, PrioParams params);
};
dictionary PrioParams {
required sequence<boolean> booleans;
};
dictionary PrioEncodedData {
Uint8Array a;
Uint8Array b;
};
......@@ -106,3 +106,7 @@ WEBIDL_FILES += [
"Glean.webidl",
"GleanPings.webidl",
]
WEBIDL_FILES += [
"PrioEncoder.webidl",
]
......@@ -1443,6 +1443,7 @@ void WindowGlobalParent::ActorDestroy(ActorDestroyReason aWhy) {
if (mDocumentURI && (net::SchemeIsHTTP(mDocumentURI) ||
net::SchemeIsHTTPS(mDocumentURI))) {
GetContentBlockingLog()->ReportOrigins();
GetContentBlockingLog()->ReportEmailTrackingLog(DocumentPrincipal());
}
}
......
......@@ -106,6 +106,7 @@ DIRS += [
"simpledb",
"reporting",
"localstorage",
"prio",
"l10n",
"origin-trials",
"webscheduling",
......
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/TextUtils.h"
#include "mozilla/dom/ToJSValue.h"
#include "mprio.h"
#include "PrioEncoder.h"
namespace mozilla::dom {
/* static */
StaticRefPtr<PrioEncoder> PrioEncoder::sSingleton;
/* static */
PublicKey PrioEncoder::sPublicKeyA = nullptr;
/* static */
PublicKey PrioEncoder::sPublicKeyB = nullptr;
// Production keys from bug 1552315 comment#3
/* static */
const char* kDefaultKeyA =
"E780C1A9C50E3FC5A9B39469FCC92D62D2527BAE6AF76BBDEF128883FA400846";
/* static */
const char* kDefaultKeyB =
"F992B575840AEC202289FBF99D6C04FB2A37B1DA1CDEB1DF8036E1340D46C561";
PrioEncoder::PrioEncoder() = default;
PrioEncoder::~PrioEncoder() {
if (sPublicKeyA) {
PublicKey_clear(sPublicKeyA);
sPublicKeyA = nullptr;
}
if (sPublicKeyB) {
PublicKey_clear(sPublicKeyB);
sPublicKeyB = nullptr;
}
Prio_clear();
}
/* static */
void PrioEncoder::Encode(GlobalObject& aGlobal, const nsCString& aBatchID,
const PrioParams& aPrioParams,
RootedDictionary<PrioEncodedData>& aData,
ErrorResult& aRv) {
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
if (!global) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return;
}
nsCString aResult;
nsCString bResult;
nsresult rv = PrioEncoder::EncodeNative(aBatchID, aPrioParams.mBooleans,
aResult, bResult);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return;
}
nsTArray<uint8_t> arrayForServerA;
nsTArray<uint8_t> arrayForServerB;
if (!arrayForServerA.AppendElements(
reinterpret_cast<const uint8_t*>(aResult.BeginReading()),
aResult.Length(), fallible)) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
if (!arrayForServerB.AppendElements(
reinterpret_cast<const uint8_t*>(bResult.BeginReading()),
bResult.Length(), fallible)) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
JS::Rooted<JS::Value> valueA(aGlobal.Context());
if (!ToJSValue(aGlobal.Context(),
TypedArrayCreator<Uint8Array>(arrayForServerA), &valueA)) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
aData.mA.Construct().Init(&valueA.toObject());
JS::Rooted<JS::Value> valueB(aGlobal.Context());
if (!ToJSValue(aGlobal.Context(),
TypedArrayCreator<Uint8Array>(arrayForServerB), &valueB)) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
aData.mB.Construct().Init(&valueB.toObject());
}
/* static */
nsresult PrioEncoder::EncodeNative(const nsCString& aBatchID,
const nsTArray<bool>& aData,
nsCString& aResult, nsCString& bResult) {
SECStatus prio_rv = SECSuccess;
nsresult rv = PrioEncoder::LazyInitSingleton();
if (NS_FAILED(rv)) {
return rv;
}
if (aData.Length() > gNumBooleans) {
return NS_ERROR_INVALID_ARG;
}
PrioConfig prioConfig = PrioConfig_new(
aData.Length(), sPublicKeyA, sPublicKeyB,
reinterpret_cast<const unsigned char*>(aBatchID.BeginReading()),
aBatchID.Length());
if (!prioConfig) {
return NS_ERROR_FAILURE;
}
auto configGuard = MakeScopeExit([&] { PrioConfig_clear(prioConfig); });
unsigned char* forServerA = nullptr;
unsigned int lenA = 0;
unsigned char* forServerB = nullptr;
unsigned int lenB = 0;
prio_rv = PrioClient_encode(prioConfig, aData.Elements(), &forServerA, &lenA,
&forServerB, &lenB);
aResult.Adopt(reinterpret_cast<char*>(forServerA), lenA);
bResult.Adopt(reinterpret_cast<char*>(forServerB), lenB);
if (prio_rv != SECSuccess) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
bool PrioEncoder::IsValidHexPublicKey(mozilla::Span<const char> aStr) {
if (aStr.Length() != CURVE25519_KEY_LEN_HEX) {
return false;
}
for (auto c : aStr) {
if (!IsAsciiHexDigit(c)) {
return false;
}
}
return true;
}
/* static */
nsresult PrioEncoder::SetKeys(const char* aKeyA, const char* aKeyB) {
nsAutoCStringN<CURVE25519_KEY_LEN_HEX + 1> prioKeyA;
if (aKeyA == nullptr) {
prioKeyA = kDefaultKeyA;
} else {
prioKeyA = aKeyA;
}
nsAutoCStringN<CURVE25519_KEY_LEN_HEX + 1> prioKeyB;
if (aKeyB == nullptr) {
prioKeyB = kDefaultKeyB;
} else {
prioKeyB = aKeyB;
}
// Check that both public keys are of the right length
// and contain only hex digits 0-9a-fA-f
if (!PrioEncoder::IsValidHexPublicKey(prioKeyA) ||
!PrioEncoder::IsValidHexPublicKey(prioKeyB)) {
return NS_ERROR_UNEXPECTED;
}
SECStatus prio_rv = SECSuccess;
prio_rv = Prio_init();
if (prio_rv != SECSuccess) {
return NS_ERROR_UNEXPECTED;
}
prio_rv = PublicKey_import_hex(
&sPublicKeyA,
reinterpret_cast<const unsigned char*>(prioKeyA.BeginReading()),
CURVE25519_KEY_LEN_HEX);
if (prio_rv != SECSuccess) {
return NS_ERROR_UNEXPECTED;
}
prio_rv = PublicKey_import_hex(
&sPublicKeyB,
reinterpret_cast<const unsigned char*>(prioKeyB.BeginReading()),
CURVE25519_KEY_LEN_HEX);
if (prio_rv != SECSuccess) {
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
/* static */
nsresult PrioEncoder::LazyInitSingleton() {
if (!sSingleton) {
// Init to the default keys.
nsresult rv = PrioEncoder::SetKeys();
if (!NS_SUCCEEDED(rv)) {
return rv;
}
sSingleton = new PrioEncoder();
ClearOnShutdown(&sSingleton);
}
return NS_OK;
}
} // namespace mozilla::dom
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_PrioEncoder_h
#define mozilla_dom_PrioEncoder_h
#include "mozilla/dom/PrioEncoderBinding.h"
#include "mozilla/dom/RootedDictionary.h"
class nsIGlobalObject;
using SECKEYPublicKey = struct SECKEYPublicKeyStr;
using PublicKey = SECKEYPublicKey*;
namespace mozilla::dom {
class PrioEncoder {
public:
NS_INLINE_DECL_REFCOUNTING(PrioEncoder)
// C++ API
static nsresult EncodeNative(const nsCString& aBatchID,
const nsTArray<bool>& aData, nsCString& aResult,
nsCString& bResult);
// DOM API
static void Encode(GlobalObject& aGlobal, const nsCString& aBatchID,
const PrioParams& aPrioParams,
RootedDictionary<PrioEncodedData>& aData,
ErrorResult& aRv);
// maximum number of booleans that can be prio-encoded in a single batch.
const static uint32_t gNumBooleans = 2046;
// Set (or, by passing nullptrs, reset) the keys used to encode the payloads.
static nsresult SetKeys(const char* aKeyA = nullptr,
const char* aKeyB = nullptr);
private:
PrioEncoder();
~PrioEncoder();
static PublicKey sPublicKeyA;
static PublicKey sPublicKeyB;
static StaticRefPtr<PrioEncoder> sSingleton;
static bool IsValidHexPublicKey(mozilla::Span<const char>);
static nsresult LazyInitSingleton();
};
} // namespace mozilla::dom
#endif // mozilla_dom_PrioEncoder_h
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
with Files("**"):
BUG_COMPONENT = ("Core", "DOM: Core & HTML")
LOCAL_INCLUDES += ["/third_party/msgpack/include"]
EXPORTS.mozilla.dom += [
"PrioEncoder.h",
]
UNIFIED_SOURCES += [
"PrioEncoder.cpp",
]
TEST_DIRS += ["test/gtest"]
FINAL_LIBRARY = "xul"
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gtest/gtest.h"
#include "jsapi.h"
#include "PrioEncoder.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/ErrorResult.h"
#include "mprio.h"
TEST(PrioEncoder, BadPublicKeys)
{
mozilla::ErrorResult rv;
rv = mozilla::dom::PrioEncoder::SetKeys("badA", "badB");
ASSERT_TRUE(rv.Failed());
rv = mozilla::ErrorResult();
}
TEST(PrioEncoder, BooleanLimitExceeded)
{
mozilla::dom::AutoJSAPI jsAPI;
ASSERT_TRUE(jsAPI.Init(xpc::PrivilegedJunkScope()));
JSContext* cx = jsAPI.cx();
mozilla::dom::GlobalObject global(cx, xpc::PrivilegedJunkScope());
nsCString batchID = "abc123"_ns;
mozilla::dom::PrioParams prioParams;
FallibleTArray<bool> sequence;
const int ndata = mozilla::dom::PrioEncoder::gNumBooleans + 1;
const int seed = time(nullptr);
srand(seed);
for (int i = 0; i < ndata; i++) {
// Arbitrary data)
*(sequence.AppendElement(mozilla::fallible)) = rand() % 2;
}
ASSERT_TRUE(prioParams.mBooleans.Assign(sequence));
mozilla::dom::RootedDictionary<mozilla::dom::PrioEncodedData> prioEncodedData(
cx);
mozilla::ErrorResult rv;
mozilla::dom::PrioEncoder::Encode(global, batchID, prioParams,
prioEncodedData, rv);
ASSERT_TRUE(rv.Failed());
// Reset error result so test runner does not fail.
rv = mozilla::ErrorResult();
}
TEST(PrioEncoder, VerifyFull)
{
SECStatus prioRv = SECSuccess;
PublicKey pkA = nullptr;
PublicKey pkB = nullptr;
PrivateKey skA = nullptr;
PrivateKey skB = nullptr;
PrioConfig cfg = nullptr;
PrioServer sA = nullptr;
PrioServer sB = nullptr;
PrioVerifier vA = nullptr;
PrioVerifier vB = nullptr;
PrioPacketVerify1 p1A = nullptr;
PrioPacketVerify1 p1B = nullptr;
PrioPacketVerify2 p2A = nullptr;
PrioPacketVerify2 p2B = nullptr;
PrioTotalShare tA = nullptr;
PrioTotalShare tB = nullptr;
unsigned char* forServerA = nullptr;
unsigned char* forServerB = nullptr;
const int seed = time(nullptr);
srand(seed);
// Number of different boolean data fields we collect.
const int ndata = 3;
unsigned char batchIDStr[32];
memset(batchIDStr, 0, sizeof batchIDStr);
snprintf((char*)batchIDStr, sizeof batchIDStr, "%d", rand());
bool dataItems[ndata];
unsigned long long output[ndata];
// The client's data submission is an arbitrary boolean vector.
for (int i = 0; i < ndata; i++) {
// Arbitrary data
dataItems[i] = rand() % 2;
}
// Initialize NSS random number generator.
prioRv = Prio_init();
ASSERT_TRUE(prioRv == SECSuccess);
// Generate keypairs for servers
prioRv = Keypair_new(&skA, &pkA);
ASSERT_TRUE(prioRv == SECSuccess);
prioRv = Keypair_new(&skB, &pkB);
ASSERT_TRUE(prioRv == SECSuccess);
// Export public keys to hex and print to stdout
const int keyLength = CURVE25519_KEY_LEN_HEX + 1;
unsigned char pkHexA[keyLength];
unsigned char pkHexB[keyLength];
prioRv = PublicKey_export_hex(pkA, pkHexA, keyLength);
ASSERT_TRUE(prioRv == SECSuccess);
prioRv = PublicKey_export_hex(pkB, pkHexB, keyLength);
ASSERT_TRUE(prioRv == SECSuccess);
// Use the default configuration parameters.
cfg = PrioConfig_new(ndata, pkA, pkB, batchIDStr, strlen((char*)batchIDStr));
ASSERT_TRUE(cfg != nullptr);
PrioPRGSeed serverSecret;
prioRv = PrioPRGSeed_randomize(&serverSecret);
ASSERT_TRUE(prioRv == SECSuccess);
// Initialize two server objects. The role of the servers need not
// be symmetric. In a deployment, we envision that:
// * Server A is the main telemetry server that is always online.
// Clients send their encrypted data packets to Server A and
// Server A stores them.
// * Server B only comes online when the two servers want to compute
// the final aggregate statistics.
sA = PrioServer_new(cfg, PRIO_SERVER_A, skA, serverSecret);
ASSERT_TRUE(sA != nullptr);
sB = PrioServer_new(cfg, PRIO_SERVER_B, skB, serverSecret);
ASSERT_TRUE(sB != nullptr);
// Initialize empty verifier objects
vA = PrioVerifier_new(sA);
ASSERT_TRUE(vA != nullptr);
vB = PrioVerifier_new(sB);
ASSERT_TRUE(vB != nullptr);
// Initialize shares of final aggregate statistics
tA = PrioTotalShare_new();
ASSERT_TRUE(tA != nullptr);
tB = PrioTotalShare_new();
ASSERT_TRUE(tB != nullptr);
// Initialize shares of verification packets
p1A = PrioPacketVerify1_new();
ASSERT_TRUE(p1A != nullptr);
p1B = PrioPacketVerify1_new();
ASSERT_TRUE(p1B != nullptr);
p2A = PrioPacketVerify2_new();
ASSERT_TRUE(p2A != nullptr);
p2B = PrioPacketVerify2_new();
ASSERT_TRUE(p2B != nullptr);
// I. CLIENT DATA SUBMISSION.
//
// Read in the client data packets
unsigned int aLen = 0, bLen = 0;
mozilla::dom::AutoJSAPI jsAPI;
ASSERT_TRUE(jsAPI.Init(xpc::PrivilegedJunkScope()));
JSContext* cx = jsAPI.cx();
mozilla::dom::GlobalObject global(cx, xpc::PrivilegedJunkScope());
nsCString batchID;
batchID = (char*)(batchIDStr);
mozilla::dom::PrioParams prioParams;
FallibleTArray<bool> sequence;
*(sequence.AppendElement(mozilla::fallible)) = dataItems[0];
*(sequence.AppendElement(mozilla::fallible)) = dataItems[1];
*(sequence.AppendElement(mozilla::fallible)) = dataItems[2];
ASSERT_TRUE(prioParams.mBooleans.Assign(sequence));
mozilla::dom::RootedDictionary<mozilla::dom::PrioEncodedData> prioEncodedData(
cx);
mozilla::ErrorResult rv;
rv =
mozilla::dom::PrioEncoder::SetKeys(reinterpret_cast<const char*>(pkHexA),
reinterpret_cast<const char*>(pkHexB));
ASSERT_FALSE(rv.Failed());
mozilla::dom::PrioEncoder::Encode(global, batchID, prioParams,
prioEncodedData, rv);
ASSERT_FALSE(rv.Failed());
prioEncodedData.mA.Value().ComputeState();
prioEncodedData.mB.Value().ComputeState();
forServerA = prioEncodedData.mA.Value().Data();
forServerB = prioEncodedData.mB.Value().Data();
aLen = prioEncodedData.mA.Value().Length();
bLen = prioEncodedData.mB.Value().Length();
// II. VALIDATION PROTOCOL. (at servers)
//
// The servers now run a short 2-step protocol to check each
// client's packet:
// 1) Servers A and B broadcast one message (PrioPacketVerify1)
// to each other.
// 2) Servers A and B broadcast another message (PrioPacketVerify2)
// to each other.
// 3) Servers A and B can both determine whether the client's data
// submission is well-formed (in which case they add it to their
// running total of aggregate statistics) or ill-formed
// (in which case they ignore it).
// These messages must be sent over an authenticated channel, so
// that each server is assured that every received message came
// from its peer.
// Set up a Prio verifier object.
prioRv = PrioVerifier_set_data(vA, forServerA, aLen);
ASSERT_TRUE(prioRv == SECSuccess);
prioRv = PrioVerifier_set_data(vB, forServerB, bLen);
ASSERT_TRUE(prioRv == SECSuccess);
// Both servers produce a packet1. Server A sends p1A to Server B
// and vice versa.
prioRv = PrioPacketVerify1_set_data(p1A, vA);
ASSERT_TRUE(prioRv == SECSuccess);
prioRv = PrioPacketVerify1_set_data(p1B, vB);
ASSERT_TRUE(prioRv == SECSuccess);
// Both servers produce a packet2. Server A sends p2A to Server B
// and vice versa.
prioRv = PrioPacketVerify2_set_data(p2A, vA, p1A, p1B);
ASSERT_TRUE(prioRv == SECSuccess);
prioRv = PrioPacketVerify2_set_data(p2B, vB, p1A, p1B);
ASSERT_TRUE(prioRv == SECSuccess);
// Using p2A and p2B, the servers can determine whether the request
// is valid. (In fact, only Server A needs to perform this
// check, since Server A can just tell Server B whether the check
// succeeded or failed.)
prioRv = PrioVerifier_isValid(vA, p2A, p2B);
ASSERT_TRUE(prioRv == SECSuccess);
prioRv = PrioVerifier_isValid(vB, p2A, p2B);
ASSERT_TRUE(prioRv == SECSuccess);
// If we get here, the client packet is valid, so add it to the aggregate
// statistic counter for both servers.
prioRv = PrioServer_aggregate(sA, vA);
ASSERT_TRUE(prioRv == SECSuccess);
prioRv = PrioServer_aggregate(sB, vB);
ASSERT_TRUE(prioRv == SECSuccess);
// The servers repeat the steps above for each client submission.
// III. PRODUCTION OF AGGREGATE STATISTICS.
//
// After collecting aggregates from MANY clients, the servers can compute
// their shares of the aggregate statistics.
//
// Server B can send tB to Server A.
prioRv = PrioTotalShare_set_data(tA, sA);
ASSERT_TRUE(prioRv == SECSuccess);
prioRv = PrioTotalShare_set_data(tB, sB);
ASSERT_TRUE(prioRv == SECSuccess);
// Once Server A has tA and tB, it can learn the aggregate statistics
// in the clear.
prioRv = PrioTotalShare_final(cfg, output, tA, tB);
ASSERT_TRUE(prioRv == SECSuccess);
for (int i = 0; i < ndata; i++) {
ASSERT_TRUE(output[i] == dataItems[i]);
}
PrioTotalShare_clear(tA);
PrioTotalShare_clear(tB);
PrioPacketVerify2_clear(p2A);
PrioPacketVerify2_clear(p2B);
PrioPacketVerify1_clear(p1A);
PrioPacketVerify1_clear(p1B);
PrioVerifier_clear(vA);
PrioVerifier_clear(vB);
PrioServer_clear(sA);
PrioServer_clear(sB);
PrioConfig_clear(cfg);
PublicKey_clear(pkA);
PublicKey_clear(pkB);
PrivateKey_clear(skA);
PrivateKey_clear(skB);
Prio_clear();
}
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, you can obtain one at http://mozilla.org/MPL/2.0/.
UNIFIED_SOURCES += [
"TestPrioEncoder.cpp",
]
LOCAL_INCLUDES += [
"/dom/prio",
"/third_party/msgpack/include",
"/third_party/prio/include",
]
FINAL_LIBRARY = "xul-gtest"
......@@ -46,6 +46,9 @@ with Files('webkit/**'):
with Files('xsimd/**'):
BUG_COMPONENT = ('Firefox Build System', 'General')
with Files('prio/**'):
BUG_COMPONENT = ('Firefox Build System', 'General')
with Files('msgpack/**'):
BUG_COMPONENT = ('Firefox Build System', 'General')
......
This directory contains the Prio source from the upstream repo:
https://github.com/mozilla/libprio
Current version: 1.6 [commit 52643cefe6662b4099e16a40a057cb60651ab001]
UPDATING:
Our in-tree copy of Prio does not depend on any generated files from the
upstream build system. Therefore, it should be sufficient to simply overwrite
the in-tree files one the updated ones from upstream to perform updates.
To simplify this, the in-tree copy can be updated by running
sh update.sh
from within the third_party/libprio directory.
If the collection of source files changes, manual updates to moz.build may be
needed as we don't use the upstream makefiles.
/*
* Copyright (c) 2018, Henry Corrigan-Gibbs
*
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __PRIO_H__
#define __PRIO_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <blapit.h>
#include <msgpack.h>
#include <pk11pub.h>
#include <seccomon.h>
#include <stdbool.h>
#include <stddef.h>
/* Seed for a pseudo-random generator (PRG). */
#define PRG_SEED_LENGTH AES_128_KEY_LENGTH
typedef unsigned char PrioPRGSeed[PRG_SEED_LENGTH];
/* Length of a raw curve25519 public key, in bytes. */
#define CURVE25519_KEY_LEN 32
/* Length of a hex-encoded curve25519 public key, in bytes. */
#define CURVE25519_KEY_LEN_HEX 64
/*
* Type for each of the two servers.
*/
typedef enum { PRIO_SERVER_A, PRIO_SERVER_B } PrioServerId;
/*
* Opaque types
*/
typedef struct prio_config* PrioConfig;
typedef const struct prio_config* const_PrioConfig;
typedef struct prio_server* PrioServer;
typedef const struct prio_server* const_PrioServer;
typedef struct prio_verifier* PrioVerifier;
typedef const struct prio_verifier* const_PrioVerifier;
typedef struct prio_packet_verify1* PrioPacketVerify1;
typedef const struct prio_packet_verify1* const_PrioPacketVerify1;
typedef struct prio_packet_verify2* PrioPacketVerify2;
typedef const struct prio_packet_verify2* const_PrioPacketVerify2;
typedef struct prio_total_share* PrioTotalShare;
typedef const struct prio_total_share* const_PrioTotalShare;
typedef SECKEYPublicKey* PublicKey;
typedef const SECKEYPublicKey* const_PublicKey;
typedef SECKEYPrivateKey* PrivateKey;
typedef const SECKEYPrivateKey* const_PrivateKey;
/*
* Initialize and clear random number generator state.
* You must call Prio_init() before using the library.
* To avoid memory leaks, call Prio_clear() afterwards.
*/
SECStatus Prio_init();
void Prio_clear();
/*
* PrioConfig holds the system parameters. The two relevant things determined
* by the config object are:
* (1) the number of data fields we are collecting, and
* (2) the modulus we use for modular arithmetic.
* The default configuration uses an 87-bit modulus.
*
* The value `nFields` must be in the range `0 < nFields <= max`, where `max`
* is the value returned by the function `PrioConfig_maxDataFields()` below.
*
* The `batch_id` field specifies which "batch" of aggregate statistics we are
* computing. For example, if the aggregate statistics are computed every 24
* hours, the `batch_id` might be set to an encoding of the date. The clients
* and servers must all use the same `batch_id` for each run of the protocol.
* Each set of aggregate statistics should use a different `batch_id`.
*
* `PrioConfig_new` does not keep a pointer to the `batch_id` string that the
* caller passes in, so you may free the `batch_id` string as soon as
* `PrioConfig_new` returns.
*/
PrioConfig PrioConfig_new(int nFields, PublicKey serverA, PublicKey serverB,
const unsigned char* batchId,
unsigned int batchIdLen);
void PrioConfig_clear(PrioConfig cfg);
int PrioConfig_numDataFields(const_PrioConfig cfg);
/*
* Return the maximum number of data fields that the implementation supports.
*/
int PrioConfig_maxDataFields(void);
/*
* Create a PrioConfig object with no encryption keys. This routine is
* useful for testing, but PrioClient_encode() will always fail when used with
* this config.
*/
PrioConfig PrioConfig_newTest(int nFields);
/*
* We use the PublicKey and PrivateKey objects for public-key encryption. Each
* Prio server has an associated public key, and the clients use these keys to
* encrypt messages to the servers.
*/
SECStatus Keypair_new(PrivateKey* pvtkey, PublicKey* pubkey);
/*
* Import a new curve25519 public/private key from the raw bytes given. When
* importing a private key, you must pass in the corresponding public key as
* well. The byte arrays given as input should be of length
* `CURVE25519_KEY_LEN`.
*
* These functions will allocate a new `PublicKey`/`PrivateKey` object, which
* the caller must free using `PublicKey_clear`/`PrivateKey_clear`.
*/
SECStatus PublicKey_import(PublicKey* pk, const unsigned char* data,
unsigned int dataLen);
SECStatus PrivateKey_import(PrivateKey* sk, const unsigned char* privData,
unsigned int privDataLen,
const unsigned char* pubData,
unsigned int pubDataLen);
/*
* Import a new curve25519 public/private key from a hex string that contains
* only the characters 0-9a-fA-F.
*
* The hex strings passed in must each be of length `CURVE25519_KEY_LEN_HEX`.
* These functions will allocate a new `PublicKey`/`PrivateKey` object, which
* the caller must free using `PublicKey_clear`/`PrivateKey_clear`.
*/
SECStatus PublicKey_import_hex(PublicKey* pk, const unsigned char* hexData,
unsigned int dataLen);
SECStatus PrivateKey_import_hex(PrivateKey* sk,
const unsigned char* privHexData,
unsigned int privDataLen,
const unsigned char* pubHexData,
unsigned int pubDataLen);
/*
* Export a curve25519 key as a raw byte-array.
*
* The output buffer `data` must have length exactly `CURVE25519_KEY_LEN`.
*/
SECStatus PublicKey_export(const_PublicKey pk, unsigned char* data,
unsigned int dataLen);
SECStatus PrivateKey_export(PrivateKey sk, unsigned char* data,
unsigned int dataLen);
/*
* Export a curve25519 key as a NULL-terminated hex string.
*
* The output buffer `data` must have length exactly `CURVE25519_KEY_LEN_HEX +
* 1`.
*/
SECStatus PublicKey_export_hex(const_PublicKey pk, unsigned char* data,
unsigned int dataLen);
SECStatus PrivateKey_export_hex(PrivateKey sk, unsigned char* data,
unsigned int dataLen);
void PublicKey_clear(PublicKey pubkey);
void PrivateKey_clear(PrivateKey pvtkey);
/*
* PrioPacketClient_encode
*
* Takes as input a pointer to an array (`data_in`) of boolean values
* whose length is equal to the number of data fields specified in
* the config. It then encodes the data for servers A and B into a
* string.
*
* NOTE: The caller must free() the strings `for_server_a` and
* `for_server_b` to avoid memory leaks.
*/
SECStatus PrioClient_encode(const_PrioConfig cfg, const bool* data_in,
unsigned char** forServerA, unsigned int* aLen,
unsigned char** forServerB, unsigned int* bLen);
/*
* Generate a new PRG seed using the NSS global randomness source.
* Use this routine to initialize the secret that the two Prio servers
* share.
*/
SECStatus PrioPRGSeed_randomize(PrioPRGSeed* seed);
/*
* The PrioServer object holds the state of the Prio servers.
* Pass in the _same_ secret PRGSeed when initializing the two servers.
* The PRGSeed must remain secret to the two servers.
*/
PrioServer PrioServer_new(const_PrioConfig cfg, PrioServerId serverIdx,
PrivateKey serverPriv,
const PrioPRGSeed serverSharedSecret);
void PrioServer_clear(PrioServer s);
/*
* After receiving a client packet, each of the servers generate
* a PrioVerifier object that they use to check whether the client's
* encoded packet is well formed.
*/
PrioVerifier PrioVerifier_new(PrioServer s);
void PrioVerifier_clear(PrioVerifier v);
/*
* Read in encrypted data from the client, decrypt it, and prepare to check the
* request for validity.
*/
SECStatus PrioVerifier_set_data(PrioVerifier v, unsigned char* data,
unsigned int dataLen);
/*
* Generate the first packet that servers need to exchange to verify the
* client's submission. This should be sent over a TLS connection between the
* servers.
*/
PrioPacketVerify1 PrioPacketVerify1_new(void);
void PrioPacketVerify1_clear(PrioPacketVerify1 p1);
SECStatus PrioPacketVerify1_set_data(PrioPacketVerify1 p1,
const_PrioVerifier v);
SECStatus PrioPacketVerify1_write(const_PrioPacketVerify1 p,
msgpack_packer* pk);
SECStatus PrioPacketVerify1_read(PrioPacketVerify1 p, msgpack_unpacker* upk,
const_PrioConfig cfg);
/*
* Generate the second packet that the servers need to exchange to verify the
* client's submission. The routine takes as input the PrioPacketVerify1
* packets from both server A and server B.
*
* This should be sent over a TLS connection between the servers.
*/
PrioPacketVerify2 PrioPacketVerify2_new(void);
void PrioPacketVerify2_clear(PrioPacketVerify2 p);
SECStatus PrioPacketVerify2_set_data(PrioPacketVerify2 p2, const_PrioVerifier v,
const_PrioPacketVerify1 p1A,
const_PrioPacketVerify1 p1B);
SECStatus PrioPacketVerify2_write(const_PrioPacketVerify2 p,
msgpack_packer* pk);
SECStatus PrioPacketVerify2_read(PrioPacketVerify2 p, msgpack_unpacker* upk,
const_PrioConfig cfg);
/*
* Use the PrioPacketVerify2s from both servers to check whether
* the client's submission is well formed.
*/
SECStatus PrioVerifier_isValid(const_PrioVerifier v, const_PrioPacketVerify2 pA,
const_PrioPacketVerify2 pB);
/*
* Each of the two servers calls this routine to aggregate the data
* submission from a client that is included in the PrioVerifier object.
*
* IMPORTANT: This routine does *not* check the validity of the client's
* data packet. The servers must execute the verification checks
* above before aggregating any client data.
*/
SECStatus PrioServer_aggregate(PrioServer s, PrioVerifier v);
/*
* After the servers have aggregated data packets from "enough" clients
* (this determines the anonymity set size), each server runs this routine
* to get a share of the aggregate statistics.
*/
PrioTotalShare PrioTotalShare_new(void);
void PrioTotalShare_clear(PrioTotalShare t);
SECStatus PrioTotalShare_set_data(PrioTotalShare t, const_PrioServer s);
SECStatus PrioTotalShare_write(const_PrioTotalShare t, msgpack_packer* pk);
SECStatus PrioTotalShare_read(PrioTotalShare t, msgpack_unpacker* upk,
const_PrioConfig cfg);
/*
* Read the output data into an array of unsigned longs. You should
* be sure that each data value can fit into a single `unsigned long`
* and that the pointer `output` points to a buffer large enough to
* store one long per data field.
*
* This function returns failure if some final data value is too
* long to fit in an `unsigned long`.
*/
SECStatus PrioTotalShare_final(const_PrioConfig cfg, unsigned long long* output,
const_PrioTotalShare tA,
const_PrioTotalShare tB);
#endif /* __PRIO_H__ */
#ifdef __cplusplus
}
#endif
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
LOCAL_INCLUDES += [
'/security/nss/lib/freebl/mpi',
'/third_party/msgpack/include',
'include',
]
EXPORTS += [
'include/mprio.h',
]
# We allow warnings for third-party code that can be updated from upstream.
AllowCompilerWarnings()
NoVisibilityFlags()
SOURCES += [
'/security/nss/lib/freebl/mpi/montmulf.c',
'/security/nss/lib/freebl/mpi/mp_gf2m.c',
'/security/nss/lib/freebl/mpi/mpcpucache.c',
'/security/nss/lib/freebl/mpi/mpi.c',
'/security/nss/lib/freebl/mpi/mplogic.c',
'/security/nss/lib/freebl/mpi/mpmontg.c',
'/security/nss/lib/freebl/mpi/mpprime.c',
]
SOURCES += [
'prio/client.c',
'prio/config.c',
'prio/encrypt.c',
'prio/mparray.c',
'prio/poly.c',
'prio/prg.c',
'prio/rand.c',
'prio/serial.c',
'prio/server.c',
'prio/share.c',
]
FINAL_LIBRARY = 'xul'
# Use PKCS11 v2 struct definitions for now, otherwise NSS requires
# CK_GCM_PARAMS.ulIvBits to be set. This workaround is only required
# until NSS 3.52 RTM and upstream correctly initializes the field.
DEFINES['NSS_PKCS11_2_0_COMPAT'] = True
import os
Import('env')
penv = env.Clone()
src = [
"client.c",
"config.c",
"encrypt.c",
"mparray.c",
"poly.c",
"rand.c",
"prg.c",
"server.c",
"serial.c",
"share.c",
]
libs = [
"msgpackc"
]
penv.Append(LIBS = libs)
penv.StaticLibrary("mprio", src)
/*
* Copyright (c) 2018, Henry Corrigan-Gibbs
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mpi.h>
#include <mprio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "client.h"
#include "config.h"
#include "encrypt.h"
#include "poly.h"
#include "rand.h"
#include "serial.h"
#include "share.h"
#include "util.h"
// Let the points of data_in be [x1, x2, x3, ... ].
// We construct the polynomial f such that
// (a) f(0) = random,
// (b) f(i) = x_i for all i >= 1,
// (c) degree(f)+1 is a power of two.
// We then evaluate f at the 2N-th roots of unity
// and we return these evaluations as `evals_out`
// and we return f(0) as `const_term`.
static SECStatus
data_polynomial_evals(const_PrioConfig cfg, const_MPArray data_in,
MPArray evals_out, mp_int* const_term)
{
SECStatus rv = SECSuccess;
const mp_int* mod = &cfg->modulus;
MPArray points_f = NULL;
MPArray poly_f = NULL;
// Number of multiplication gates in the Valid() circuit.
const int mul_gates = cfg->num_data_fields;
// Little n is the number of points on the polynomials.
// The constant term is randomized, so it's (mul_gates + 1).
const int n = mul_gates + 1;
// Big N is n rounded up to a power of two.
const int N = next_power_of_two(n);
P_CHECKA(points_f = MPArray_new(N));
P_CHECKA(poly_f = MPArray_new(N));
// Set constant term f(0) to random
P_CHECKC(rand_int(&points_f->data[0], mod));
MP_CHECKC(mp_copy(&points_f->data[0], const_term));
// Set other values of f(x)
for (int i = 1; i < n; i++) {
MP_CHECKC(mp_copy(&data_in->data[i - 1], &points_f->data[i]));
}
// Interpolate through the Nth roots of unity
P_CHECKC(poly_fft(poly_f, points_f, cfg, true));
// Evaluate at all 2N-th roots of unity.
// To do so, first resize the eval arrays and fill upper
// values with zeros.
P_CHECKC(MPArray_resize(poly_f, 2 * N));
P_CHECKC(MPArray_resize(evals_out, 2 * N));
// Evaluate at the 2N-th roots of unity
P_CHECKC(poly_fft(evals_out, poly_f, cfg, false));
cleanup:
MPArray_clear(points_f);
MPArray_clear(poly_f);
return rv;
}
static SECStatus
share_polynomials(const_PrioConfig cfg, const_MPArray data_in,
PrioPacketClient pA, PrioPacketClient pB, PRG prgB)
{
SECStatus rv = SECSuccess;
const mp_int* mod = &cfg->modulus;
const_MPArray points_f = data_in;
mp_int f0, g0;
MP_DIGITS(&f0) = NULL;
MP_DIGITS(&g0) = NULL;
MPArray points_g = NULL;
MPArray evals_f_2N = NULL;
MPArray evals_g_2N = NULL;
P_CHECKA(points_g = MPArray_dup(points_f));
P_CHECKA(evals_f_2N = MPArray_new(0));
P_CHECKA(evals_g_2N = MPArray_new(0));
MP_CHECKC(mp_init(&f0));
MP_CHECKC(mp_init(&g0));
for (int i = 0; i < points_f->len; i++) {
// For each input value x_i, we compute x_i * (x_i-1).
// f(i) = x_i
// g(i) = x_i - 1
MP_CHECKC(mp_sub_d(&points_g->data[i], 1, &points_g->data[i]));
MP_CHECKC(mp_mod(&points_g->data[i], mod, &points_g->data[i]));
}
P_CHECKC(data_polynomial_evals(cfg, points_f, evals_f_2N, &f0));
P_CHECKC(data_polynomial_evals(cfg, points_g, evals_g_2N, &g0));
// The values f(0) and g(0) are set to random values.
// We must send to each server a share of the points
// f(0), g(0), and h(0) = f(0)*g(0)
P_CHECKC(share_int(cfg, &f0, &pA->f0_share, &pB->f0_share));
P_CHECKC(share_int(cfg, &g0, &pA->g0_share, &pB->g0_share));
// Compute h(0) = f(0)*g(0).
MP_CHECKC(mp_mulmod(&f0, &g0, mod, &f0));
// Give one share of h(0) to each server.
P_CHECKC(share_int(cfg, &f0, &pA->h0_share, &pB->h0_share));
// const int lenN = (evals_f_2N->len/2);
// P_CHECKC (MPArray_resize (pA->shares.A.h_points, lenN));
// We need to send to the servers the evaluations of
// f(r) * g(r)
// for all 2N-th roots of unity r that are not also
// N-th roots of unity.
//
// For each such root r, compute h(r) = f(r)*g(r) and
// send a share of this value to each server.
int j = 0;
for (int i = 1; i < evals_f_2N->len; i += 2) {
MP_CHECKC(mp_mulmod(&evals_f_2N->data[i], &evals_g_2N->data[i], mod, &f0));
P_CHECKC(PRG_share_int(prgB, &pA->shares.A.h_points->data[j], &f0, cfg));
j++;
}
cleanup:
MPArray_clear(evals_f_2N);
MPArray_clear(evals_g_2N);
MPArray_clear(points_g);
mp_clear(&f0);
mp_clear(&g0);
return rv;
}
PrioPacketClient
PrioPacketClient_new(const_PrioConfig cfg, PrioServerId for_server)
{
SECStatus rv = SECSuccess;
const int data_len = cfg->num_data_fields;
PrioPacketClient p = NULL;
p = malloc(sizeof(*p));
if (!p)
return NULL;
p->for_server = for_server;
p->triple = NULL;
MP_DIGITS(&p->f0_share) = NULL;
MP_DIGITS(&p->g0_share) = NULL;
MP_DIGITS(&p->h0_share) = NULL;
switch (p->for_server) {
case PRIO_SERVER_A:
p->shares.A.data_shares = NULL;
p->shares.A.h_points = NULL;
break;
case PRIO_SERVER_B:
memset(p->shares.B.seed, 0, PRG_SEED_LENGTH);
break;
default:
// Should never get here
rv = SECFailure;
goto cleanup;
}
MP_CHECKC(mp_init(&p->f0_share));
MP_CHECKC(mp_init(&p->g0_share));
MP_CHECKC(mp_init(&p->h0_share));
P_CHECKA(p->triple = BeaverTriple_new());
if (p->for_server == PRIO_SERVER_A) {
const int num_h_points = PrioConfig_hPoints(cfg);
P_CHECKA(p->shares.A.data_shares = MPArray_new(data_len));
P_CHECKA(p->shares.A.h_points = MPArray_new(num_h_points));
}
cleanup:
if (rv != SECSuccess) {
PrioPacketClient_clear(p);
return NULL;
}
return p;
}
SECStatus
PrioPacketClient_set_data(const_PrioConfig cfg, const bool* data_in,
PrioPacketClient pA, PrioPacketClient pB)
{
MPArray client_data = NULL;
PRG prgB = NULL;
SECStatus rv = SECSuccess;
const int data_len = cfg->num_data_fields;
if (!data_in)
return SECFailure;
P_CHECKC(PrioPRGSeed_randomize(&pB->shares.B.seed));
P_CHECKA(prgB = PRG_new(pB->shares.B.seed));
P_CHECKC(BeaverTriple_set_rand(cfg, pA->triple, pB->triple));
P_CHECKA(client_data = MPArray_new_bool(data_len, data_in));
P_CHECKC(PRG_share_array(prgB, pA->shares.A.data_shares, client_data, cfg));
P_CHECKC(share_polynomials(cfg, client_data, pA, pB, prgB));
cleanup:
MPArray_clear(client_data);
PRG_clear(prgB);
return rv;
}
void
PrioPacketClient_clear(PrioPacketClient p)
{
if (p == NULL)
return;
if (p->for_server == PRIO_SERVER_A) {
MPArray_clear(p->shares.A.h_points);
MPArray_clear(p->shares.A.data_shares);
}
BeaverTriple_clear(p->triple);
mp_clear(&p->f0_share);
mp_clear(&p->g0_share);
mp_clear(&p->h0_share);
free(p);
}
bool
PrioPacketClient_areEqual(const_PrioPacketClient p1, const_PrioPacketClient p2)
{
if (!BeaverTriple_areEqual(p1->triple, p2->triple))
return false;
if (mp_cmp(&p1->f0_share, &p2->f0_share))
return false;
if (mp_cmp(&p1->g0_share, &p2->g0_share))
return false;
if (mp_cmp(&p1->h0_share, &p2->h0_share))
return false;
if (p1->for_server != p2->for_server)
return false;
switch (p1->for_server) {
case PRIO_SERVER_A:
if (!MPArray_areEqual(p1->shares.A.data_shares, p2->shares.A.data_shares))
return false;
if (!MPArray_areEqual(p1->shares.A.h_points, p2->shares.A.h_points))
return false;
break;
case PRIO_SERVER_B:
if (memcmp(p1->shares.B.seed, p2->shares.B.seed, PRG_SEED_LENGTH))
return false;
break;
default:
// Should never get here.
return false;
}
return true;
}
SECStatus
PrioClient_encode(const_PrioConfig cfg, const bool* data_in,
unsigned char** for_server_a, unsigned int* aLen,
unsigned char** for_server_b, unsigned int* bLen)
{
SECStatus rv = SECSuccess;
PrioPacketClient pA = NULL;
PrioPacketClient pB = NULL;
*for_server_a = NULL;
*for_server_b = NULL;
msgpack_sbuffer sbufA, sbufB;
msgpack_packer packerA, packerB;
msgpack_sbuffer_init(&sbufA);
msgpack_sbuffer_init(&sbufB);
msgpack_packer_init(&packerA, &sbufA, msgpack_sbuffer_write);
msgpack_packer_init(&packerB, &sbufB, msgpack_sbuffer_write);
P_CHECKA(pA = PrioPacketClient_new(cfg, PRIO_SERVER_A));
P_CHECKA(pB = PrioPacketClient_new(cfg, PRIO_SERVER_B));
P_CHECKC(PrioPacketClient_set_data(cfg, data_in, pA, pB));
P_CHECKC(serial_write_packet_client(&packerA, pA, cfg));
P_CHECKC(serial_write_packet_client(&packerB, pB, cfg));
P_CHECKC(PublicKey_encryptSize(sbufA.size, aLen));
P_CHECKC(PublicKey_encryptSize(sbufB.size, bLen));
P_CHECKA(*for_server_a = malloc(*aLen));
P_CHECKA(*for_server_b = malloc(*bLen));
unsigned int writtenA;
unsigned int writtenB;
P_CHECKC(PublicKey_encrypt(cfg->server_a_pub, *for_server_a, &writtenA, *aLen,
(unsigned char*)sbufA.data, sbufA.size));
P_CHECKC(PublicKey_encrypt(cfg->server_b_pub, *for_server_b, &writtenB, *bLen,
(unsigned char*)sbufB.data, sbufB.size));
P_CHECKCB(writtenA == *aLen);
P_CHECKCB(writtenB == *bLen);
cleanup:
if (rv != SECSuccess) {
if (*for_server_a)
free(*for_server_a);
if (*for_server_b)
free(*for_server_b);
*for_server_a = NULL;
*for_server_b = NULL;
}
PrioPacketClient_clear(pA);
PrioPacketClient_clear(pB);
msgpack_sbuffer_destroy(&sbufA);
msgpack_sbuffer_destroy(&sbufB);
return rv;
}
SECStatus
PrioPacketClient_decrypt(PrioPacketClient p, const_PrioConfig cfg,
PrivateKey server_priv, const unsigned char* data_in,
unsigned int data_len)
{
SECStatus rv = SECSuccess;
msgpack_unpacker upk;
if (!msgpack_unpacker_init(&upk, data_len)) {
return SECFailure;
}
// Decrypt the ciphertext into dec_buf
unsigned int bytes_decrypted;
P_CHECKC(PrivateKey_decrypt(server_priv,
(unsigned char*)msgpack_unpacker_buffer(&upk),
&bytes_decrypted, data_len, data_in, data_len));
msgpack_unpacker_buffer_consumed(&upk, bytes_decrypted);
P_CHECKC(serial_read_packet_client(&upk, p, cfg));
cleanup:
msgpack_unpacker_destroy(&upk);
return rv;
}
/*
* Copyright (c) 2018, Henry Corrigan-Gibbs
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#ifndef __CLIENT_H__
#define __CLIENT_H__
#include "mparray.h"
#include "prg.h"
#include "share.h"
/*
* The PrioPacketClient object holds the encoded client data.
* The client sends one packet to server A and one packet to
* server B. The `for_server` parameter determines which server
* the packet is for.
*/
typedef struct prio_packet_client* PrioPacketClient;
typedef const struct prio_packet_client* const_PrioPacketClient;
struct server_a_data
{
// These values are only set for server A.
MPArray data_shares;
MPArray h_points;
};
struct server_b_data
{
// This value is only used for server B.
//
// We use a pseudo-random generator to compress the secret-shared data
// values. See Appendix I of the Prio paper (the paragraph starting
// "Optimization: PRG secret sharing.") for details on this.
PrioPRGSeed seed;
};
/*
* The data that a Prio client sends to each server.
*/
struct prio_packet_client
{
// TODO: Can also use a PRG to avoid need for sending Beaver triple shares.
// Since this optimization only saves ~30 bytes of communication, we haven't
// bothered implementing it yet.
BeaverTriple triple;
mp_int f0_share, g0_share, h0_share;
PrioServerId for_server;
union
{
struct server_a_data A;
struct server_b_data B;
} shares;
};
PrioPacketClient PrioPacketClient_new(const_PrioConfig cfg,
PrioServerId for_server);
void PrioPacketClient_clear(PrioPacketClient p);
SECStatus PrioPacketClient_set_data(const_PrioConfig cfg, const bool* data_in,
PrioPacketClient for_server_a,
PrioPacketClient for_server_b);
SECStatus PrioPacketClient_decrypt(PrioPacketClient p, const_PrioConfig cfg,
PrivateKey server_priv,
const unsigned char* data_in,
unsigned int data_len);
bool PrioPacketClient_areEqual(const_PrioPacketClient p1,
const_PrioPacketClient p2);
#endif /* __CLIENT_H__ */
/*
* Copyright (c) 2018, Henry Corrigan-Gibbs
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <mprio.h>
#include <stdlib.h>
#include "config.h"
#include "mparray.h"
#include "params.h"
#include "rand.h"
#include "util.h"
int
PrioConfig_maxDataFields(void)
{
const int n_roots = 1 << Generator2Order;
return (n_roots >> 1) - 1;
}
PrioConfig
PrioConfig_new(int n_fields, PublicKey server_a, PublicKey server_b,
const unsigned char* batch_id, unsigned int batch_id_len)
{
SECStatus rv = SECSuccess;
PrioConfig cfg = malloc(sizeof(*cfg));
if (!cfg)
return NULL;
cfg->batch_id = NULL;
cfg->batch_id_len = batch_id_len;
cfg->server_a_pub = server_a;
cfg->server_b_pub = server_b;
cfg->num_data_fields = n_fields;
cfg->n_roots = 1 << Generator2Order;
MP_DIGITS(&cfg->modulus) = NULL;
MP_DIGITS(&cfg->inv2) = NULL;
MP_DIGITS(&cfg->generator) = NULL;
P_CHECKCB(cfg->n_roots > 1);
P_CHECKCB(cfg->num_data_fields <= PrioConfig_maxDataFields());
P_CHECKA(cfg->batch_id = malloc(batch_id_len));
strncpy((char*)cfg->batch_id, (char*)batch_id, batch_id_len);
MP_CHECKC(mp_init(&cfg->modulus));
MP_CHECKC(mp_read_radix(&cfg->modulus, Modulus, 16));
MP_CHECKC(mp_init(&cfg->generator));
MP_CHECKC(mp_read_radix(&cfg->generator, Generator, 16));
// Compute 2^{-1} modulo M
MP_CHECKC(mp_init(&cfg->inv2));
mp_set(&cfg->inv2, 2);
MP_CHECKC(mp_invmod(&cfg->inv2, &cfg->modulus, &cfg->inv2));
cleanup:
if (rv != SECSuccess) {
PrioConfig_clear(cfg);
return NULL;
}
return cfg;
}
PrioConfig
PrioConfig_newTest(int nFields)
{
return PrioConfig_new(nFields, NULL, NULL, (unsigned char*)"testBatch", 9);
}
void
PrioConfig_clear(PrioConfig cfg)
{
if (!cfg)
return;
if (cfg->batch_id)
free(cfg->batch_id);
mp_clear(&cfg->modulus);
mp_clear(&cfg->inv2);
mp_clear(&cfg->generator);
free(cfg);
}
int
PrioConfig_numDataFields(const_PrioConfig cfg)
{
return cfg->num_data_fields;
}
SECStatus
Prio_init(void)
{
return rand_init();
}
void
Prio_clear(void)
{
rand_clear();
}
int
PrioConfig_hPoints(const_PrioConfig cfg)
{
const int mul_gates = cfg->num_data_fields + 1;
const int N = next_power_of_two(mul_gates);
return N;
}
/*
* Copyright (c) 2018, Henry Corrigan-Gibbs
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#ifndef __CONFIG_H__
#define __CONFIG_H__
#include <mpi.h>
#include "mparray.h"
struct prio_config
{
int num_data_fields;
unsigned char* batch_id;
unsigned int batch_id_len;
PublicKey server_a_pub;
PublicKey server_b_pub;
mp_int modulus;
mp_int inv2;
int n_roots;
mp_int generator;
};
int PrioConfig_hPoints(const_PrioConfig cfg);
#endif /* __CONFIG_H__ */
/*
* Copyright (c) 2018, Henry Corrigan-Gibbs
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#ifndef __DEBUG_H__
#define __DEBUG_H__
#include <stdio.h>
#ifdef DEBUG
#define PRIO_DEBUG(msg) \
do { \
fprintf(stderr, "Error: %s\n", msg); \
} while (false);
#else
#define PRIO_DEBUG(msg) ;
#endif
#endif /* __DEBUG_H__ */
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment