Bug 1518728 - update vendored libprio to 1.4 r=glandium
authorRobert Helmer <rhelmer@mozilla.com>
Mon, 14 Jan 2019 23:50:08 +0000
changeset 514042 cee164c51ba7d1d2d4c4395184fbc70eb45ba675
parent 514041 1c686d17264b9565aa3b67d2aca0034190a86f3a
child 514043 e56cc5e7b57a5d18ab72207f7f246a9b8c610c1c
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersglandium
bugs1518728
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1518728 - update vendored libprio to 1.4 r=glandium 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