net.named_data.jndn.encrypt.Schedule Maven / Gradle / Ivy
/**
* Copyright (C) 2015-2019 Regents of the University of California.
* @author: Jeff Thompson
* @author: From ndn-group-encrypt src/schedule https://github.com/named-data/ndn-group-encrypt
*
* 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.encrypt;
import java.nio.ByteBuffer;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.TimeZone;
import java.util.HashSet;
import net.named_data.jndn.encoding.EncodingException;
import net.named_data.jndn.encoding.tlv.Tlv;
import net.named_data.jndn.encoding.tlv.TlvDecoder;
import net.named_data.jndn.encoding.tlv.TlvEncoder;
import net.named_data.jndn.util.Blob;
import net.named_data.jndn.util.Common;
/**
* Schedule is used to manage the times when a member can access data using two
* sets of RepetitiveInterval as follows. whiteIntervalList is an ordered
* set for the times a member is allowed to access to data, and
* blackIntervalList is for the times a member is not allowed.
* @note This class is an experimental feature. The API may change.
*/
public class Schedule {
public static class Result {
public Result(boolean isPositive, Interval interval)
{
this.isPositive = isPositive;
this.interval = interval;
}
public boolean isPositive;
public Interval interval;
}
/**
* Create a Schedule with empty whiteIntervalList and blackIntervalList.
*/
public Schedule()
{
}
/**
* Create a Schedule, copying values from the given schedule.
* @param schedule The Schedule to copy values from.
*/
public Schedule(Schedule schedule)
{
// RepetitiveInterval is immutable, so we don't need to make a deep copy.
whiteIntervalList_.addAll(schedule.whiteIntervalList_);
blackIntervalList_.addAll(schedule.blackIntervalList_);
}
/**
* Add the repetitiveInterval to the whiteIntervalList.
* @param repetitiveInterval The RepetitiveInterval to add. If the list
* already contains the same RepetitiveInterval, this does nothing.
* @return This Schedule so you can chain calls to add.
*/
public final Schedule
addWhiteInterval(RepetitiveInterval repetitiveInterval)
{
// RepetitiveInterval is immutable, so we don't need to make a copy.
whiteIntervalList_.add(repetitiveInterval);
return this;
}
/**
* Add the repetitiveInterval to the blackIntervalList.
* @param repetitiveInterval The RepetitiveInterval to add. If the list
* already contains the same RepetitiveInterval, this does nothing.
* @return This Schedule so you can chain calls to add.
*/
public final Schedule
addBlackInterval(RepetitiveInterval repetitiveInterval)
{
// RepetitiveInterval is immutable, so we don't need to make a copy.
blackIntervalList_.add(repetitiveInterval);
return this;
}
/**
* Get the interval that covers the time stamp. This iterates over the two
* repetitive interval sets and find the shortest interval that allows a group
* member to access the data. If there is no interval covering the time stamp,
* this returns false for isPositive and returns a negative interval.
* @param timeStamp The time stamp as milliseconds since Jan 1, 1970 UTC.
* @return An object with fields (isPositive, interval) where isPositive is
* true if the returned interval is positive or false if negative, and
* interval is the Interval covering the time stamp, or a negative interval if
* not found.
*/
public final Result
getCoveringInterval(double timeStamp)
{
Interval blackPositiveResult = new Interval(true);
Interval whitePositiveResult = new Interval(true);
Interval blackNegativeResult = new Interval();
Interval whiteNegativeResult = new Interval();
// Get the black result.
calculateIntervalResult
(blackIntervalList_, timeStamp, blackPositiveResult, blackNegativeResult);
// If the black positive result is not empty, then isPositive must be false.
if (!blackPositiveResult.isEmpty())
return new Result(false, blackPositiveResult);
// Get the whiteResult.
calculateIntervalResult
(whiteIntervalList_, timeStamp, whitePositiveResult, whiteNegativeResult);
if (whitePositiveResult.isEmpty() && !whiteNegativeResult.isValid()) {
// There is no white interval covering the time stamp.
// Return false and a 24-hour interval.
double timeStampDateOnly =
RepetitiveInterval.toDateOnlyMilliseconds(timeStamp);
return new Result
(false, new Interval
(timeStampDateOnly, timeStampDateOnly + MILLISECONDS_IN_DAY));
}
if (!whitePositiveResult.isEmpty()) {
// There is white interval covering the time stamp.
// Return true and calculate the intersection.
if (blackNegativeResult.isValid())
return new Result
(true, whitePositiveResult.intersectWith(blackNegativeResult));
else
return new Result(true, whitePositiveResult);
}
else
// There is no white interval covering the time stamp.
// Return false.
return new Result(false, whiteNegativeResult);
}
/**
* Encode this Schedule.
* @return The encoded buffer.
*/
public final Blob
wireEncode()
{
// For now, don't use WireFormat and hardcode to use TLV since the encoding
// doesn't go out over the wire, only into the local SQL database.
TlvEncoder encoder = new TlvEncoder(256);
int saveLength = encoder.getLength();
// Encode backwards.
// Encode the blackIntervalList.
int saveLengthForList = encoder.getLength();
Object[] array = blackIntervalList_.toArray();
Arrays.sort(array);
for (int i = array.length - 1; i >= 0; --i) {
RepetitiveInterval element = (RepetitiveInterval)array[i];
encodeRepetitiveInterval(element, encoder);
}
encoder.writeTypeAndLength
(Tlv.Encrypt_BlackIntervalList, encoder.getLength() - saveLengthForList);
// Encode the whiteIntervalList.
saveLengthForList = encoder.getLength();
array = whiteIntervalList_.toArray();
Arrays.sort(array);
for (int i = array.length - 1; i >= 0; --i) {
RepetitiveInterval element = (RepetitiveInterval)array[i];
encodeRepetitiveInterval(element, encoder);
}
encoder.writeTypeAndLength
(Tlv.Encrypt_WhiteIntervalList, encoder.getLength() - saveLengthForList);
encoder.writeTypeAndLength
(Tlv.Encrypt_Schedule, encoder.getLength() - saveLength);
return new Blob(encoder.getOutput(), false);
}
/**
* Decode the input and update this Schedule object.
* @param input The input buffer to decode. This reads from position() to
* limit(), but does not change the position.
* @throws EncodingException For invalid encoding.
*/
public final void
wireDecode(ByteBuffer input) throws EncodingException
{
// For now, don't use WireFormat and hardcode to use TLV since the encoding
// doesn't go out over the wire, only into the local SQL database.
TlvDecoder decoder = new TlvDecoder(input);
int endOffset = decoder.readNestedTlvsStart(Tlv.Encrypt_Schedule);
// Decode the whiteIntervalList.
whiteIntervalList_.clear();
int listEndOffset = decoder.readNestedTlvsStart(Tlv.Encrypt_WhiteIntervalList);
while (decoder.getOffset() < listEndOffset)
whiteIntervalList_.add(decodeRepetitiveInterval(decoder));
decoder.finishNestedTlvs(listEndOffset);
// Decode the blackIntervalList.
blackIntervalList_.clear();
listEndOffset = decoder.readNestedTlvsStart(Tlv.Encrypt_BlackIntervalList);
while (decoder.getOffset() < listEndOffset)
blackIntervalList_.add(decodeRepetitiveInterval(decoder));
decoder.finishNestedTlvs(listEndOffset);
decoder.finishNestedTlvs(endOffset);
}
/**
* Decode the input and update this Schedule object.
* @param input The input buffer to decode. This reads from position() to
* limit(), but does not change the position.
* @throws EncodingException For invalid encoding.
*/
public final void
wireDecode(Blob input) throws EncodingException
{
wireDecode(input.buf());
}
/**
* Encode the RepetitiveInterval as NDN-TLV to the encoder.
* @param repetitiveInterval The RepetitiveInterval to encode.
* @param encoder The TlvEncoder to receive the encoding.
*/
private static void
encodeRepetitiveInterval
(RepetitiveInterval repetitiveInterval, TlvEncoder encoder)
{
int saveLength = encoder.getLength();
// Encode backwards.
encoder.writeNonNegativeIntegerTlv
(Tlv.Encrypt_RepeatUnit,
RepetitiveInterval.getRepeatUnitNumericType(repetitiveInterval.getRepeatUnit()));
encoder.writeNonNegativeIntegerTlv
(Tlv.Encrypt_NRepeats, repetitiveInterval.getNRepeats());
encoder.writeNonNegativeIntegerTlv
(Tlv.Encrypt_IntervalEndHour, repetitiveInterval.getIntervalEndHour());
encoder.writeNonNegativeIntegerTlv
(Tlv.Encrypt_IntervalStartHour, repetitiveInterval.getIntervalStartHour());
// Use Blob to convert the string to UTF8 encoding.
encoder.writeBlobTlv(Tlv.Encrypt_EndDate,
new Blob(toIsoString(repetitiveInterval.getEndDate())).buf());
encoder.writeBlobTlv(Tlv.Encrypt_StartDate,
new Blob(toIsoString(repetitiveInterval.getStartDate())).buf());
encoder.writeTypeAndLength
(Tlv.Encrypt_RepetitiveInterval, encoder.getLength() - saveLength);
}
/**
* Decode the input as an NDN-TLV RepetitiveInterval.
* @param decoder The decoder with the input to decode.
* @return A new RepetitiveInterval with the decoded result.
*/
private static RepetitiveInterval
decodeRepetitiveInterval(TlvDecoder decoder) throws EncodingException
{
int endOffset = decoder.readNestedTlvsStart(Tlv.Encrypt_RepetitiveInterval);
// Use Blob to convert UTF8 to a string.
double startDate = fromIsoString
(new Blob(decoder.readBlobTlv(Tlv.Encrypt_StartDate), true).toString());
double endDate = fromIsoString
(new Blob(decoder.readBlobTlv(Tlv.Encrypt_EndDate), true).toString());
int startHour = (int)decoder.readNonNegativeIntegerTlv
(Tlv.Encrypt_IntervalStartHour);
int endHour = (int)decoder.readNonNegativeIntegerTlv
(Tlv.Encrypt_IntervalEndHour);
int nRepeats = (int)decoder.readNonNegativeIntegerTlv(Tlv.Encrypt_NRepeats);
int repeatUnitCode = (int)decoder.readNonNegativeIntegerTlv
(Tlv.Encrypt_RepeatUnit);
RepetitiveInterval.RepeatUnit repeatUnit;
if (repeatUnitCode == Tlv.Encrypt_RepeatUnit_NONE)
repeatUnit = RepetitiveInterval.RepeatUnit.NONE;
else if (repeatUnitCode == Tlv.Encrypt_RepeatUnit_DAY)
repeatUnit = RepetitiveInterval.RepeatUnit.DAY;
else if (repeatUnitCode == Tlv.Encrypt_RepeatUnit_MONTH)
repeatUnit = RepetitiveInterval.RepeatUnit.MONTH;
else if (repeatUnitCode == Tlv.Encrypt_RepeatUnit_YEAR)
repeatUnit = RepetitiveInterval.RepeatUnit.YEAR;
else
throw new EncodingException
("Unrecognized RepetitiveInterval RepeatUnit code: " + repeatUnitCode);
decoder.finishNestedTlvs(endOffset);
return new RepetitiveInterval
(startDate, endDate, startHour, endHour, nRepeats, repeatUnit);
}
/**
* A helper function to calculate black interval results or white interval
* results.
* @param list The set of RepetitiveInterval, which can be the white list or
* the black list.
* @param timeStamp The time stamp as milliseconds since Jan 1, 1970 UTC.
* @param positiveResult The positive result which is updated.
* @param negativeResult The negative result which is updated.
*/
private static void
calculateIntervalResult
(HashSet list, double timeStamp, Interval positiveResult,
Interval negativeResult)
{
Object[] array = list.toArray();
Arrays.sort(array);
for (Object elementObj : array) {
RepetitiveInterval element = (RepetitiveInterval)elementObj;
RepetitiveInterval.Result result = element.getInterval(timeStamp);
Interval tempInterval = result.interval;
if (result.isPositive == true) {
try {
positiveResult.unionWith(tempInterval);
} catch (Interval.Error ex) {
// We don't expect to get this error.
throw new Error("Error in Interval.unionWith: " + ex.getMessage());
}
}
else {
if (!negativeResult.isValid())
negativeResult.set(tempInterval);
else
negativeResult.intersectWith(tempInterval);
}
}
}
public static double
fromIsoString(String dateString) throws EncodingException
{
try {
return (double)Common.dateToMillisecondsSince1970
(dateFormat.parse(dateString));
} catch (ParseException ex) {
throw new EncodingException("Cannot parse date string " + dateString);
}
}
public static String
toIsoString(double msSince1970)
{
return dateFormat.format
(Common.millisecondsSince1970ToDate((long)Math.round(msSince1970)));
}
private static SimpleDateFormat
getDateFormat()
{
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
return dateFormat;
}
private final HashSet whiteIntervalList_ = new HashSet();
private final HashSet blackIntervalList_ = new HashSet();
private static final SimpleDateFormat dateFormat = getDateFormat();
private static final long MILLISECONDS_IN_DAY = 24 * 3600 * 1000;
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy