1 | /* |
2 | * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, |
3 | * and the EPL 1.0 (http://h2database.com/html/license.html). |
4 | * Initial Developer: H2 Group |
5 | */ |
6 | package org.h2.security; |
7 | |
8 | import org.h2.engine.Constants; |
9 | import org.h2.store.DataHandler; |
10 | import org.h2.store.FileStore; |
11 | import org.h2.util.MathUtils; |
12 | |
13 | /** |
14 | * A file store that encrypts all data before writing, and decrypts all data |
15 | * after reading. Areas that were never written to (for example after calling |
16 | * setLength to enlarge the file) are not encrypted (contains 0 bytes). |
17 | */ |
18 | public class SecureFileStore extends FileStore { |
19 | |
20 | private byte[] key; |
21 | private final BlockCipher cipher; |
22 | private final BlockCipher cipherForInitVector; |
23 | private byte[] buffer = new byte[4]; |
24 | private long pos; |
25 | private final byte[] bufferForInitVector; |
26 | private final int keyIterations; |
27 | |
28 | public SecureFileStore(DataHandler handler, String name, String mode, |
29 | String cipher, byte[] key, int keyIterations) { |
30 | super(handler, name, mode); |
31 | this.key = key; |
32 | this.cipher = CipherFactory.getBlockCipher(cipher); |
33 | this.cipherForInitVector = CipherFactory.getBlockCipher(cipher); |
34 | this.keyIterations = keyIterations; |
35 | bufferForInitVector = new byte[Constants.FILE_BLOCK_SIZE]; |
36 | } |
37 | |
38 | @Override |
39 | protected byte[] generateSalt() { |
40 | return MathUtils.secureRandomBytes(Constants.FILE_BLOCK_SIZE); |
41 | } |
42 | |
43 | @Override |
44 | protected void initKey(byte[] salt) { |
45 | key = SHA256.getHashWithSalt(key, salt); |
46 | for (int i = 0; i < keyIterations; i++) { |
47 | key = SHA256.getHash(key, true); |
48 | } |
49 | cipher.setKey(key); |
50 | key = SHA256.getHash(key, true); |
51 | cipherForInitVector.setKey(key); |
52 | } |
53 | |
54 | @Override |
55 | protected void writeDirect(byte[] b, int off, int len) { |
56 | super.write(b, off, len); |
57 | pos += len; |
58 | } |
59 | |
60 | @Override |
61 | public void write(byte[] b, int off, int len) { |
62 | if (buffer.length < b.length) { |
63 | buffer = new byte[len]; |
64 | } |
65 | System.arraycopy(b, off, buffer, 0, len); |
66 | xorInitVector(buffer, 0, len, pos); |
67 | cipher.encrypt(buffer, 0, len); |
68 | super.write(buffer, 0, len); |
69 | pos += len; |
70 | } |
71 | |
72 | @Override |
73 | protected void readFullyDirect(byte[] b, int off, int len) { |
74 | super.readFully(b, off, len); |
75 | pos += len; |
76 | } |
77 | |
78 | @Override |
79 | public void readFully(byte[] b, int off, int len) { |
80 | super.readFully(b, off, len); |
81 | for (int i = 0; i < len; i++) { |
82 | if (b[i] != 0) { |
83 | cipher.decrypt(b, off, len); |
84 | xorInitVector(b, off, len, pos); |
85 | break; |
86 | } |
87 | } |
88 | pos += len; |
89 | } |
90 | |
91 | @Override |
92 | public void seek(long x) { |
93 | this.pos = x; |
94 | super.seek(x); |
95 | } |
96 | |
97 | private void xorInitVector(byte[] b, int off, int len, long p) { |
98 | byte[] iv = bufferForInitVector; |
99 | while (len > 0) { |
100 | for (int i = 0; i < Constants.FILE_BLOCK_SIZE; i += 8) { |
101 | long block = (p + i) >>> 3; |
102 | iv[i] = (byte) (block >> 56); |
103 | iv[i + 1] = (byte) (block >> 48); |
104 | iv[i + 2] = (byte) (block >> 40); |
105 | iv[i + 3] = (byte) (block >> 32); |
106 | iv[i + 4] = (byte) (block >> 24); |
107 | iv[i + 5] = (byte) (block >> 16); |
108 | iv[i + 6] = (byte) (block >> 8); |
109 | iv[i + 7] = (byte) block; |
110 | } |
111 | cipherForInitVector.encrypt(iv, 0, Constants.FILE_BLOCK_SIZE); |
112 | for (int i = 0; i < Constants.FILE_BLOCK_SIZE; i++) { |
113 | b[off + i] ^= iv[i]; |
114 | } |
115 | p += Constants.FILE_BLOCK_SIZE; |
116 | off += Constants.FILE_BLOCK_SIZE; |
117 | len -= Constants.FILE_BLOCK_SIZE; |
118 | } |
119 | } |
120 | |
121 | } |