docs: function generatePreambleAndBeacons (publicKeysToNotify,
                                       ecdhForLocalDevice,
                                       millisecondsUntilExpiration) {
    if (publicKeysToNotify == null) {
      throw new Error('publicKeysToNotify cannot be null');
    }
  
    if (ecdhForLocalDevice == null) {
      throw new Error('ecdhForLocalDevice cannot be null');
    }
  
    if (millisecondsUntilExpiration <= 0 ||
        millisecondsUntilExpiration > module.exports.ONE_DAY) {
      throw new Error('millisecondsUntilExpiration must be > 0 & < ' +
        module.exports.ONE_DAY);
    }
  
    if (publicKeysToNotify.length === 0) { return null; }
  
    var beacons = [];
  
    var ke = crypto.createECDH(thaliConfig.BEACON_CURVE);
  
    // Generate preamble
    var pubKe = ke.generateKeys();
    // We fuzz the expiration by adding on a random number that fits in
    // an unsigned 8 bit integer.
    var expiration = Date.now() + millisecondsUntilExpiration +
      crypto.randomBytes(1).readUInt8(0);
    var expirationLong = Long.fromNumber(expiration);
    var expirationBuffer = new Buffer(module.exports.LONG_SIZE);
    expirationBuffer.writeInt32BE(expirationLong.high, 0);
    expirationBuffer.writeInt32BE(expirationLong.low, 4);
  
    beacons.push(Buffer.concat([pubKe, expirationBuffer]));
  
    // UnencryptedKeyId = SHA256(Kx.public().encode()).first(16)
    var unencryptedKeyId =
      createPublicKeyHash(ecdhForLocalDevice.getPublicKey());
  
    publicKeysToNotify.forEach(function (pubKy) {
      // Sxy = ECDH(Kx.private(), PubKy)
      var sxy = ecdhForLocalDevice.computeSecret(pubKy);
  
      // HKxy = HKDF(SHA256, Sxy, Expiration, 32)
      var hkxy = HKDF(module.exports.SHA256, sxy, expirationBuffer)
        .derive('', module.exports.SHA256_HMAC_KEY_SIZE);
  
      // BeaconHmac = HMAC(SHA256, HKxy, Expiration).first(16)
      var beaconHmac = crypto.createHmac(module.exports.SHA256, hkxy)
        .update(expirationBuffer)
        .digest()
        .slice(0, module.exports.TRUNCATED_HASH_SIZE);
  
      // Sey = ECDH(Ke.private(), PubKy)
      var sey = ke.computeSecret(pubKy);
  
      // hkey = HKDF(SHA256, Sey, Expiration, 16)
      var hkey = HKDF(module.exports.SHA256, sey, expirationBuffer)
        .derive('', module.exports.AES_128_KEY_SIZE);
  
      // beacons.append(AESEncrypt(GCM, HKey, 0, 128, UnencryptedKeyId) +
      // BeaconHmac)
      var aes = crypto.createCipher(module.exports.GCM, hkey);
  
      beacons.push(Buffer.concat([
        Buffer.concat([aes.update(unencryptedKeyId), aes.final()]),
        beaconHmac
      ]));
    });
  
    return Buffer.concat(beacons);
  }