001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (http://h2database.com/html/license.html).
004: * Initial Developer: H2 Group
005: */
006: package org.h2.security;
007:
008: import java.sql.SQLException;
009:
010: import org.h2.constant.ErrorCode;
011: import org.h2.engine.Constants;
012: import org.h2.message.Message;
013: import org.h2.store.DataHandler;
014: import org.h2.store.FileStore;
015: import org.h2.util.RandomUtils;
016:
017: /**
018: * A file store that encrypts all data before writing,
019: * and decrypts all data after reading.
020: */
021: public class SecureFileStore extends FileStore {
022:
023: private byte[] key;
024: private BlockCipher cipher;
025: private BlockCipher cipherForInitVector;
026: private byte[] buffer = new byte[4];
027: private long pos;
028: private byte[] bufferForInitVector;
029: private int keyIterations;
030:
031: public SecureFileStore(DataHandler handler, String name,
032: String mode, byte[] magic, String cipher, byte[] key,
033: int keyIterations) throws SQLException {
034: super (handler, name, mode, magic);
035: this .key = key;
036: if ("XTEA".equalsIgnoreCase(cipher)) {
037: this .cipher = new XTEA();
038: this .cipherForInitVector = new XTEA();
039: } else if ("AES".equalsIgnoreCase(cipher)) {
040: this .cipher = new AES();
041: this .cipherForInitVector = new AES();
042: } else {
043: throw Message.getSQLException(ErrorCode.UNSUPPORTED_CIPHER,
044: cipher);
045: }
046: this .keyIterations = keyIterations;
047: bufferForInitVector = new byte[Constants.FILE_BLOCK_SIZE];
048: }
049:
050: protected byte[] generateSalt() {
051: return RandomUtils.getSecureBytes(Constants.FILE_BLOCK_SIZE);
052: }
053:
054: protected void initKey(byte[] salt) {
055: SHA256 sha = new SHA256();
056: key = sha.getHashWithSalt(key, salt);
057: for (int i = 0; i < keyIterations; i++) {
058: key = sha.getHash(key);
059: }
060: cipher.setKey(key);
061: key = sha.getHash(key);
062: cipherForInitVector.setKey(key);
063: }
064:
065: protected void writeDirect(byte[] b, int off, int len)
066: throws SQLException {
067: super .write(b, off, len);
068: pos += len;
069: }
070:
071: public void write(byte[] b, int off, int len) throws SQLException {
072: if (buffer.length < b.length) {
073: buffer = new byte[len];
074: }
075: System.arraycopy(b, off, buffer, 0, len);
076: xorInitVector(buffer, 0, len, pos);
077: cipher.encrypt(buffer, 0, len);
078: super .write(buffer, 0, len);
079: pos += len;
080: }
081:
082: protected void readFullyDirect(byte[] b, int off, int len)
083: throws SQLException {
084: super .readFully(b, off, len);
085: pos += len;
086: }
087:
088: public void readFully(byte[] b, int off, int len)
089: throws SQLException {
090: super .readFully(b, off, len);
091: cipher.decrypt(b, off, len);
092: xorInitVector(b, off, len, pos);
093: pos += len;
094: }
095:
096: public void seek(long x) throws SQLException {
097: this .pos = x;
098: super .seek(x);
099: }
100:
101: public void setLength(long newLength) throws SQLException {
102: long oldPos = pos;
103: long length = length();
104: if (newLength > length) {
105: seek(length);
106: byte[] empty = EMPTY;
107: while (true) {
108: int p = (int) Math
109: .min(newLength - length, EMPTY.length);
110: if (p <= 0) {
111: break;
112: }
113: write(empty, 0, p);
114: length += p;
115: }
116: seek(oldPos);
117: } else {
118: super .setLength(newLength);
119: }
120: }
121:
122: private void xorInitVector(byte[] b, int off, int len, long pos) {
123: byte[] iv = bufferForInitVector;
124: while (len > 0) {
125: for (int i = 0; i < Constants.FILE_BLOCK_SIZE; i += 8) {
126: long block = ((pos + i) >>> 3);
127: iv[i] = (byte) (block >> 56);
128: iv[i + 1] = (byte) (block >> 48);
129: iv[i + 2] = (byte) (block >> 40);
130: iv[i + 3] = (byte) (block >> 32);
131: iv[i + 4] = (byte) (block >> 24);
132: iv[i + 5] = (byte) (block >> 16);
133: iv[i + 6] = (byte) (block >> 8);
134: iv[i + 7] = (byte) block;
135: }
136: cipherForInitVector.encrypt(iv, 0,
137: Constants.FILE_BLOCK_SIZE);
138: for (int i = 0; i < Constants.FILE_BLOCK_SIZE; i++) {
139: b[off + i] ^= iv[i];
140: }
141: pos += Constants.FILE_BLOCK_SIZE;
142: off += Constants.FILE_BLOCK_SIZE;
143: len -= Constants.FILE_BLOCK_SIZE;
144: }
145: }
146:
147: public boolean isEncrypted() {
148: return true;
149: }
150:
151: }
|