All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.named_data.jndn.security.v2.CertificateCacheV2 Maven / Gradle / Ivy

/**
 * Copyright (C) 2017-2018 Regents of the University of California.
 * @author: Jeff Thompson 
 * @author: From ndn-cxx security https://github.com/named-data/ndn-cxx/blob/master/src/security/v2/certificate-cache.cpp
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see .
 * A copy of the GNU Lesser General Public License is in the file COPYING.
 */

package net.named_data.jndn.security.v2;

import java.util.ArrayList;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.named_data.jndn.Interest;
import net.named_data.jndn.Name;
import net.named_data.jndn.encoding.EncodingException;
import net.named_data.jndn.encrypt.Schedule;
import net.named_data.jndn.util.Common;

/**
 * A CertificateCacheV2 holds other user's verified certificates in security v2
 * format CertificateV2. A certificate is removed no later than its NotAfter
 * time, or maxLifetime after it has been added to the cache.
 */
public class CertificateCacheV2 {
  /**
   * Create a CertificateCacheV2.
   * @param maxLifetimeMilliseconds The maximum time that certificates can live
   * inside the cache, in milliseconds.
   */
  public CertificateCacheV2(double maxLifetimeMilliseconds)
  {
    maxLifetimeMilliseconds_ = maxLifetimeMilliseconds;
  }

  /**
   * Create a CertificateCacheV2. Set the maximum time that certificates can
   * live inside the cache to getDefaultLifetime().
   */
  public CertificateCacheV2()
  {
    maxLifetimeMilliseconds_ = getDefaultLifetime();
  }

  /**
   * Insert the certificate into the cache. The inserted certificate will be
   * removed no later than its NotAfter time, or maxLifetimeMilliseconds given
   * to the constructor.
   * @param certificate The certificate object, which is copied.
   */
  public final void
  insert(CertificateV2 certificate) throws CertificateV2.Error
  {
    double notAfterTime = certificate.getValidityPeriod().getNotAfter();
    // nowOffsetMilliseconds_ is only used for testing.
    double now = Common.getNowMilliseconds() + nowOffsetMilliseconds_;
    if (notAfterTime < now) {
      logger_.log(Level.FINE, "Not adding {0}: already expired at {1}",
        new Object[] {certificate.getName().toUri(),
                      Schedule.toIsoString(notAfterTime)});
      return;
    }

    double removalTime =
      Math.min(notAfterTime, now + maxLifetimeMilliseconds_);
    if (removalTime < nextRefreshTime_)
      // We need to run refresh() sooner.)
      nextRefreshTime_ = removalTime;

    double removalHours = (removalTime - now) / (3600 * 1000.0);
    logger_.log(Level.FINE, "Adding {0}, will remove in {1} hours",
      new Object[] {certificate.getName().toUri(), removalHours});
    CertificateV2 certificateCopy = new CertificateV2(certificate);
    certificatesByName_.put
      (certificateCopy.getName(), new Entry(certificateCopy, removalTime));
  }

  /**
   * Find the certificate by the given key name.
   * @param certificatePrefix The certificate prefix for searching for the
   * certificate.
   * @return The found certificate, or null if not found. You must not modify
   * the returned object. If you need to modify it, then make a copy.
   */
  public final CertificateV2
  find(Name certificatePrefix)
  {
    if (certificatePrefix.size() > 0 &&
        certificatePrefix.get(-1).isImplicitSha256Digest())
      logger_.log(Level.FINE,
        "Certificate search using a name with an implicit digest is not yet supported");

    refresh();

    Name entryKey = (Name)certificatesByName_.ceilingKey(certificatePrefix);
    if (entryKey == null)
      return null;

    CertificateV2 certificate =
      ((Entry)certificatesByName_.get(entryKey)).certificate_;
    if (!certificatePrefix.isPrefixOf(certificate.getName()))
      return null;
    return certificate;
  }

  /**
   * Find the certificate by the given interest.
   * @param interest The input interest object.
   * @return The found certificate which matches the interest, or null if not
   * found. You must not modify the returned object. If you need to modify it,
   * then make a copy.
   * @note ChildSelector is not supported.
   */
  public final CertificateV2
  find(Interest interest)
  {
    if (interest.getChildSelector() >= 0)
      logger_.log(Level.FINE,
        "Certificate search using a ChildSelector is not supported. Searching as if this selector not specified");

    if (interest.getName().size() > 0 &&
        interest.getName().get(-1).isImplicitSha256Digest())
      logger_.log(Level.FINE,
        "Certificate search using a name with an implicit digest is not yet supported");

    refresh();

    Name firstKey = (Name)certificatesByName_.ceilingKey(interest.getName());
    if (firstKey == null)
      return null;

    for (Object key : certificatesByName_.navigableKeySet().tailSet(firstKey)) {
      CertificateV2 certificate = ((Entry)certificatesByName_.get
        ((Name)key)).certificate_;
      if (!interest.getName().isPrefixOf(certificate.getName()))
        break;

      try {
        if (interest.matchesData(certificate))
          return certificate;
      } catch (EncodingException ex) {
        // We don't expect this. Promote to Error.
        throw new Error("Error in Interest.matchesData: " + ex);
      }
    }

    return null;
  }

  /**
   * Remove the certificate whose name equals the given name. If no such
   * certificate is in the cache, do nothing.
   * @param certificateName The name of the certificate.
   */
  public final void
  deleteCertificate(Name certificateName)
  {
    certificatesByName_.remove(certificateName);
    // This may be the certificate to be removed at nextRefreshTime_ by refresh(),
    // but just allow refresh() to run instead of update nextRefreshTime_ now.
  }

  /**
   * Clear all certificates from the cache.
   */
  public final void
  clear()
  {
    certificatesByName_.clear();
    nextRefreshTime_ = Double.MAX_VALUE;
  }

  /**
   * Get the default maximum lifetime (1 hour).
   * @return The lifetime in milliseconds.
   */
  public static double
  getDefaultLifetime() { return 3600.0 * 1000; }

  /**
   * Set the offset when insert() and refresh() get the current time, which
   * should only be used for testing.
   * @param nowOffsetMilliseconds The offset in milliseconds.
   */
  public final void
  setNowOffsetMilliseconds_(double nowOffsetMilliseconds)
  {
    nowOffsetMilliseconds_ = nowOffsetMilliseconds;
  }

  /**
   * CertificateCacheV2.Entry is the value of the certificatesByName_ map.
   */
  private static class Entry {
    /**
     * Create a new CertificateCacheV2.Entry with the given values.
     * @param certificate The certificate.
     * @param removalTime The removal time for this entry  as milliseconds since
     * Jan 1, 1970 UTC.
     */
    public Entry(CertificateV2 certificate, double removalTime)
    {
      certificate_ = certificate;
      removalTime_ = removalTime;
    }

    public final CertificateV2 certificate_;
    public final double removalTime_;
  };

  /**
   * Remove all outdated certificate entries.
   */
  private void
  refresh()
  {
    // nowOffsetMilliseconds_ is only used for testing.
    double now = Common.getNowMilliseconds() + nowOffsetMilliseconds_;
    if (now < nextRefreshTime_)
      return;

    // We recompute nextRefreshTime_.
    double nextRefreshTime = Double.MAX_VALUE;
    // Keep a separate list of entries to erase since we can't erase while iterating.
    ArrayList namesToErase = new ArrayList();
    for (Object key : certificatesByName_.keySet()) {
      Name name = (Name)key;
      Entry entry = (Entry)certificatesByName_.get(name);

      if (entry.removalTime_ <= now)
        namesToErase.add(name);
      else
        nextRefreshTime = Math.min(nextRefreshTime, entry.removalTime_);
    }

    nextRefreshTime_ = nextRefreshTime;
    // Now actually erase.
    for (int i = 0; i < namesToErase.size(); ++i)
      certificatesByName_.remove(namesToErase.get(i));
  }

  // Name => CertificateCacheV2.Entry..
  private final TreeMap certificatesByName_ = new TreeMap();
  private double nextRefreshTime_ = Double.MAX_VALUE;
  private final double maxLifetimeMilliseconds_;
  private static final Logger logger_ =
    Logger.getLogger(CertificateCacheV2.class.getName());
  private double nowOffsetMilliseconds_ = 0;

  // This is to force an import of net.named_data.jndn.util.
  private static Common dummyCommon_ = new Common();
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy