package cologne.eck.all_peas.data;
/*
* Peafactory - Production of Password Encryption Archives
* Copyright (C) 2015 Axel von dem Bruch
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
* 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 General Public License for more details.
* See: http://www.gnu.org/licenses/gpl-2.0.html
* You should have received a copy of the GNU General Public License
* along with this library.
*/
/**
* Attachments for byte blocks and files.
*/
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;
import org.bouncycastle.crypto.InvalidCipherTextException;
import cologne.eck.peafactory.crypto.CipherStuff;
import cologne.eck.peafactory.crypto.HashStuff;
import cologne.eck.peafactory.crypto.RandomStuff;
import cologne.eck.peafactory.crypto.kdf.KeyDerivation;
import cologne.eck.tools.Comparator;
import cologne.eck.tools.Zeroizer;
public final class Attachments {
// Random values:
private static byte[] programRandomBytes = null; // unique for every program/dialog
private static byte[] nonce = null; // unique for every encrypted content
private static byte[] fileIdentifier = null; // unique for every program/dialog
//protected final static int PSW_IDENTIFIER_SIZE = 16;//8; // byte
protected final static int FILE_IDENTIFIER_SIZE = 8; // byte
protected final static int PROGRAM_RANDOM_BYTES_SIZE = KeyDerivation.getSaltSize();// at most 129
protected final static int NONCE_SIZE = 16;// byte
private final static byte[] PSW_IDENTIFIER = "_PEA_FACTORY_ID_".getBytes(PeaProperties.getCharset());// 16 byte
//========== Helper Functions: ==============================================
public final static void addBytes(RandomAccessFile f, byte[] supplementBytes)
throws IOException {
f.seek( f.length() ); // set file pointer
f.write(supplementBytes, 0, supplementBytes.length );
}
public final static byte[] getLastBytes(RandomAccessFile f, int resultSize, boolean truncate)
throws IOException {
if (resultSize < 0 || f.length() < resultSize) {
throw new IllegalArgumentException("invalid value to get last bytes");
}
byte[] result = new byte[resultSize];
long fileSize = 0;
fileSize = f.length();
if (fileSize - resultSize < 0) {
throw new IllegalArgumentException( "invalid file size, file: " + f.toString() );
}
f.seek(fileSize - resultSize); // set file pointer
f.read(result, 0, resultSize ); // read bytes
if (truncate == true) {
cutLastBytes(f, resultSize); // truncate file
}
return result;
}
// more secure cut (overwrites truncated bytes)
private final static void cutLastBytes(RandomAccessFile f, int truncateSize)
throws IOException {
if (truncateSize < 0 || f.length() < truncateSize) {
throw new IllegalArgumentException("invalid value to cut last bytes");
}
byte[] nullBytes = new byte[truncateSize];
f.seek(f.length() - truncateSize); // set file pointer
f.write(nullBytes, 0, nullBytes.length);
f.setLength(f.length() - truncateSize);
}
public final static byte[] addBytes(byte[] previousBytes, byte[] bytesToAdd) {
if(previousBytes == null || bytesToAdd == null) {
System.err.println("Attachments: addBytes failed: "
+ " null argument");
System.exit(1);
}
byte[] result = new byte[previousBytes.length + bytesToAdd.length];
System.arraycopy(previousBytes, 0,
result, 0, previousBytes.length);
System.arraycopy(bytesToAdd, 0,
result, previousBytes.length, bytesToAdd.length);
Zeroizer.zero(previousBytes);
return result;
}
//=== psw identifier ======================================================
/**
* Get the cipher text of the String "_PEA_FACTORY_ID_"
*
* @param key the key to use for encryption
* @param nonce the nonce to use for encryption
*
* @return the encrypted String "_PEA_FACTORY_ID_"
* @throws IllegalStateException
* @throws InvalidCipherTextException
*/
public final static byte[] createPswIdentifierAttachment(byte[] key, byte[] nonce)
throws IllegalStateException, InvalidCipherTextException {
if (PeaProperties.getWorkingMode().equals("-t")){
Comparator.checkNullVector(key);
Comparator.checkNullVector(nonce);
}
// use the first part of the hash of the nonce as nonce:
byte[] extraNonce = new byte[nonce.length];
byte[] hashedNonce = HashStuff.hash(nonce);
System.arraycopy(hashedNonce, 0, extraNonce, 0, extraNonce.length);
byte[] pswIdentifier = Attachments.getPSW_IDENTIFIER();
return CipherStuff.getCipherMode().processBytesNoMAC(
true,
pswIdentifier,
key,
extraNonce);
}
/**
* Check if the given byte array is the cipher text of the
* String "_PEA_FACTORY_ID_"
*
* @param pswIdentifierAttachment the byte array to check
* @param key the key used for decryption
* @param nonce the nonce used for decryption
*
* @return true if the byte array is the
* encrypted String "_PEA_FACTORY_ID_"
*/
public final static boolean checkPswIdentifier(byte[] pswIdentifierAttachment, byte[] key, byte[] nonce) {
Comparator.checkNullVector(key);
Comparator.checkNullVector(nonce);
// create an extra nonce from hash of original nonce:
byte[] extraNonce = new byte[nonce.length];
byte[] hashedNonce = HashStuff.hash(nonce);
System.arraycopy(hashedNonce, 0, extraNonce, 0, extraNonce.length);
byte[] pswIdentifier = Attachments.getPSW_IDENTIFIER();
byte[] encryptedPswIdentifier = CipherStuff.getCipherMode().processBytesNoMAC(
true,
pswIdentifier,
key,
extraNonce);
return Comparator.compare(encryptedPswIdentifier, pswIdentifierAttachment);
}
//=== file identifier ======================================================
public final static void addFileIdentifier( RandomAccessFile f)
throws IOException {
if( fileIdentifier == null ) {
throw new IllegalArgumentException ("fileIdentifier null");
}
addBytes(f, fileIdentifier);
}
public final static byte[] addFileIdentifier(byte[] cipherBytes) {
if(cipherBytes == null || fileIdentifier == null) {
System.err.println("Attachments: add fileIdentifier failed: "
+ "cipherBytes or fileIdentifier null");
System.exit(1);
}
byte[] result = new byte[cipherBytes.length + fileIdentifier.length];
System.arraycopy(cipherBytes, 0, result, 0, cipherBytes.length);
System.arraycopy(fileIdentifier, 0,
result, cipherBytes.length, fileIdentifier.length);
Zeroizer.zero(cipherBytes);
return result;
}
public final static boolean checkFileIdentifier( RandomAccessFile f, boolean truncate)
throws IOException {
if(f.length() < FILE_IDENTIFIER_SIZE ) {
return false;
}
byte[] checkIdentifier = getLastBytes(f, FILE_IDENTIFIER_SIZE, false);
if (Arrays.equals(checkIdentifier, fileIdentifier)) {
if (truncate == true) {
cutLastBytes(f, FILE_IDENTIFIER_SIZE);
}
return true;
} else {
return false;
}
}
// return null if check failed
public final static byte[] checkFileIdentifier( byte[] input, boolean truncate) {
byte[] result = new byte[input.length - FILE_IDENTIFIER_SIZE];
if(input.length < FILE_IDENTIFIER_SIZE ) {
System.err.println("input too short to contain fileIdentifier");
return null;
}
byte[] identifierToCheck = new byte[FILE_IDENTIFIER_SIZE];
System.arraycopy(input, input.length - FILE_IDENTIFIER_SIZE, identifierToCheck, 0, FILE_IDENTIFIER_SIZE);
if (Arrays.equals(identifierToCheck, fileIdentifier)) {
if (truncate == true) {
System.arraycopy(input, 0, result, 0, result.length);
Zeroizer.zero(input);
}
return result;
} else {
return null;
}
}
public final static void generateAndSetFileIdentifier() {
fileIdentifier = new RandomStuff().createUniqueBytes( Attachments.getFileIdentifierSize() );
}
public final static void setFileIdentifier(byte[] _fileIdentifier) {
fileIdentifier = _fileIdentifier;
}
public final static byte[] getFileIdentifier() {
return fileIdentifier;
}
//=== NONCE ======================================================
public final static void addNonce( RandomAccessFile f, byte[] nonce)
throws IOException {
if( nonce == null ) {
throw new IllegalArgumentException ("Nonce null");
}
if (nonce.length != NONCE_SIZE){
throw new IllegalArgumentException ("Wrong size of nonce : " + nonce.length
+ ", must be: " + NONCE_SIZE);
}
addBytes(f, nonce);
}
public final static byte[] addNonce(byte[] cipherBytes, byte[] nonce) {
if (nonce.length != NONCE_SIZE){
throw new IllegalArgumentException ("Wrong size of nonce : " + nonce.length
+ ", must be: " + NONCE_SIZE);
}
if(cipherBytes == null || nonce == null) {
System.err.println("Attachments: addNonce failed: "
+ "cipherBytes or nonce null");
System.exit(1);
}
byte[] result = new byte[cipherBytes.length + NONCE_SIZE];
System.arraycopy(cipherBytes, 0, result, 0, cipherBytes.length);
System.arraycopy(nonce, 0,
result, cipherBytes.length, NONCE_SIZE);
Zeroizer.zero(cipherBytes);
return result;
}
public final static byte[] getAndCutNonce( RandomAccessFile f, boolean truncate)
throws IOException {
if(f.length() < NONCE_SIZE ) {
throw new IllegalArgumentException("file is too short: " + f.toString() );
}
return getLastBytes(f, NONCE_SIZE, truncate);
}
public final static byte[] generateNonce() {
return new RandomStuff().createUniqueBytes( NONCE_SIZE);
}
public final static void setNonce(byte[] _nonce
) { // required in PeaControl
if (_nonce == null) {
throw new IllegalArgumentException("Nonce to set null");
}
if (_nonce.length != NONCE_SIZE){
throw new IllegalArgumentException ("Wrong size of nonce : " + nonce.length
+ ", must be: " + NONCE_SIZE);
}
nonce = _nonce;
}
public final static byte[] getNonce() {
if (nonce == null) {
//System.err.println("Attachments getCryptIV: cryptIV null");
}
return nonce;
}
public final static byte[] calculateNonce( byte[] input ) {
byte[] result = new byte[NONCE_SIZE];
if (input.length < NONCE_SIZE) {
System.err.println("Attachments calculateNonce: input too short");
return null;
} else {
System.arraycopy(input, input.length - NONCE_SIZE, result, 0, result.length);
return result;
}
}
public final static byte[] cutNonce(byte[] input) {
if(input.length < NONCE_SIZE ) {
System.err.println("input is too short to contain Nonce. ");
return null;
}
byte[] result = new byte[input.length - NONCE_SIZE];
System.arraycopy(input, 0, result, 0, result.length);
Zeroizer.zero(input);
return result;
}
//===================================================
// Getter & Setter & Generators
public final static void generateAndSetProgramRandomBytes() {
programRandomBytes = new RandomStuff().createRandomBytes( Attachments.getProgramRandomBytesSize() );
}
public final static void setProgramRandomBytes( byte[] _programRandomBytes) {
programRandomBytes = _programRandomBytes;
}
public final static byte[] getProgramRandomBytes() {
if (programRandomBytes == null) {
System.err.println("Attachments getProgramRandomBytes: programRandomBytes null");
}
return programRandomBytes;
}
public final static int getProgramRandomBytesSize() {
return PROGRAM_RANDOM_BYTES_SIZE;
}
public final static int getPswIdentifierSize() {
return PSW_IDENTIFIER.length;//PSW_IDENTIFIER_SIZE;
}
public final static int getFileIdentifierSize() {
return FILE_IDENTIFIER_SIZE;
}
public final static int getNonceSize() {
return NONCE_SIZE;
}
public static byte[] attachBytes(byte[] sourceBytes, byte[] bytesToAttach) {
if(sourceBytes == null || bytesToAttach == null) {
System.err.println("Attachments: attachedBytes failed");
System.exit(1);
}
byte[] result = new byte[sourceBytes.length + bytesToAttach.length];
System.arraycopy(sourceBytes, 0, result, 0, sourceBytes.length);
System.arraycopy(bytesToAttach, 0,
result, sourceBytes.length, bytesToAttach.length);
Zeroizer.zero(sourceBytes);
return result;
}
public final static void addSalt( RandomAccessFile f, byte[] attachedSalt)
throws IOException {
if( attachedSalt == null ) {
throw new IllegalArgumentException ("Attached Salt null");
}
addBytes(f, attachedSalt);
}
public final static void cutSalt( RandomAccessFile f)
throws IOException {
if(f.length() < PROGRAM_RANDOM_BYTES_SIZE ) {
throw new IllegalArgumentException("file is too short: " + f.toString() );
}
cutLastBytes(f, PROGRAM_RANDOM_BYTES_SIZE);
}
public final static byte[] cutSalt(byte[] input) {
if(input.length < PROGRAM_RANDOM_BYTES_SIZE ) {
System.err.println("input is too short to contain the salt. ");
return null;
}
byte[] result = new byte[input.length - PROGRAM_RANDOM_BYTES_SIZE];
System.arraycopy(input, 0, result, 0, result.length);
Zeroizer.zero(input);
return result;
}
public final static byte[] getSalt(byte[] cipherBytes) {
byte[] result = new byte[PROGRAM_RANDOM_BYTES_SIZE];
System.arraycopy(cipherBytes, 0, result, 0, result.length);
return result;
}
public final static byte[] getAndCutSalt( RandomAccessFile f, boolean truncate)
throws IOException {
if(f.length() < PROGRAM_RANDOM_BYTES_SIZE ) {
throw new IllegalArgumentException("file is too short: " + f.toString() );
}
return getLastBytes(f, PROGRAM_RANDOM_BYTES_SIZE, truncate);
}
public final static byte[] getEndBytesOfFile( String fileName, int resultLen)
throws IOException {
RandomAccessFile f = new RandomAccessFile( fileName, "r" );
if(f.length() < resultLen) {
System.err.println("File with length " + f.length() + " is too short to get " + resultLen + " end bytes");
}
byte[] result = getLastBytes(f, resultLen, false);
f.close();
return result;
}
/**
* @return the pSW_IDENTIFIER
*/
public static final byte[] getPSW_IDENTIFIER() {
return PSW_IDENTIFIER;
}
}