Bug 1518728 - Update vendored libprio to 1.4. r=glandium, a=RyanVM
authorRobert Helmer <rhelmer@mozilla.com>
Mon, 14 Jan 2019 23:50:08 +0000
changeset 509518 7f847507d3bb84ed438fe58b898d07e15cab9a32
parent 509517 561137bf321030c3bb2c7cfb692f85d3ccda4622
child 509519 548bb5b013ffd0bfac11879c6fe89b276dddae24
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersglandium, RyanVM
bugs1518728
milestone65.0
Bug 1518728 - Update vendored libprio to 1.4. r=glandium, a=RyanVM Differential Revision: https://phabricator.services.mozilla.com/D16266
third_party/prio/README-mozilla
third_party/prio/include/mprio.h
third_party/prio/prio/config.c
third_party/prio/prio/encrypt.c
third_party/prio/prio/poly.c
third_party/prio/prio/prg.c
third_party/prio/prio/prg.h
third_party/prio/prio/server.c
third_party/prio/update.sh
--- a/third_party/prio/README-mozilla
+++ b/third_party/prio/README-mozilla
@@ -1,12 +1,12 @@
 This directory contains the Prio source from the upstream repo:
 https://github.com/mozilla/libprio
 
-Current version: 1.2 [commit 02a81fb652d385d0f4f10989d051317097ab55fb]
+Current version: 1.4 [commit a95cfdd5eaf7104582709c54ef23395d24d7f7fd]
 
 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
--- a/third_party/prio/include/mprio.h
+++ b/third_party/prio/include/mprio.h
@@ -72,109 +72,140 @@ 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 n_fields, PublicKey server_a, PublicKey server_b,
-                          const unsigned char* batch_id,
-                          unsigned int batch_id_len);
+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 n_fields);
+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 key from the raw bytes given. The key passed
- * in
- * as `data` should be of length `CURVE25519_KEY_LEN`. This function allocates
- * a new PublicKey object, which the caller must free using `PublicKey_clear`.
+ * 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);
-
-/*
- * Import a new curve25519 public key from a hex string that contains only the
- * characters 0-9a-fA-F. The hex string passed in as `hex_data` should be of
- * length `CURVE25519_KEY_LEN_HEX`. This function allocates a new PublicKey
- * object, which the caller must free using `PublicKey_clear`.
- */
-SECStatus PublicKey_import_hex(PublicKey* pk, const unsigned char* hex_data,
-                               unsigned int dataLen);
+SECStatus PrivateKey_import(PrivateKey* sk, const unsigned char* privData,
+                            unsigned int privDataLen,
+                            const unsigned char* pubData,
+                            unsigned int pubDataLen);
 
 /*
- * Export a curve25519 public key as a raw byte-array.
+ * 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_export(const_PublicKey pk,
-                           unsigned char data[CURVE25519_KEY_LEN]);
+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 public key as a NULL-terminated hex string.
+ * Export a curve25519 key as a raw byte-array.
+ *
+ * The output buffer `data` must have length exactly `CURVE25519_KEY_LEN`.
  */
-SECStatus PublicKey_export_hex(const_PublicKey pk,
-                               unsigned char data[CURVE25519_KEY_LEN_HEX + 1]);
+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** for_server_a, unsigned int* aLen,
-                            unsigned char** for_server_b, unsigned int* bLen);
+                            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 server_idx,
-                          PrivateKey server_priv,
-                          const PrioPRGSeed server_shared_secret);
+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);
@@ -250,21 +281,24 @@ void PrioTotalShare_clear(PrioTotalShare
 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 long and that
- * the pointer `output` points to a buffer large enough to store
- * one long per data field.
+ * 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* output,
+SECStatus PrioTotalShare_final(const_PrioConfig cfg, unsigned long long* output,
                                const_PrioTotalShare tA,
                                const_PrioTotalShare tB);
 
 #endif /* __PRIO_H__ */
 
 #ifdef __cplusplus
 }
 #endif
--- a/third_party/prio/prio/config.c
+++ b/third_party/prio/prio/config.c
@@ -46,16 +46,23 @@ initialize_roots(MPArray arr, const char
     for (unsigned int i = RootWidth, j = 1; i < n_chars; i += RootWidth, j++) {
       MP_CHECK(mp_read_radix(&arr->data[j], &values[i], 16));
     }
   }
 
   return SECSuccess;
 }
 
+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;
@@ -66,20 +73,18 @@ PrioConfig_new(int n_fields, PublicKey s
   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;
   cfg->roots = NULL;
   cfg->rootsInv = NULL;
 
-  if (cfg->num_data_fields >= cfg->n_roots) {
-    rv = SECFailure;
-    goto cleanup;
-  }
+  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));
 
   // Compute  2^{-1} modulo M
--- a/third_party/prio/prio/encrypt.c
+++ b/third_party/prio/prio/encrypt.c
@@ -21,25 +21,57 @@
 // Use 96-bit IV
 #define GCM_IV_LEN_BYTES 12
 // Use 128-bit auth tag
 #define GCM_TAG_LEN_BYTES 16
 
 #define PRIO_TAG "PrioPacket"
 #define AAD_LEN (strlen(PRIO_TAG) + CURVE25519_KEY_LEN + GCM_IV_LEN_BYTES)
 
-// The all-zeros curve25519 public key, as DER-encoded SKPI blob.
+// For an example of NSS curve25519 import/export code, see:
+// https://searchfox.org/nss/rev/cfd5fcba7efbfe116e2c08848075240ec3a92718/gtests/pk11_gtest/pk11_curve25519_unittest.cc#66
+
+// The all-zeros curve25519 public key, as DER-encoded SPKI blob.
 static const uint8_t curve25519_spki_zeros[] = {
   0x30, 0x39, 0x30, 0x14, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
   0x01, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, 0x47, 0x0f, 0x01,
   0x03, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 };
 
+// The all-zeros curve25519 private key, as a PKCS#8 blob.
+static const uint8_t curve25519_priv_zeros[] = {
+  0x30, 0x67, 0x02, 0x01, 0x00, 0x30, 0x14, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce,
+  0x3d, 0x02, 0x01, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, 0x47, 0x0f,
+  0x01, 0x04, 0x4c, 0x30, 0x4a, 0x02, 0x01, 0x01, 0x04, 0x20,
+
+  /* Byte index 36:  32 bytes of curve25519 private key. */
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+  /* misc type fields */
+  0xa1, 0x23, 0x03, 0x21,
+
+  /* Byte index 73:  32 bytes of curve25519 public key. */
+  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+// Index into `curve25519_priv_zeros` at which the private key begins.
+static const size_t curve25519_priv_sk_offset = 36;
+// Index into `curve25519_priv_zeros` at which the public key begins.
+static const size_t curve25519_priv_pk_offset = 73;
+
+static SECStatus key_from_hex(
+  unsigned char key_out[CURVE25519_KEY_LEN],
+  const unsigned char hex_in[CURVE25519_KEY_LEN_HEX]);
+
 // Note that we do not use isxdigit because it is locale-dependent
 // See: https://github.com/mozilla/libprio/issues/20
 static inline char
 is_hex_digit(char c)
 {
   return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') ||
          ('A' <= c && c <= 'F');
 }
@@ -101,16 +133,17 @@ PublicKey_import(PublicKey* pk, const un
   if (dataLen != CURVE25519_KEY_LEN)
     return SECFailure;
 
   P_CHECKA(key_bytes = calloc(dataLen, sizeof(unsigned char)));
   memcpy(key_bytes, data, dataLen);
 
   const int spki_len = sizeof(curve25519_spki_zeros);
   P_CHECKA(spki_data = calloc(spki_len, sizeof(uint8_t)));
+
   memcpy(spki_data, curve25519_spki_zeros, spki_len);
   SECItem spki_item = { siBuffer, spki_data, spki_len };
 
   // Import the all-zeros curve25519 public key.
   P_CHECKA(pkinfo = SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item));
   P_CHECKA(*pk = SECKEY_ExtractPublicKey(pkinfo));
 
   // Overwrite the all-zeros public key with the 32-byte curve25519 public key
@@ -126,69 +159,215 @@ cleanup:
     SECKEY_DestroySubjectPublicKeyInfo(pkinfo);
 
   if (rv != SECSuccess)
     PublicKey_clear(*pk);
   return rv;
 }
 
 SECStatus
-PublicKey_import_hex(PublicKey* pk, const unsigned char* hex_data,
+PrivateKey_import(PrivateKey* sk, const unsigned char* sk_data,
+                  unsigned int sk_data_len, const unsigned char* pk_data,
+                  unsigned int pk_data_len)
+{
+  if (sk_data_len != CURVE25519_KEY_LEN || !sk_data) {
+    return SECFailure;
+  }
+
+  if (pk_data_len != CURVE25519_KEY_LEN || !pk_data) {
+    return SECFailure;
+  }
+
+  SECStatus rv = SECSuccess;
+  PK11SlotInfo* slot = NULL;
+  uint8_t* zero_priv_data = NULL;
+  *sk = NULL;
+  const int zero_priv_len = sizeof(curve25519_priv_zeros);
+
+  P_CHECKA(slot = PK11_GetInternalSlot());
+
+  P_CHECKA(zero_priv_data = calloc(zero_priv_len, sizeof(uint8_t)));
+  SECItem zero_priv_item = { siBuffer, zero_priv_data, zero_priv_len };
+
+  // Copy the PKCS#8-encoded keypair into writable buffer.
+  memcpy(zero_priv_data, curve25519_priv_zeros, zero_priv_len);
+  // Copy private key into bytes beginning at index `curve25519_priv_sk_offset`.
+  memcpy(zero_priv_data + curve25519_priv_sk_offset, sk_data, sk_data_len);
+  // Copy private key into bytes beginning at index `curve25519_priv_pk_offset`.
+  memcpy(zero_priv_data + curve25519_priv_pk_offset, pk_data, pk_data_len);
+
+  P_CHECKC(PK11_ImportDERPrivateKeyInfoAndReturnKey(
+    slot, &zero_priv_item, NULL, NULL, PR_FALSE, PR_FALSE, KU_ALL, sk, NULL));
+
+cleanup:
+  if (slot) {
+    PK11_FreeSlot(slot);
+  }
+  if (zero_priv_data) {
+    free(zero_priv_data);
+  }
+  if (rv != SECSuccess) {
+    PrivateKey_clear(*sk);
+  }
+  return rv;
+}
+
+SECStatus
+PublicKey_import_hex(PublicKey* pk, const unsigned char* hexData,
                      unsigned int dataLen)
 {
   unsigned char raw_bytes[CURVE25519_KEY_LEN];
 
-  if (dataLen != CURVE25519_KEY_LEN_HEX)
+  if (dataLen != CURVE25519_KEY_LEN_HEX || !hexData) {
     return SECFailure;
-
-  for (unsigned int i = 0; i < dataLen; i++) {
-    if (!is_hex_digit(hex_data[i]))
-      return SECFailure;
   }
 
-  const unsigned char* p = hex_data;
-  for (unsigned int i = 0; i < CURVE25519_KEY_LEN; i++) {
-    uint8_t d0 = hex_to_int(p[0]);
-    uint8_t d1 = hex_to_int(p[1]);
-    raw_bytes[i] = (d0 << 4) | d1;
-    p += 2;
+  if (key_from_hex(raw_bytes, hexData) != SECSuccess) {
+    return SECFailure;
   }
 
   return PublicKey_import(pk, raw_bytes, CURVE25519_KEY_LEN);
 }
 
 SECStatus
-PublicKey_export(const_PublicKey pk, unsigned char data[CURVE25519_KEY_LEN])
+PrivateKey_import_hex(PrivateKey* sk, const unsigned char* privHexData,
+                      unsigned int privDataLen, const unsigned char* pubHexData,
+                      unsigned int pubDataLen)
+{
+  SECStatus rv = SECSuccess;
+  unsigned char raw_priv[CURVE25519_KEY_LEN];
+  unsigned char raw_pub[CURVE25519_KEY_LEN];
+
+  if (privDataLen != CURVE25519_KEY_LEN_HEX ||
+      pubDataLen != CURVE25519_KEY_LEN_HEX) {
+    return SECFailure;
+  }
+
+  if (!privHexData || !pubHexData) {
+    return SECFailure;
+  }
+
+  P_CHECK(key_from_hex(raw_priv, privHexData));
+  P_CHECK(key_from_hex(raw_pub, pubHexData));
+
+  return PrivateKey_import(sk, raw_priv, CURVE25519_KEY_LEN, raw_pub,
+                           CURVE25519_KEY_LEN);
+}
+
+SECStatus
+PublicKey_export(const_PublicKey pk, unsigned char* data, unsigned int dataLen)
+{
+  if (pk == NULL || dataLen != CURVE25519_KEY_LEN) {
+    return SECFailure;
+  }
+
+  const SECItem* key = &pk->u.ec.publicValue;
+  if (key->len != CURVE25519_KEY_LEN) {
+    return SECFailure;
+  }
+
+  memcpy(data, key->data, key->len);
+  return SECSuccess;
+}
+
+SECStatus
+PrivateKey_export(PrivateKey sk, unsigned char* data, unsigned int dataLen)
 {
-  if (pk == NULL)
+  if (sk == NULL || dataLen != CURVE25519_KEY_LEN) {
     return SECFailure;
+  }
 
-  memcpy(data, pk->u.ec.publicValue.data, CURVE25519_KEY_LEN);
+  SECStatus rv = SECSuccess;
+  SECItem item = { siBuffer, NULL, 0 };
+
+  P_CHECKC(PK11_ReadRawAttribute(PK11_TypePrivKey, sk, CKA_VALUE, &item));
+
+  // If the leading bytes of the key are '\0', then this string can be
+  // shorter than `CURVE25519_KEY_LEN` bytes.
+  memset(data, 0, CURVE25519_KEY_LEN);
+  P_CHECKCB(item.len <= CURVE25519_KEY_LEN);
+
+  // Copy into the low-order bytes of the output.
+  const size_t leading_zeros = CURVE25519_KEY_LEN - item.len;
+  memcpy(data + leading_zeros, item.data, item.len);
+
+cleanup:
+  if (item.data != NULL) {
+    SECITEM_ZfreeItem(&item, PR_FALSE);
+  }
+
+  return rv;
+}
+
+static void
+key_to_hex(const unsigned char key_in[CURVE25519_KEY_LEN],
+           unsigned char hex_out[(2 * CURVE25519_KEY_LEN) + 1])
+{
+  const unsigned char* p = key_in;
+  for (unsigned int i = 0; i < CURVE25519_KEY_LEN; i++) {
+    unsigned char bytel = p[0] & 0x0f;
+    unsigned char byteu = (p[0] & 0xf0) >> 4;
+    hex_out[2 * i] = int_to_hex(byteu);
+    hex_out[2 * i + 1] = int_to_hex(bytel);
+    p++;
+  }
+
+  hex_out[2 * CURVE25519_KEY_LEN] = '\0';
+}
+
+static SECStatus
+key_from_hex(unsigned char key_out[CURVE25519_KEY_LEN],
+             const unsigned char hex_in[CURVE25519_KEY_LEN_HEX])
+{
+  for (unsigned int i = 0; i < CURVE25519_KEY_LEN_HEX; i++) {
+    if (!is_hex_digit(hex_in[i]))
+      return SECFailure;
+  }
+
+  const unsigned char* p = hex_in;
+  for (unsigned int i = 0; i < CURVE25519_KEY_LEN; i++) {
+    uint8_t d0 = hex_to_int(p[0]);
+    uint8_t d1 = hex_to_int(p[1]);
+    key_out[i] = (d0 << 4) | d1;
+    p += 2;
+  }
 
   return SECSuccess;
 }
 
 SECStatus
-PublicKey_export_hex(const_PublicKey pk,
-                     unsigned char data[(2 * CURVE25519_KEY_LEN) + 1])
+PublicKey_export_hex(const_PublicKey pk, unsigned char* data,
+                     unsigned int dataLen)
 {
-  unsigned char raw_data[CURVE25519_KEY_LEN];
-  if (PublicKey_export(pk, raw_data) != SECSuccess)
+  if (dataLen != CURVE25519_KEY_LEN_HEX + 1) {
     return SECFailure;
+  }
 
-  const unsigned char* p = raw_data;
-  for (unsigned int i = 0; i < CURVE25519_KEY_LEN; i++) {
-    unsigned char bytel = p[0] & 0x0f;
-    unsigned char byteu = (p[0] & 0xf0) >> 4;
-    data[2 * i] = int_to_hex(byteu);
-    data[2 * i + 1] = int_to_hex(bytel);
-    p++;
+  unsigned char raw_data[CURVE25519_KEY_LEN];
+  if (PublicKey_export(pk, raw_data, sizeof(raw_data)) != SECSuccess) {
+    return SECFailure;
   }
 
-  data[2 * CURVE25519_KEY_LEN] = '\0';
+  key_to_hex(raw_data, data);
+  return SECSuccess;
+}
+
+SECStatus
+PrivateKey_export_hex(PrivateKey sk, unsigned char* data, unsigned int dataLen)
+{
+  if (dataLen != CURVE25519_KEY_LEN_HEX + 1) {
+    return SECFailure;
+  }
+
+  unsigned char raw_data[CURVE25519_KEY_LEN];
+  if (PrivateKey_export(sk, raw_data, sizeof(raw_data)) != SECSuccess) {
+    return SECFailure;
+  }
+
+  key_to_hex(raw_data, data);
   return SECSuccess;
 }
 
 SECStatus
 Keypair_new(PrivateKey* pvtkey, PublicKey* pubkey)
 {
   if (pvtkey == NULL)
     return SECFailure;
@@ -215,21 +394,23 @@ Keypair_new(PrivateKey* pvtkey, PublicKe
   ecp.data[0] = SEC_ASN1_OBJECT_ID;
   ecp.data[1] = oid_data->oid.len;
   memcpy(&ecp.data[2], oid_data->oid.data, oid_data->oid.len);
 
   P_CHECKA(slot = PK11_GetInternalSlot());
   P_CHECKA(*pvtkey = PK11_GenerateKeyPair(slot, CKM_EC_KEY_PAIR_GEN, &ecp,
                                           (SECKEYPublicKey**)pubkey, PR_FALSE,
                                           PR_FALSE, NULL));
-  PK11_FreeSlot(slot);
-
 cleanup:
-  if (ecp.data)
+  if (slot) {
+    PK11_FreeSlot(slot);
+  }
+  if (ecp.data) {
     free(ecp.data);
+  }
   if (rv != SECSuccess) {
     PublicKey_clear(*pubkey);
     PrivateKey_clear(*pvtkey);
   }
   return rv;
 }
 
 void
--- a/third_party/prio/prio/poly.c
+++ b/third_party/prio/prio/poly.c
@@ -69,18 +69,18 @@ fft_interpolate_raw(mp_int* out, const m
 
   P_CHECKA(tmp = MPArray_new(nPoints));
   P_CHECKA(ySub = MPArray_new(nPoints));
   P_CHECKA(rootsSub = MPArray_new(nPoints));
 
   mp_int n_inverse;
   MP_DIGITS(&n_inverse) = NULL;
 
-  MP_CHECK(fft_recurse(out, mod, nPoints, roots, ys, tmp->data, ySub->data,
-                       rootsSub->data));
+  MP_CHECKC(fft_recurse(out, mod, nPoints, roots, ys, tmp->data, ySub->data,
+                        rootsSub->data));
 
   if (invert) {
     MP_CHECKC(mp_init(&n_inverse));
 
     mp_set(&n_inverse, nPoints);
     MP_CHECKC(mp_invmod(&n_inverse, mod, &n_inverse));
     for (int i = 0; i < nPoints; i++) {
       MP_CHECKC(mp_mulmod(&out[i], &n_inverse, mod, &out[i]));
--- a/third_party/prio/prio/prg.c
+++ b/third_party/prio/prio/prg.c
@@ -115,16 +115,42 @@ PRG_get_bytes(PRG prg, unsigned char* by
 
 SECStatus
 PRG_get_int(PRG prg, mp_int* out, const mp_int* max)
 {
   return rand_int_rng(out, max, &PRG_get_bytes_internal, (void*)prg);
 }
 
 SECStatus
+PRG_get_int_range(PRG prg, mp_int* out, const mp_int* lower, const mp_int* max)
+{
+  SECStatus rv;
+  mp_int width;
+  MP_DIGITS(&width) = NULL;
+  MP_CHECKC(mp_init(&width));
+
+  // Compute
+  //    width = max - lower
+  MP_CHECKC(mp_sub(max, lower, &width));
+
+  // Get an integer x in the range [0, width)
+  P_CHECKC(PRG_get_int(prg, out, &width));
+
+  // Set
+  //    out = lower + x
+  // which is in the range [lower, width+lower),
+  // which is              [lower, max).
+  MP_CHECKC(mp_add(lower, out, out));
+
+cleanup:
+  mp_clear(&width);
+  return rv;
+}
+
+SECStatus
 PRG_get_array(PRG prg, MPArray dst, const mp_int* mod)
 {
   SECStatus rv;
   for (int i = 0; i < dst->len; i++) {
     P_CHECK(PRG_get_int(prg, &dst->data[i], mod));
   }
 
   return SECSuccess;
--- a/third_party/prio/prio/prg.h
+++ b/third_party/prio/prio/prg.h
@@ -31,16 +31,23 @@ SECStatus PRG_get_bytes(PRG prg, unsigne
 
 /*
  * Use the PRG output to sample a big integer x in the range
  *    0 <= x < max.
  */
 SECStatus PRG_get_int(PRG prg, mp_int* out, const mp_int* max);
 
 /*
+ * Use the PRG output to sample a big integer x in the range
+ *    lower <= x < max.
+ */
+SECStatus PRG_get_int_range(PRG prg, mp_int* out, const mp_int* lower,
+                            const mp_int* max);
+
+/*
  * Use secret sharing to split the int src into two shares.
  * Use PRG to generate the value `shareB`.
  * The mp_ints must be initialized.
  */
 SECStatus PRG_share_int(PRG prg, mp_int* shareA, const mp_int* src,
                         const_PrioConfig cfg);
 
 /*
--- a/third_party/prio/prio/server.c
+++ b/third_party/prio/prio/server.c
@@ -13,16 +13,23 @@
 
 #include "client.h"
 #include "mparray.h"
 #include "poly.h"
 #include "prg.h"
 #include "server.h"
 #include "util.h"
 
+/* In `PrioTotalShare_final`, we need to be able to store
+ * an `mp_digit` in an `unsigned long long`.
+ */
+#if (MP_DIGIT_MAX > ULLONG_MAX)
+#error "Unsigned long long is not long enough to hold an MP digit"
+#endif
+
 PrioServer
 PrioServer_new(const_PrioConfig cfg, PrioServerId server_idx,
                PrivateKey server_priv, const PrioPRGSeed seed)
 {
   SECStatus rv = SECSuccess;
   PrioServer s = malloc(sizeof(*s));
   if (!s)
     return NULL;
@@ -107,17 +114,17 @@ PrioTotalShare_set_data(PrioTotalShare t
 
   P_CHECK(MPArray_resize(t->data_shares, s->data_shares->len));
   P_CHECK(MPArray_copy(t->data_shares, s->data_shares));
 
   return rv;
 }
 
 SECStatus
-PrioTotalShare_final(const_PrioConfig cfg, unsigned long* output,
+PrioTotalShare_final(const_PrioConfig cfg, unsigned long long* output,
                      const_PrioTotalShare tA, const_PrioTotalShare tB)
 {
   if (tA->data_shares->len != cfg->num_data_fields)
     return SECFailure;
   if (tA->data_shares->len != tB->data_shares->len)
     return SECFailure;
   if (tA->idx != PRIO_SERVER_A || tB->idx != PRIO_SERVER_B)
     return SECFailure;
@@ -127,17 +134,20 @@ PrioTotalShare_final(const_PrioConfig cf
   mp_int tmp;
   MP_DIGITS(&tmp) = NULL;
   MP_CHECKC(mp_init(&tmp));
 
   for (int i = 0; i < cfg->num_data_fields; i++) {
     MP_CHECKC(mp_addmod(&tA->data_shares->data[i], &tB->data_shares->data[i],
                         &cfg->modulus, &tmp));
 
-    output[i] = tmp.dp[0];
+    if (MP_USED(&tmp) > 1) {
+      P_CHECKCB(false);
+    }
+    output[i] = MP_DIGIT(&tmp, 0);
   }
 
 cleanup:
   mp_clear(&tmp);
   return rv;
 }
 
 inline static mp_int*
@@ -173,29 +183,35 @@ get_h_share(const_PrioVerifier v, int i)
  */
 static SECStatus
 compute_shares(PrioVerifier v, const_PrioPacketClient p)
 {
   SECStatus rv;
   const int n = v->s->cfg->num_data_fields + 1;
   const int N = next_power_of_two(n);
   mp_int eval_at;
+  mp_int lower;
   MP_DIGITS(&eval_at) = NULL;
+  MP_DIGITS(&lower) = NULL;
 
   MPArray points_f = NULL;
   MPArray points_g = NULL;
   MPArray points_h = NULL;
 
   MP_CHECKC(mp_init(&eval_at));
+  MP_CHECKC(mp_init(&lower));
   P_CHECKA(points_f = MPArray_new(N));
   P_CHECKA(points_g = MPArray_new(N));
   P_CHECKA(points_h = MPArray_new(2 * N));
 
-  // Use PRG to generate random point
-  MP_CHECKC(PRG_get_int(v->s->prg, &eval_at, &v->s->cfg->modulus));
+  // Use PRG to generate random point. Per Appendix D.2 of full version of
+  // Prio paper, this value must be in the range
+  //      [n+1, modulus).
+  mp_set(&lower, n + 1);
+  P_CHECKC(PRG_get_int_range(v->s->prg, &eval_at, &lower, &v->s->cfg->modulus));
 
   // Reduce value into the field we're using. This
   // doesn't yield exactly a uniformly random point,
   // but for values this large, it will be close
   // enough.
   MP_CHECKC(mp_mod(&eval_at, &v->s->cfg->modulus, &eval_at));
 
   // Client sends us the values of f(0) and g(0)
@@ -228,16 +244,17 @@ compute_shares(PrioVerifier v, const_Pri
   P_CHECKC(poly_interp_evaluate(&v->share_gR, points_g, &eval_at, v->s->cfg));
   P_CHECKC(poly_interp_evaluate(&v->share_hR, points_h, &eval_at, v->s->cfg));
 
 cleanup:
   MPArray_clear(points_f);
   MPArray_clear(points_g);
   MPArray_clear(points_h);
   mp_clear(&eval_at);
+  mp_clear(&lower);
   return rv;
 }
 
 PrioVerifier
 PrioVerifier_new(PrioServer s)
 {
   SECStatus rv = SECSuccess;
   PrioVerifier v = malloc(sizeof *v);
--- a/third_party/prio/update.sh
+++ b/third_party/prio/update.sh
@@ -1,16 +1,16 @@
 #!/bin/sh
 
 # Script to update the mozilla in-tree copy of the libprio library.
 # Run this within the /third_party/libprio directory of the source tree.
 
 MY_TEMP_DIR=`mktemp -d -t libprio_update.XXXXXX` || exit 1
 
-COMMIT="02a81fb652d385d0f4f10989d051317097ab55fb"
+COMMIT="a95cfdd5eaf7104582709c54ef23395d24d7f7fd"
 
 git clone -n https://github.com/mozilla/libprio ${MY_TEMP_DIR}/libprio
 git -C ${MY_TEMP_DIR}/libprio checkout ${COMMIT}
 
 FILES="include prio"
 VERSION=$(git -C ${MY_TEMP_DIR}/libprio describe --tags)
 perl -p -i -e "s/Current version: \S+ \[commit [0-9a-f]{40}\]/Current version: ${VERSION} [commit ${COMMIT}]/" README-mozilla