private boolean handleGetOfferedKey(Message m, PeerNode source) {
    Key key = (Key) m.getObject(DMT.KEY);
    byte[] authenticator = ((ShortBuffer) m.getObject(DMT.OFFER_AUTHENTICATOR)).getData();
    long uid = m.getLong(DMT.UID);
    if (!HMAC.verifyWithSHA256(
        node.failureTable.offerAuthenticatorKey, key.getFullKey(), authenticator)) {
      Logger.error(
          this, "Invalid offer request from " + source + " : authenticator did not verify");
      try {
        source.sendAsync(
            DMT.createFNPGetOfferedKeyInvalid(uid, DMT.GET_OFFERED_KEY_REJECTED_BAD_AUTHENTICATOR),
            null,
            node.failureTable.senderCounter);
      } catch (NotConnectedException e) {
        // Too bad.
      }
      return true;
    }
    if (logMINOR) Logger.minor(this, "Valid GetOfferedKey for " + key + " from " + source);

    // Do we want it? We can RejectOverload if we don't have the bandwidth...
    boolean isSSK = key instanceof NodeSSK;
    OfferReplyTag tag = new OfferReplyTag(isSSK);
    node.lockUID(uid, isSSK, false, true, false, tag);
    boolean needPubKey;
    try {
      needPubKey = m.getBoolean(DMT.NEED_PUB_KEY);
      String reject = nodeStats.shouldRejectRequest(true, false, isSSK, false, true, source, false);
      if (reject != null) {
        Logger.normal(
            this, "Rejecting FNPGetOfferedKey from " + source + " for " + key + " : " + reject);
        Message rejected = DMT.createFNPRejectedOverload(uid, true);
        try {
          source.sendAsync(rejected, null, node.failureTable.senderCounter);
        } catch (NotConnectedException e) {
          Logger.normal(
              this, "Rejecting (overload) data request from " + source.getPeer() + ": " + e);
        }
        node.unlockUID(uid, isSSK, false, false, true, false, tag);
        return true;
      }

    } catch (Error e) {
      node.unlockUID(uid, isSSK, false, false, true, false, tag);
      throw e;
    } catch (RuntimeException e) {
      node.unlockUID(uid, isSSK, false, false, true, false, tag);
      throw e;
    } // Otherwise, sendOfferedKey is responsible for unlocking.

    // Accept it.

    try {
      node.failureTable.sendOfferedKey(key, isSSK, needPubKey, uid, source, tag);
    } catch (NotConnectedException e) {
      // Too bad.
    }
    return true;
  }
 /**
  * Key Derivation Function for client.dat: Use the database key as an HMAC key to an HMAC of the
  * key plus some constant plus the storeIdentifier.
  *
  * @return An encryption key, as byte[].
  */
 public byte[] getKeyForClientLayer() {
   byte[] full = new byte[databaseKey.length + CLIENT_LAYER.length];
   int x = 0;
   System.arraycopy(databaseKey, 0, full, 0, databaseKey.length);
   x += databaseKey.length;
   System.arraycopy(CLIENT_LAYER, 0, full, x, CLIENT_LAYER.length);
   return HMAC.macWithSHA256(databaseKey, full);
 }
 /**
  * Key Derivation Function for plugin stores: Use the database key as an HMAC key to an HMAC of
  * the key plus some constant plus the storeIdentifier.
  *
  * @param storeIdentifier The classname of the plugin, used as part of a filename.
  * @return An encryption key, as byte[].
  */
 public byte[] getPluginStoreKey(String storeIdentifier) {
   try {
     byte[] id = storeIdentifier.getBytes("UTF-8");
     byte[] full = new byte[databaseKey.length + PLUGIN.length + id.length];
     int x = 0;
     System.arraycopy(databaseKey, 0, full, 0, databaseKey.length);
     x += databaseKey.length;
     System.arraycopy(PLUGIN, 0, full, x, PLUGIN.length);
     x += PLUGIN.length;
     System.arraycopy(id, 0, full, x, id.length);
     return HMAC.macWithSHA256(databaseKey, full);
   } catch (UnsupportedEncodingException e) {
     throw new Error(e);
   }
 }