/* * Copyright 2011 David Brazdil * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package uk.ac.cam.db538.cryptosms.crypto; import java.security.SecureRandom; import uk.ac.cam.db538.cryptosms.crypto.AesCbc; import uk.ac.cam.db538.cryptosms.crypto.Encryption; import uk.ac.cam.db538.cryptosms.crypto.EncryptionInterface; import uk.ac.cam.db538.cryptosms.state.Pki; import uk.ac.cam.db538.cryptosms.state.Pki.PkiNotReadyException; import uk.ac.cam.db538.cryptosms.utils.LowLevel; import uk.ac.cam.dje38.PKIwrapper.PKIwrapper.BadInputException; import uk.ac.cam.dje38.PKIwrapper.PKIwrapper.DeclinedException; import uk.ac.cam.dje38.PKIwrapper.PKIwrapper.NotConnectedException; import uk.ac.cam.dje38.PKIwrapper.PKIwrapper.PKIErrorException; import uk.ac.cam.dje38.PKIwrapper.PKIwrapper.TimeoutException; /* * Class handling encryption through PKI */ public final class EncryptionPki implements EncryptionInterface { private EncryptionNone mEncryptionNone = null; /** * Instantiates a new encryption pki. */ public EncryptionPki() { mEncryptionNone = new EncryptionNone(); } // METHODS /** * Returns byte array with random data. * * @param length the length * @return the byte[] */ @Override public byte[] generateRandomData(int length) { return mEncryptionNone.generateRandomData(length); } /** * Returns SHA-512 hash of given data. * * @param data the data * @return the hash */ @Override public byte[] getHash(byte[] data) { return mEncryptionNone.getHash(data); } /** * Returns the length of data after encryption. * Encryption adds some overhead (IV and MAC) and the data is also aligned to 16-byte blocks with random stuff * * @param length the length * @return the symmetric encrypted length */ @Override public int getSymmetricEncryptedLength(int length) { return mEncryptionNone.getSymmetricEncryptedLength(length); } /** * Returns the least multiple of AES_BLOCKSIZE greater than the argument. * * @param length the length * @return the symmetric aligned length */ @Override public int getSymmetricAlignedLength(int length) { return mEncryptionNone.getSymmetricAlignedLength(length); } /** * Encrypts data with Master Key stored with PKI. * * @param data the data * @param forceLogIn the force log in * @return the byte[] * @throws EncryptionException the encryption exception */ @Override public byte[] encryptSymmetricWithMasterKey(byte[] data, boolean forceLogIn) throws EncryptionException { try { return encryptSymmetric(data, Pki.getMasterKey(forceLogIn)); } catch (PkiNotReadyException e) { throw new EncryptionException(e); } } /** * Encrypts data with Master Key stored with PKI. * * @param data the data * @return the byte[] * @throws EncryptionException the encryption exception */ @Override public byte[] encryptSymmetricWithMasterKey(byte[] data) throws EncryptionException { return encryptSymmetricWithMasterKey(data, false); } /** * Encrypts data with given key. * * @param data the data * @param key the key * @return the byte[] */ @Override public byte[] encryptSymmetric(byte[] data, byte[] key) { // align data for MAC checking data = LowLevel.wrapData(data, getSymmetricAlignedLength(data.length)); // generate everything byte[] iv = generateRandomData(Encryption.SYM_IV_LENGTH); byte[] mac = getHash(data); // encrypt byte[] dataEncrypted = AesCbc.encrypt(data, iv, key, true, false); // save everything byte[] result = new byte[dataEncrypted.length + Encryption.SYM_OVERHEAD]; // MAC System.arraycopy(mac, 0, result, 0, Encryption.HMAC_LENGTH); // IV System.arraycopy(iv, 0, result, Encryption.HMAC_LENGTH, Encryption.SYM_IV_LENGTH); //data System.arraycopy(dataEncrypted, 0, result, Encryption.SYM_OVERHEAD, dataEncrypted.length); return result; } /** * Decrypts data with Master Key stored with PKI. * * @param data the data * @param forceLogIn the force log in * @return the byte[] * @throws EncryptionException the encryption exception */ @Override public byte[] decryptSymmetricWithMasterKey(byte[] data, boolean forceLogIn) throws EncryptionException { try { return decryptSymmetric(data, Pki.getMasterKey(forceLogIn)); } catch (PkiNotReadyException e) { throw new EncryptionException(e); } } /** * Decrypts data with Master Key stored with PKI. * * @param data the data * @return the byte[] * @throws EncryptionException the encryption exception */ @Override public byte[] decryptSymmetricWithMasterKey(byte[] data) throws EncryptionException { return decryptSymmetricWithMasterKey(data, false); } /* (non-Javadoc) * @see uk.ac.cam.db538.cryptosms.crypto.EncryptionInterface#decryptSymmetric(byte[], byte[], int) */ @Override public byte[] decryptSymmetric(byte[] data, byte[] key, int blocks) throws EncryptionException { int length = blocks * Encryption.SYM_BLOCK_LENGTH; // cut the file up byte[] macSaved = LowLevel.cutData(data, 0, Encryption.HMAC_LENGTH); byte[] iv = LowLevel.cutData(data, Encryption.HMAC_LENGTH, Encryption.SYM_IV_LENGTH); byte[] dataEncrypted = LowLevel.cutData(data, Encryption.SYM_OVERHEAD, length - Encryption.SYM_OVERHEAD); // decrypt byte[] dataDecrypted = AesCbc.decrypt(dataEncrypted, iv, key, false); // generate new MAC byte[] macReal = getHash(dataDecrypted); // compare MACs if (compareMACs(macSaved, macReal)) return dataDecrypted; else throw new WrongKeyDecryptionException(); } /** * Decrypts data with given key. * * @param data the data * @param key the key * @return the byte[] * @throws EncryptionException the encryption exception */ @Override public byte[] decryptSymmetric(byte[] data, byte[] key) throws EncryptionException { return decryptSymmetric(data, key, data.length / Encryption.SYM_BLOCK_LENGTH); } private boolean compareMACs(byte[] saved, byte[] actual) { // compare MACs boolean isCorrect = true; for (int i = 0; i < Encryption.HMAC_LENGTH; ++i) isCorrect = isCorrect && saved[i] == actual[i]; return isCorrect; } /* (non-Javadoc) * @see uk.ac.cam.db538.cryptosms.crypto.EncryptionInterface#sign(byte[]) */ @Override public byte[] sign(byte[] data) throws EncryptionException { try { byte[] signature = Pki.getPkiWrapper().sign(data); if (signature.length != Encryption.ASYM_SIGNATURE_LENGTH) throw new EncryptionException(); return signature; } catch (TimeoutException e) { throw new EncryptionException(e); } catch (PKIErrorException e) { throw new EncryptionException(e); } catch (DeclinedException e) { throw new EncryptionException(e); } catch (NotConnectedException e) { throw new EncryptionException(e); } catch (BadInputException e) { throw new EncryptionException(e); } } /* (non-Javadoc) * @see uk.ac.cam.db538.cryptosms.crypto.EncryptionInterface#verify(byte[], byte[], long) */ @Override public boolean verify(byte[] data, byte[] signature, long contactId) throws EncryptionException { try { return Pki.getPkiWrapper().verify(signature, data, contactId); } catch (TimeoutException e) { throw new EncryptionException(e); } catch (PKIErrorException e) { throw new EncryptionException(e); } catch (DeclinedException e) { throw new EncryptionException(e); } catch (NotConnectedException e) { throw new EncryptionException(e); } catch (BadInputException e) { throw new EncryptionException(e); } } @Override public SecureRandom getRandom() { return mEncryptionNone.getRandom(); } /* (non-Javadoc) * @see uk.ac.cam.db538.cryptosms.crypto.EncryptionInterface#getHMAC(byte[], byte[]) */ @Override public byte[] getHMAC(byte[] data, byte[] key) { return mEncryptionNone.getHMAC(data, key); } }