/* * Copyright 2014 individual contributors as indicated by the @author * tags * * This is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this software. If not, see <http://www.gnu.org/licenses/>. */ package org.sector67.otp.key; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; /** * An in-memory implementation of an OTP keystore primarily for testing purposes * * @author scott.hasse@gmail.com */ public class InMemoryKeyStore implements TestableKeyStore { private Map<String, Integer> offsets = new HashMap<String, Integer>(); private Map<String, byte[]> keys = new HashMap<String, byte[]>(); Random r = new SecureRandom(); private KeyEraser eraser = new MultiPassSecureRandomEraser(); @Override public void init() { } @Override public void setKeyEraser(KeyEraser eraser) { this.eraser = eraser; } public void generateKey(String name, int length) { if (name == null) { throw new IllegalArgumentException( "You cannot create a null key name."); } if (keys.containsKey(name)) { throw new IllegalArgumentException( "You cannot create a key that already exists: " + name); } offsets.put(name, new Integer(0)); byte[] key = new byte[length]; r.nextBytes(key); keys.put(name, key); } /* * Put a key in the key store, for testing purposes */ public void addKey(String name, byte[] key, int offset) { if (name == null || key == null) { throw new IllegalArgumentException( "You cannot create a null key name."); } if (keys.containsKey(name)) { throw new IllegalArgumentException( "You cannot create a key that already exists: " + name); } offsets.put(name, new Integer(offset)); keys.put(name, Arrays.copyOf(key, key.length)); } /* * Provides the next bytes from the key and updates the current key offset */ @Override public byte[] getKeyBytesForEncryption(String name, int length) throws KeyException { if (name == null) { throw new KeyException("You cannot use a null key name."); } if (!keys.containsKey(name)) { throw new KeyException( "The requested key does not exist in this key store: " + name); } byte[] key = keys.get(name); int currentOffset = offsets.get(name); if (key.length < currentOffset + length) { throw new KeyException( "The key is not long enough to provide the requested bytes"); } byte[] result = new byte[length]; for (int i = 0; i < length; i++) { result[i] = key[currentOffset + i]; } // update the offset offsets.put(name, currentOffset + length); return result; } public Set<String> getKeyNames() { return keys.keySet(); } public void copyKey(String source, String destination) { byte[] key = keys.get(source); byte[] copy = Arrays.copyOf(key, key.length); int offset = offsets.get(source); offsets.put(destination, offset); keys.put(destination, copy); } public class InMemoryKeyData implements KeyData { private int position = 0; private byte[] key; public InMemoryKeyData(byte[] key) { this.key = key; this.position = 0; } public void close() { // no-op for in-memory } public void seek(int position) throws KeyException { this.position = position; } public void write(byte[] data) throws KeyException { // System.out.println("data length: " + data.length + " position: " // + position); for (int i = 0; i < data.length; i++) { key[position + i] = data[position]; } } public byte[] getBytes() throws KeyException { return key; } } @Override public List<String> listKeys() { List<String> result = new ArrayList<String>(); result.addAll(keys.keySet()); Collections.sort(result); return result; } @Override public void deleteKey(String name) { if (offsets.containsKey(name)) { offsets.remove(name); } if (keys.containsKey(name)) { keys.remove(name); } } @Override public int getCurrentOffset(String keyName) throws KeyException { return offsets.get(keyName); } @Override public int getSize(String keyName) throws KeyException { throw new UnsupportedOperationException("Not Yet Implemented"); } @Override public byte[] getKeyBytesForDecryption(String name, int offset, int length) throws KeyException { if (name == null) { throw new KeyException("You cannot use a null key name."); } if (!keys.containsKey(name)) { throw new KeyException( "The requested key does not exist in this key store: " + name); } byte[] key = keys.get(name); int currentOffset = offset; if (key.length < currentOffset + length) { throw new KeyException( "The key is not long enough to provide the requested bytes"); } byte[] result = new byte[length]; for (int i = 0; i < length; i++) { result[i] = key[currentOffset + i]; } return result; } @Override public void eraseKeyBytes(String name, int offset, int length) throws KeyException { if (name == null) { throw new KeyException("You cannot use a null key name."); } if (!keys.containsKey(name)) { throw new KeyException( "The requested key does not exist in this key store: " + name); } byte[] key = keys.get(name); int currentOffset = offset; if (key.length < currentOffset + length) { throw new KeyException( "The key is not long enough to provide the requested bytes"); } InMemoryKeyData kd = new InMemoryKeyData(key); eraser.erase(kd, offset, length); keys.put(name, kd.getBytes()); } }