1 6 21 22 package de.schlichtherle.crypto.io; 23 24 import de.schlichtherle.crypto.SeekableBlockCipher; 25 import de.schlichtherle.io.rof.FilterReadOnlyFile; 26 import de.schlichtherle.io.rof.ReadOnlyFile; 27 28 import java.io.IOException ; 29 30 import org.bouncycastle.crypto.Mac; 31 32 48 public abstract class CipherReadOnlyFile extends FilterReadOnlyFile { 68 69 76 private static final int MAX_WINDOW_LEN = 1024; 77 78 79 private static final long min(long a, long b) { 80 return a < b ? a : b; 81 } 82 83 84 private static final long max(long a, long b) { 85 return a < b ? b : a; 86 } 87 88 89 private long start; 90 91 92 private long length; 93 94 98 private long fp; 99 100 104 private long windowOff; 105 106 112 private byte[] window; 113 114 115 private SeekableBlockCipher cipher; 116 117 122 private long blockOff; 123 124 128 private byte[] block; 129 130 131 private boolean closed; 132 133 143 public CipherReadOnlyFile(ReadOnlyFile rof) { 144 super(rof); 145 } 146 147 163 public void init( 164 final SeekableBlockCipher cipher, 165 final long start, 166 final long length) 167 throws IOException { 168 if (closed) 170 throw new IOException ("CipherReadOnlyFile has already been closed!"); 171 if (this.cipher != null) 172 throw new IllegalStateException ("CipherReadOnlyFile has already been initialized!"); 173 174 if (rof == null) 176 throw new NullPointerException ("rof"); 177 178 if (cipher == null) 180 throw new NullPointerException ("cipher"); 181 if (start < 0 || length < 0) 182 throw new IllegalArgumentException (); 183 184 this.cipher = cipher; 185 this.start = start; 186 this.length = length; 187 188 blockOff = length; 189 final int blockLen = cipher.getBlockSize(); 190 block = new byte[blockLen]; 191 windowOff = Long.MIN_VALUE; window = new byte[(MAX_WINDOW_LEN / blockLen) * blockLen]; 194 assert fp == 0; 195 assert block.length > 0; 196 assert window.length > 0; 197 assert window.length % block.length == 0; 198 assert cipher != null; 199 } 200 201 211 protected byte[] computeMac(final Mac mac) 212 throws IOException { 213 final int windowLen = window.length; 214 final byte[] buf = new byte[mac.getMacSize()]; 215 216 final long safedFp = getFilePointer(); 217 try { 218 for (fp = 0; fp < length; fp += windowLen) { 219 positionWindow(); 220 final long remaining = length - windowOff; 221 mac.update(window, 0, (int) min(windowLen, remaining)); 222 } 223 final int bufLen = mac.doFinal(buf, 0); 224 assert bufLen == buf.length; 225 } finally { 226 fp = safedFp; 227 } 228 229 return buf; 230 } 231 232 public long length() throws IOException { 233 ensureInit(); 234 235 return length; 236 } 237 238 public long getFilePointer() throws IOException { 239 ensureInit(); 240 241 return fp; 242 } 243 244 public void seek(final long fp) throws IOException { 245 if (fp < 0) 246 throw new IOException ("File pointer must not be negative!"); 247 248 ensureInit(); 249 250 if (fp > length) 251 throw new IOException ("File pointer (" + fp 252 + ") is larger than file length (" + length + ")!"); 253 254 this.fp = fp; 255 } 256 257 public int read() throws IOException { 258 ensureInit(); 260 if (fp >= length) 261 return -1; 262 263 positionBlock(); 265 return block[(int) (fp++ % block.length)]; 266 } 267 268 public int read(final byte[] buf, final int off, final int len) 269 throws IOException { 270 if (buf == null) 272 throw new NullPointerException ("buf"); 273 final int offPlusLen = off + len; 274 if ((off | len | offPlusLen | buf.length - offPlusLen) < 0) 275 throw new IndexOutOfBoundsException (); 276 if (len == 0) 277 return 0; 279 ensureInit(); 281 if (fp >= length) 282 return -1; 283 284 final int blockLen = block.length; 286 int read = 0; 288 { 289 final int o = (int) (fp % blockLen); 291 if (o != 0) { 292 positionBlock(); 294 read = (int) min(len, blockLen - o); 295 read = (int) min(read, length - fp); 296 System.arraycopy(block, o, buf, off, read); 297 fp += read; 298 } 299 } 300 301 { 302 long blockCounter = fp / blockLen; 304 while (read + blockLen < len && fp + blockLen <= length) { 305 positionWindow(); 307 cipher.setBlockCounter(blockCounter++); 308 cipher.processBlock(window, (int) (fp - windowOff), buf, off + read); 309 read += blockLen; 310 fp += blockLen; 311 } 312 } 313 314 if (read < len && fp < length) { 316 positionBlock(); 318 final int n = (int) min(len - read, length - fp); 319 System.arraycopy(block, 0, buf, off + read, n); 320 read += n; 321 fp += n; 322 } 323 324 assert read > 0; 327 return read; 328 } 329 330 public int skipBytes(int n) throws IOException { 331 if (n <= 0) 332 return 0; 334 ensureInit(); 336 337 if (fp >= length) 338 return 0; 339 final long remaining = length - fp; 340 if (n > remaining) 341 n = (int) remaining; 342 fp += n; 343 344 return n; 345 } 346 347 352 private final void ensureInit() throws IOException { 353 if (cipher == null) 354 throw new IOException ("CipherReadOnlyFile has already been closed or is not initialized!"); 355 } 356 357 364 public void close() throws IOException { 365 if (!closed) { 367 closed = true; 368 cipher = null; 369 rof.close(); 370 } 371 } 372 373 381 private void positionBlock() throws IOException { 382 final long fp = this.fp; 384 final int blockLen = block.length; 385 if (blockOff <= fp) { 386 final long nextBlockOff = blockOff + blockLen; 387 if (fp < nextBlockOff) 388 return; 389 } 390 391 positionWindow(); 393 final long blockCounter = fp / blockLen; 394 blockOff = blockCounter * blockLen; 395 396 cipher.setBlockCounter(blockCounter); 398 cipher.processBlock(window, (int) (blockOff - windowOff), block, 0); 399 } 400 401 409 private void positionWindow() throws IOException { 410 final long fp = this.fp; 412 final int windowLen = window.length; 413 final long nextWindowOff = windowOff + windowLen; 414 if (windowOff <= fp && fp < nextWindowOff) 415 return; 416 417 try { 418 final int blockLen = block.length; 420 windowOff = (fp / blockLen) * blockLen; if (windowOff != nextWindowOff) 422 rof.seek(windowOff + start); 423 424 int n = 0; 429 do { 430 int read = rof.read(window, n, windowLen - n); 431 if (read < 0) 432 break; 433 n += read; 434 } while (n < windowLen); 435 } catch (IOException ioe) { 436 windowOff = -windowLen - 1; throw ioe; 438 } 439 } 440 } 441 | Popular Tags |