From af7dab779aa71056551ba9fc6e9817c8b29b5a4a Mon Sep 17 00:00:00 2001 From: Brian Pellin Date: Sun, 3 May 2009 21:41:06 -0500 Subject: [PATCH] Start combining functionality. --- .../BufferedBlockCipherOutputStream.java | 106 ++++++++++++++++++ .../keepass/keepasslib/NullOutputStream.java | 51 +++++++++ .../keepass/keepasslib/PwManagerOutput.java | 43 ++----- src/org/phoneid/keepassj2me/ImporterV3.java | 30 +++-- 4 files changed, 187 insertions(+), 43 deletions(-) create mode 100644 src/com/android/keepass/keepasslib/BufferedBlockCipherOutputStream.java create mode 100644 src/com/android/keepass/keepasslib/NullOutputStream.java diff --git a/src/com/android/keepass/keepasslib/BufferedBlockCipherOutputStream.java b/src/com/android/keepass/keepasslib/BufferedBlockCipherOutputStream.java new file mode 100644 index 000000000..5e6e15f06 --- /dev/null +++ b/src/com/android/keepass/keepasslib/BufferedBlockCipherOutputStream.java @@ -0,0 +1,106 @@ +/* + * Copyright 2009 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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 2 of the License, or + * (at your option) any later version. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.android.keepass.keepasslib; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher; + +public class BufferedBlockCipherOutputStream extends FilterOutputStream { + + OutputStream mOS; + PaddedBufferedBlockCipher mCipher; + + public BufferedBlockCipherOutputStream(OutputStream out) { + super(out); + mOS = out; + } + + public BufferedBlockCipherOutputStream(OutputStream out, PaddedBufferedBlockCipher cipher) { + super(out); + mOS = out; + mCipher = cipher; + } + + @Override + public void close() throws IOException { + byte[] block = new byte[2*mCipher.getBlockSize()]; + int bytes; + try { + bytes = mCipher.doFinal(block, 0); + } catch (DataLengthException e) { + throw new IOException(e.getMessage()); + } catch (IllegalStateException e) { + throw new IOException("IllegalStateException."); + } catch (InvalidCipherTextException e) { + throw new IOException("InvalidCipherText."); + } + if ( bytes > 0 ) { + mOS.write(block, 0, bytes); + } + } + + @Override + public void write(byte[] buffer, int offset, int count) throws IOException { + int outputLen = mCipher.getUpdateOutputSize(count); + + if ( outputLen > 0 ) { + byte[] block = new byte[outputLen]; + int bytes = mCipher.processBytes(buffer, offset, count, block, 0); + if ( bytes > 0 ) { + mOS.write(block, 0, bytes); + } + } + } + + @Override + public void write(byte[] buffer) throws IOException { + int length = buffer.length; + int outputLen = mCipher.getUpdateOutputSize(length); + + if ( outputLen > 0 ) { + byte[] block = new byte[outputLen]; + int bytes = mCipher.processBytes(buffer, 0, length, block, 0); + + if ( bytes > 0 ) { + mOS.write(block, 0, bytes); + } + } + } + + @Override + public void write(int oneByte) throws IOException { + int outputLen = mCipher.getUpdateOutputSize(1); + + if ( outputLen > 0 ) { + byte[] block = new byte[outputLen]; + int bytes = mCipher.processByte((byte)oneByte, block, 0); + + if ( bytes > 0 ) { + mOS.write(block, 0, bytes); + } + } + } + +} diff --git a/src/com/android/keepass/keepasslib/NullOutputStream.java b/src/com/android/keepass/keepasslib/NullOutputStream.java new file mode 100644 index 000000000..bd60d4e20 --- /dev/null +++ b/src/com/android/keepass/keepasslib/NullOutputStream.java @@ -0,0 +1,51 @@ +/* + * Copyright 2009 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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 2 of the License, or + * (at your option) any later version. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.android.keepass.keepasslib; + +import java.io.IOException; +import java.io.OutputStream; + +public class NullOutputStream extends OutputStream { + + @Override + public void close() throws IOException { + super.close(); + } + + @Override + public void flush() throws IOException { + super.flush(); + } + + @Override + public void write(byte[] buffer, int offset, int count) throws IOException { + super.write(buffer, offset, count); + } + + @Override + public void write(byte[] buffer) throws IOException { + super.write(buffer); + } + + @Override + public void write(int oneByte) throws IOException { + } + +} diff --git a/src/com/android/keepass/keepasslib/PwManagerOutput.java b/src/com/android/keepass/keepasslib/PwManagerOutput.java index c4bea5936..2ccdbed1b 100644 --- a/src/com/android/keepass/keepasslib/PwManagerOutput.java +++ b/src/com/android/keepass/keepasslib/PwManagerOutput.java @@ -57,43 +57,20 @@ public class PwManagerOutput { mDebug = debug; } - /* - public void close() throws PwManagerOutputException { - try { - mOS.close(); - } catch (IOException e) { - throw new PwManagerOutputException("Failed to close stream."); - } - } - */ - public byte[] getFinalKey(PwDbHeader header) throws PwManagerOutputException { - - // Write checksum Checksum - MessageDigest md = null; try { - md = MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException e) { - assert true; - throw new PwManagerOutputException("SHA-256 not implemented here."); + return ImporterV3.makeFinalKey(header.masterSeed, header.masterSeed2, mPM.masterKey, mPM.numKeyEncRounds); + } catch (IOException e) { + throw new PwManagerOutputException("Key creation failed: " + e.getMessage()); } - NullOutputStream nos = new NullOutputStream(); - DigestOutputStream dos = new DigestOutputStream(nos, md); - - byte[] transformedMasterKey = ImporterV3.transformMasterKey(header.masterSeed2, mPM.masterKey, mPM.numKeyEncRounds); - try { - dos.write(header.masterSeed); - dos.write(transformedMasterKey); - } catch ( IOException e ) { - throw new PwManagerOutputException("Failed to build final key."); - } - - return md.digest(); - } - public byte[] getFinalKey2(PwDbHeader header) { - return ImporterV3.makeFinalKey(header.masterSeed, header.masterSeed2, mPM.masterKey, mPM.numKeyEncRounds); + public byte[] getFinalKey2(PwDbHeader header) throws PwManagerOutputException { + try { + return ImporterV3.makeFinalKey(header.masterSeed, header.masterSeed2, mPM.masterKey, mPM.numKeyEncRounds); + } catch (IOException e) { + throw new PwManagerOutputException("Key creation failed: " + e.getMessage()); + } } public void output() throws PwManagerOutputException, IOException { @@ -151,7 +128,7 @@ public class PwManagerOutput { } try { - cipher.init( Cipher.ENCRYPT_MODE, new SecretKeySpec(mPM.finalKey, "AES" ), new IvParameterSpec(header.encryptionIV) ); + cipher.init( Cipher.ENCRYPT_MODE, new SecretKeySpec(finalKey, "AES" ), new IvParameterSpec(header.encryptionIV) ); CipherOutputStream cos = new CipherOutputStream(mOS, cipher); outputPlanGroupAndEntries(cos); cos.close(); diff --git a/src/org/phoneid/keepassj2me/ImporterV3.java b/src/org/phoneid/keepassj2me/ImporterV3.java index 44aa16d07..7b486b691 100644 --- a/src/org/phoneid/keepassj2me/ImporterV3.java +++ b/src/org/phoneid/keepassj2me/ImporterV3.java @@ -27,8 +27,10 @@ package org.phoneid.keepassj2me; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; +import java.security.DigestOutputStream; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; +import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import javax.crypto.BadPaddingException; @@ -49,6 +51,8 @@ import org.phoneid.PhoneIDUtil; import android.util.Log; import com.android.keepass.keepasslib.InvalidKeyFileException; +import com.android.keepass.keepasslib.NullOutputStream; +import com.android.keepass.keepasslib.PwManagerOutput.PwManagerOutputException; /** * Load a v3 database file. @@ -267,17 +271,23 @@ public class ImporterV3 { return newManager; } - public static byte[] makeFinalKey(byte[] masterSeed, byte[] masterSeed2, byte[] masterKey, int numRounds) { - byte[] transformedMasterKey = transformMasterKey(masterSeed2, masterKey, numRounds ); + public static byte[] makeFinalKey(byte[] masterSeed, byte[] masterSeed2, byte[] masterKey, int numRounds) throws IOException { - // Hash the master password with the salt in the file - SHA256Digest md = new SHA256Digest(); - md.update( masterSeed, 0, masterSeed.length ); - md.update( transformedMasterKey, 0, transformedMasterKey.length ); - byte[] finalKey = new byte[md.getDigestSize()]; - md.doFinal(finalKey, 0); - - return finalKey; + // Write checksum Checksum + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new IOException("SHA-256 not implemented here."); + } + NullOutputStream nos = new NullOutputStream(); + DigestOutputStream dos = new DigestOutputStream(nos, md); + + byte[] transformedMasterKey = ImporterV3.transformMasterKey(masterSeed2, masterKey, numRounds); + dos.write(masterSeed); + dos.write(transformedMasterKey); + + return md.digest(); } /**