/*
* Part of the CCNx Java Library.
*
* Copyright (C) 2008, 2009, 2013 Palo Alto Research Center, Inc.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation.
* This library 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 library;
* if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
* Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.ccnx.ccn.impl.security.crypto;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import org.ccnx.ccn.impl.security.crypto.util.CryptoConstants;
import org.ccnx.ccn.impl.support.DataUtils;
import org.ccnx.ccn.impl.support.Log;
/**
* Specifies encryption algorithm, keys and if necessary IV to use for encrypting
* or decrypting content.
*
* The segmenter will be called with parameters identifying:
*
* * the encryption algorithm and mode to use, if any
* * the encryption key to use for this particular data item o (the object to be segmented)
* * an 8-byte value used as an IV seed for this item (CBC mode) or a random counter
* component (CTR) (derived in KeyDerivation)
* * the desired full segment (packet) length, including supporting data
*
* In CTR mode, the counter for a given block B (number Bnum) in segment Snum will be constructed as follows:
*
* CTR = IVseed || Snum || Bnum
*
* where the segment and block numbers is represented in unsigned, 1-based big endian format.
* The total width of the counter value is 16 bytes, where the first 8 bytes are the IV seed
* value, the next 6 bytes are the segment number, and the last 2 bytes are the block number.
* A single-segment object following the SegmentationProfile? will still have a segment number
* component in its name, and will follow the specification above for managing its encryption keys.
*
* In CBC mode, the input IV will be used as a seed to generate an IV for each segment S as follows:
*
* IV = Eko (IVseed || Snum || B0)
*
* Where the segment number is encoded in 1-based, unsigned, big-endian form, and
* represented in the B-L rightmost bytes of the plaintext above, where B is the width of
* the block cipher in use, and L is the length of the numeric representation of the
* segment number. B0 = 1 to maintain consistency with standard CTR mode use. The same IV
* expansion function is used regardless of mode for simplicity.
* The encryption is done with the specified key, in CBC mode, using the all-zeros IV
*
* IMPORTANT NOTE: Do not use static keying to encrypt network objects in CTR mode, unless
* you are careful to only save them once per key. Use CBC mode (under development) or
* a dynamic keying method, such as KDFContentKeys.
*/
public class EncryptedIVStaticContentKeys extends StaticContentKeys implements Cloneable {
/**
* EncryptedIVStaticContentKeys constructor.
* @param encryptionAlgorithm (e.g. AES/CTR/NoPadding) the encryption algorithm to use.
* First component of algorithm should be the algorithm associated with the key.
* @param key key material to be used
* @param ivctr iv or counter material to be used with specified algorithm
* @throws NoSuchPaddingException
* @throws NoSuchAlgorithmException
*/
public EncryptedIVStaticContentKeys(String encryptionAlgorithm, byte [] key, byte [] ivctr)
throws NoSuchAlgorithmException, NoSuchPaddingException {
super(encryptionAlgorithm, key, ivctr);
}
/**
* Create a EncryptedIVStaticContentKeys with the default algorithm.
* @throws NoSuchPaddingException
* @throws NoSuchAlgorithmException
*/
public EncryptedIVStaticContentKeys(byte [] key, byte [] ivctr) throws NoSuchAlgorithmException, NoSuchPaddingException {
super(null, key, ivctr);
}
/**
* EncryptedIVStaticContentKeys constructor.
*/
public EncryptedIVStaticContentKeys(String encryptionAlgorithm, Key key, byte [] ivCtr) throws NoSuchAlgorithmException, NoSuchPaddingException {
super(encryptionAlgorithm, key, ivCtr);
}
public EncryptedIVStaticContentKeys(ContentKeys other) {
super(other);
}
/**
* Create a set of random encryption/decryption keys using the default algorithm.
* @return a randomly-generated set of keys and IV that can be used for encryption
* @throws NoSuchPaddingException
* @throws NoSuchAlgorithmException
*/
public static synchronized ContentKeys generateRandomKeys() throws NoSuchAlgorithmException, NoSuchPaddingException {
return new EncryptedIVStaticContentKeys(StaticContentKeys.generateRandomKeys());
}
public EncryptedIVStaticContentKeys clone() {
return (EncryptedIVStaticContentKeys)super.clone();
}
public IvParameterSpec buildIVCtr(KeyAndIV keyAndIV, long segmentNumber, int ivCtrLen) throws InvalidKeyException, InvalidAlgorithmParameterException {
if (_encryptionAlgorithm.contains(CryptoConstants.CTR_MODE)) {
return super.buildIVCtr(keyAndIV, segmentNumber, ivCtrLen);
} else {
return buildEncryptedIV(keyAndIV, segmentNumber, ivCtrLen);
}
}
/**
* Turn a master IV and a segment number into an IV for this segment
* (used in CBC mode).
* TODO check use of input and output lengths
* @param masterIV the master IV
* @param segmentNumber the segmeont number
* @param ivLen the output IV length requested
* @return the IV
* @throws InvalidAlgorithmParameterException
* @throws InvalidKeyException
* @throws BadPaddingException
* @throws IllegalBlockSizeException
*/
public IvParameterSpec buildEncryptedIV(KeyAndIV keyAndIV, long segmentNumber, int ivLen) throws InvalidKeyException, InvalidAlgorithmParameterException {
Log.finest("Thread="+Thread.currentThread()+" Building CTR - master="+DataUtils.printHexBytes(keyAndIV.getIV())+" segment="+segmentNumber+" ivLen="+ivLen);
Cipher cipher = getCipher();
IvParameterSpec zeroIv = new IvParameterSpec(new byte[cipher.getBlockSize()]);
cipher.init(Cipher.ENCRYPT_MODE, keyAndIV.getKey(), zeroIv);
byte [] iv_input = segmentSeedValue(keyAndIV.getIV(), segmentNumber, ivLen);
byte[] iv_output;
try {
iv_output = cipher.doFinal(iv_input);
} catch (IllegalBlockSizeException e) {
String err = "Unexpected IllegalBlockSizeException for an algorithm we have already used! Rethrowing as InvalidAlgorithmParameterException.";
Log.severe(err);
throw new InvalidAlgorithmParameterException(err, e);
} catch (BadPaddingException e) {
String err = "Unexpected BadPaddingException for an algorithm we have already used! Rethrowing as InvalidAlgorithmParameterException.";
Log.severe(err);
throw new InvalidAlgorithmParameterException(err, e);
}
IvParameterSpec iv = new IvParameterSpec(iv_output, 0, ivLen);
Log.finest("IV: ivParameterSpec source="+DataUtils.printHexBytes(iv_output)+"ivParameterSpec.getIV()="+DataUtils.printHexBytes(keyAndIV.getIV()));
return iv;
}
}