/*
 * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0,
 * and the EPL 1.0 (http://h2database.com/html/license.html).
 * Initial Developer: H2 Group
 */
package org.h2.security;

import org.h2.engine.Constants;
import org.h2.store.DataHandler;
import org.h2.store.FileStore;
import org.h2.util.Bits;
import org.h2.util.MathUtils;

A file store that encrypts all data before writing, and decrypts all data after reading. Areas that were never written to (for example after calling setLength to enlarge the file) are not encrypted (contains 0 bytes).
/** * A file store that encrypts all data before writing, and decrypts all data * after reading. Areas that were never written to (for example after calling * setLength to enlarge the file) are not encrypted (contains 0 bytes). */
public class SecureFileStore extends FileStore { private byte[] key; private final BlockCipher cipher; private final BlockCipher cipherForInitVector; private byte[] buffer = new byte[4]; private long pos; private final byte[] bufferForInitVector; private final int keyIterations; public SecureFileStore(DataHandler handler, String name, String mode, String cipher, byte[] key, int keyIterations) { super(handler, name, mode); this.key = key; this.cipher = CipherFactory.getBlockCipher(cipher); this.cipherForInitVector = CipherFactory.getBlockCipher(cipher); this.keyIterations = keyIterations; bufferForInitVector = new byte[Constants.FILE_BLOCK_SIZE]; } @Override protected byte[] generateSalt() { return MathUtils.secureRandomBytes(Constants.FILE_BLOCK_SIZE); } @Override protected void initKey(byte[] salt) { key = SHA256.getHashWithSalt(key, salt); for (int i = 0; i < keyIterations; i++) { key = SHA256.getHash(key, true); } cipher.setKey(key); key = SHA256.getHash(key, true); cipherForInitVector.setKey(key); } @Override protected void writeDirect(byte[] b, int off, int len) { super.write(b, off, len); pos += len; } @Override public void write(byte[] b, int off, int len) { if (buffer.length < b.length) { buffer = new byte[len]; } System.arraycopy(b, off, buffer, 0, len); xorInitVector(buffer, 0, len, pos); cipher.encrypt(buffer, 0, len); super.write(buffer, 0, len); pos += len; } @Override protected void readFullyDirect(byte[] b, int off, int len) { super.readFully(b, off, len); pos += len; } @Override public void readFully(byte[] b, int off, int len) { super.readFully(b, off, len); for (int i = 0; i < len; i++) { if (b[i] != 0) { cipher.decrypt(b, off, len); xorInitVector(b, off, len, pos); break; } } pos += len; } @Override public void seek(long x) { this.pos = x; super.seek(x); } private void xorInitVector(byte[] b, int off, int len, long p) { byte[] iv = bufferForInitVector; while (len > 0) { for (int i = 0; i < Constants.FILE_BLOCK_SIZE; i += 8) { Bits.writeLong(iv, i, (p + i) >>> 3); } cipherForInitVector.encrypt(iv, 0, Constants.FILE_BLOCK_SIZE); for (int i = 0; i < Constants.FILE_BLOCK_SIZE; i++) { b[off + i] ^= iv[i]; } p += Constants.FILE_BLOCK_SIZE; off += Constants.FILE_BLOCK_SIZE; len -= Constants.FILE_BLOCK_SIZE; } } }