@Override public PwDatabaseV4 openDatabase( InputStream inStream, String password, String keyfile, UpdateStatus status) throws IOException, InvalidDBException { db = createDB(); PwDbHeaderV4 header = new PwDbHeaderV4(db); hashOfHeader = header.loadFromFile(inStream); db.setMasterKey(password, keyfile); db.makeFinalKey(header.masterSeed, header.transformSeed, (int) db.numKeyEncRounds); // Attach decryptor Cipher cipher; try { cipher = CipherFactory.getInstance( db.dataCipher, Cipher.DECRYPT_MODE, db.finalKey, header.encryptionIV); } catch (NoSuchAlgorithmException e) { throw new IOException("Invalid algorithm."); } catch (NoSuchPaddingException e) { throw new IOException("Invalid algorithm."); } catch (InvalidKeyException e) { throw new IOException("Invalid algorithm."); } catch (InvalidAlgorithmParameterException e) { throw new IOException("Invalid algorithm."); } InputStream decrypted = new BetterCipherInputStream(inStream, cipher, 50 * 1024); LEDataInputStream dataDecrypted = new LEDataInputStream(decrypted); byte[] storedStartBytes = null; try { storedStartBytes = dataDecrypted.readBytes(32); if (storedStartBytes == null || storedStartBytes.length != 32) { throw new InvalidPasswordException(); } } catch (IOException e) { throw new InvalidPasswordException(); } if (!Arrays.equals(storedStartBytes, header.streamStartBytes)) { throw new InvalidPasswordException(); } HashedBlockInputStream hashed = new HashedBlockInputStream(dataDecrypted); InputStream decompressed; if (db.compressionAlgorithm == PwCompressionAlgorithm.Gzip) { decompressed = new GZIPInputStream(hashed); } else { decompressed = hashed; } if (header.protectedStreamKey == null) { assert (false); throw new IOException("Invalid stream key."); } randomStream = PwStreamCipherFactory.getInstance(header.innerRandomStream, header.protectedStreamKey); if (randomStream == null) { throw new ArcFourException(); } ReadXmlStreamed(decompressed); return db; }
private KdbContext ReadXmlElement(KdbContext ctx, XmlPullParser xpp) throws XmlPullParserException, IOException, InvalidDBException { String name = xpp.getName(); switch (ctx) { case Null: if (name.equalsIgnoreCase(ElemDocNode)) { return SwitchContext(ctx, KdbContext.KeePassFile, xpp); } else ReadUnknown(xpp); break; case KeePassFile: if (name.equalsIgnoreCase(ElemMeta)) { return SwitchContext(ctx, KdbContext.Meta, xpp); } else if (name.equalsIgnoreCase(ElemRoot)) { return SwitchContext(ctx, KdbContext.Root, xpp); } else { ReadUnknown(xpp); } break; case Meta: if (name.equalsIgnoreCase(ElemGenerator)) { ReadString(xpp); // Ignore } else if (name.equalsIgnoreCase(ElemHeaderHash)) { String encodedHash = ReadString(xpp); if (!EmptyUtils.isNullOrEmpty(encodedHash) && (hashOfHeader != null)) { byte[] hash = Base64Coder.decode(encodedHash); if (!Arrays.equals(hash, hashOfHeader)) { throw new InvalidDBException(); } } } else if (name.equalsIgnoreCase(ElemDbName)) { db.name = ReadString(xpp); } else if (name.equalsIgnoreCase(ElemDbNameChanged)) { db.nameChanged = ReadTime(xpp); } else if (name.equalsIgnoreCase(ElemDbDesc)) { db.description = ReadString(xpp); } else if (name.equalsIgnoreCase(ElemDbDescChanged)) { db.descriptionChanged = ReadTime(xpp); } else if (name.equalsIgnoreCase(ElemDbDefaultUser)) { db.defaultUserName = ReadString(xpp); } else if (name.equalsIgnoreCase(ElemDbDefaultUserChanged)) { db.defaultUserNameChanged = ReadTime(xpp); } else if (name.equalsIgnoreCase(ElemDbColor)) { // TODO: Add support to interpret the color if we want to allow changing the database // color db.color = ReadString(xpp); } else if (name.equalsIgnoreCase(ElemDbMntncHistoryDays)) { db.maintenanceHistoryDays = ReadUInt(xpp, DEFAULT_HISTORY_DAYS); } else if (name.equalsIgnoreCase(ElemDbKeyChanged)) { db.keyLastChanged = ReadTime(xpp); } else if (name.equalsIgnoreCase(ElemDbKeyChangeRec)) { db.keyChangeRecDays = ReadLong(xpp, -1); } else if (name.equalsIgnoreCase(ElemDbKeyChangeForce)) { db.keyChangeForceDays = ReadLong(xpp, -1); } else if (name.equalsIgnoreCase(ElemMemoryProt)) { return SwitchContext(ctx, KdbContext.MemoryProtection, xpp); } else if (name.equalsIgnoreCase(ElemCustomIcons)) { return SwitchContext(ctx, KdbContext.CustomIcons, xpp); } else if (name.equalsIgnoreCase(ElemRecycleBinEnabled)) { db.recycleBinEnabled = ReadBool(xpp, true); } else if (name.equalsIgnoreCase(ElemRecycleBinUuid)) { db.recycleBinUUID = ReadUuid(xpp); } else if (name.equalsIgnoreCase(ElemRecycleBinChanged)) { db.recycleBinChanged = ReadTime(xpp); } else if (name.equalsIgnoreCase(ElemEntryTemplatesGroup)) { db.entryTemplatesGroup = ReadUuid(xpp); } else if (name.equalsIgnoreCase(ElemEntryTemplatesGroupChanged)) { db.entryTemplatesGroupChanged = ReadTime(xpp); } else if (name.equalsIgnoreCase(ElemHistoryMaxItems)) { db.historyMaxItems = ReadInt(xpp, -1); } else if (name.equalsIgnoreCase(ElemHistoryMaxSize)) { db.historyMaxSize = ReadLong(xpp, -1); } else if (name.equalsIgnoreCase(ElemEntryTemplatesGroupChanged)) { db.entryTemplatesGroupChanged = ReadTime(xpp); } else if (name.equalsIgnoreCase(ElemLastSelectedGroup)) { db.lastSelectedGroup = ReadUuid(xpp); } else if (name.equalsIgnoreCase(ElemLastTopVisibleGroup)) { db.lastTopVisibleGroup = ReadUuid(xpp); } else if (name.equalsIgnoreCase(ElemBinaries)) { return SwitchContext(ctx, KdbContext.Binaries, xpp); } else if (name.equalsIgnoreCase(ElemCustomData)) { return SwitchContext(ctx, KdbContext.CustomData, xpp); } break; case MemoryProtection: if (name.equalsIgnoreCase(ElemProtTitle)) { db.memoryProtection.protectTitle = ReadBool(xpp, false); } else if (name.equalsIgnoreCase(ElemProtUserName)) { db.memoryProtection.protectUserName = ReadBool(xpp, false); } else if (name.equalsIgnoreCase(ElemProtPassword)) { db.memoryProtection.protectPassword = ReadBool(xpp, false); } else if (name.equalsIgnoreCase(ElemProtURL)) { db.memoryProtection.protectUrl = ReadBool(xpp, false); } else if (name.equalsIgnoreCase(ElemProtNotes)) { db.memoryProtection.protectNotes = ReadBool(xpp, false); } else if (name.equalsIgnoreCase(ElemProtAutoHide)) { db.memoryProtection.autoEnableVisualHiding = ReadBool(xpp, false); } else { ReadUnknown(xpp); } break; case CustomIcons: if (name.equalsIgnoreCase(ElemCustomIconItem)) { return SwitchContext(ctx, KdbContext.CustomIcon, xpp); } else { ReadUnknown(xpp); } break; case CustomIcon: if (name.equalsIgnoreCase(ElemCustomIconItemID)) { customIconID = ReadUuid(xpp); } else if (name.equalsIgnoreCase(ElemCustomIconItemData)) { String strData = ReadString(xpp); if (strData != null && strData.length() > 0) { customIconData = Base64Coder.decode(strData); } else { assert (false); } } else { ReadUnknown(xpp); } break; case Binaries: if (name.equalsIgnoreCase(ElemBinary)) { String key = xpp.getAttributeValue(null, AttrId); if (key != null) { ProtectedBinary pbData = ReadProtectedBinary(xpp); binPool.put(key, pbData); } else { ReadUnknown(xpp); } } else { ReadUnknown(xpp); } break; case CustomData: if (name.equalsIgnoreCase(ElemStringDictExItem)) { return SwitchContext(ctx, KdbContext.CustomDataItem, xpp); } else { ReadUnknown(xpp); } break; case CustomDataItem: if (name.equalsIgnoreCase(ElemKey)) { customDataKey = ReadString(xpp); } else if (name.equalsIgnoreCase(ElemValue)) { customDataValue = ReadString(xpp); } else { ReadUnknown(xpp); } break; case Root: if (name.equalsIgnoreCase(ElemGroup)) { assert (ctxGroups.size() == 0); if (ctxGroups.size() != 0) throw new IOException("Group list should be empty."); db.rootGroup = new PwGroupV4(); ctxGroups.push((PwGroupV4) db.rootGroup); ctxGroup = ctxGroups.peek(); return SwitchContext(ctx, KdbContext.Group, xpp); } else if (name.equalsIgnoreCase(ElemDeletedObjects)) { return SwitchContext(ctx, KdbContext.RootDeletedObjects, xpp); } else { ReadUnknown(xpp); } break; case Group: if (name.equalsIgnoreCase(ElemUuid)) { ctxGroup.uuid = ReadUuid(xpp); } else if (name.equalsIgnoreCase(ElemName)) { ctxGroup.name = ReadString(xpp); } else if (name.equalsIgnoreCase(ElemNotes)) { ctxGroup.notes = ReadString(xpp); } else if (name.equalsIgnoreCase(ElemIcon)) { ctxGroup.icon = db.iconFactory.getIcon((int) ReadUInt(xpp, 0)); } else if (name.equalsIgnoreCase(ElemCustomIconID)) { ctxGroup.customIcon = db.iconFactory.getIcon(ReadUuid(xpp)); } else if (name.equalsIgnoreCase(ElemTimes)) { return SwitchContext(ctx, KdbContext.GroupTimes, xpp); } else if (name.equalsIgnoreCase(ElemIsExpanded)) { ctxGroup.isExpanded = ReadBool(xpp, true); } else if (name.equalsIgnoreCase(ElemGroupDefaultAutoTypeSeq)) { ctxGroup.defaultAutoTypeSequence = ReadString(xpp); } else if (name.equalsIgnoreCase(ElemEnableAutoType)) { ctxGroup.enableAutoType = StringToBoolean(ReadString(xpp)); } else if (name.equalsIgnoreCase(ElemEnableSearching)) { ctxGroup.enableSearching = StringToBoolean(ReadString(xpp)); } else if (name.equalsIgnoreCase(ElemLastTopVisibleEntry)) { ctxGroup.lastTopVisibleEntry = ReadUuid(xpp); } else if (name.equalsIgnoreCase(ElemGroup)) { ctxGroup = new PwGroupV4(); ctxGroups.peek().AddGroup(ctxGroup, true); ctxGroups.push(ctxGroup); return SwitchContext(ctx, KdbContext.Group, xpp); } else if (name.equalsIgnoreCase(ElemEntry)) { ctxEntry = new PwEntryV4(); ctxGroup.AddEntry(ctxEntry, true); entryInHistory = false; return SwitchContext(ctx, KdbContext.Entry, xpp); } else { ReadUnknown(xpp); } break; case Entry: if (name.equalsIgnoreCase(ElemUuid)) { ctxEntry.setUUID(ReadUuid(xpp)); } else if (name.equalsIgnoreCase(ElemIcon)) { ctxEntry.icon = db.iconFactory.getIcon((int) ReadUInt(xpp, 0)); } else if (name.equalsIgnoreCase(ElemCustomIconID)) { ctxEntry.customIcon = db.iconFactory.getIcon(ReadUuid(xpp)); } else if (name.equalsIgnoreCase(ElemFgColor)) { ctxEntry.foregroundColor = ReadString(xpp); } else if (name.equalsIgnoreCase(ElemBgColor)) { ctxEntry.backgroupColor = ReadString(xpp); } else if (name.equalsIgnoreCase(ElemOverrideUrl)) { ctxEntry.overrideURL = ReadString(xpp); } else if (name.equalsIgnoreCase(ElemTags)) { ctxEntry.tags = ReadString(xpp); } else if (name.equalsIgnoreCase(ElemTimes)) { return SwitchContext(ctx, KdbContext.EntryTimes, xpp); } else if (name.equalsIgnoreCase(ElemString)) { return SwitchContext(ctx, KdbContext.EntryString, xpp); } else if (name.equalsIgnoreCase(ElemBinary)) { return SwitchContext(ctx, KdbContext.EntryBinary, xpp); } else if (name.equalsIgnoreCase(ElemAutoType)) { return SwitchContext(ctx, KdbContext.EntryAutoType, xpp); } else if (name.equalsIgnoreCase(ElemHistory)) { assert (!entryInHistory); if (!entryInHistory) { ctxHistoryBase = ctxEntry; return SwitchContext(ctx, KdbContext.EntryHistory, xpp); } else { ReadUnknown(xpp); } } else { ReadUnknown(xpp); } break; case GroupTimes: case EntryTimes: ITimeLogger tl; if (ctx == KdbContext.GroupTimes) { tl = ctxGroup; } else { tl = ctxEntry; } if (name.equalsIgnoreCase(ElemLastModTime)) { tl.setLastModificationTime(ReadTime(xpp)); } else if (name.equalsIgnoreCase(ElemCreationTime)) { tl.setCreationTime(ReadTime(xpp)); } else if (name.equalsIgnoreCase(ElemLastAccessTime)) { tl.setLastAccessTime(ReadTime(xpp)); } else if (name.equalsIgnoreCase(ElemExpiryTime)) { tl.setExpiryTime(ReadTime(xpp)); } else if (name.equalsIgnoreCase(ElemExpires)) { tl.setExpires(ReadBool(xpp, false)); } else if (name.equalsIgnoreCase(ElemUsageCount)) { tl.setUsageCount(ReadULong(xpp, 0)); } else if (name.equalsIgnoreCase(ElemLocationChanged)) { tl.setLocationChanged(ReadTime(xpp)); } else { ReadUnknown(xpp); } break; case EntryString: if (name.equalsIgnoreCase(ElemKey)) { ctxStringName = ReadString(xpp); } else if (name.equalsIgnoreCase(ElemValue)) { ctxStringValue = ReadProtectedString(xpp); } else { ReadUnknown(xpp); } break; case EntryBinary: if (name.equalsIgnoreCase(ElemKey)) { ctxBinaryName = ReadString(xpp); } else if (name.equalsIgnoreCase(ElemValue)) { ctxBinaryValue = ReadProtectedBinary(xpp); } break; case EntryAutoType: if (name.equalsIgnoreCase(ElemAutoTypeEnabled)) { ctxEntry.autoType.enabled = ReadBool(xpp, true); } else if (name.equalsIgnoreCase(ElemAutoTypeObfuscation)) { ctxEntry.autoType.obfuscationOptions = ReadUInt(xpp, 0); } else if (name.equalsIgnoreCase(ElemAutoTypeDefaultSeq)) { ctxEntry.autoType.defaultSequence = ReadString(xpp); } else if (name.equalsIgnoreCase(ElemAutoTypeItem)) { return SwitchContext(ctx, KdbContext.EntryAutoTypeItem, xpp); } else { ReadUnknown(xpp); } break; case EntryAutoTypeItem: if (name.equalsIgnoreCase(ElemWindow)) { ctxATName = ReadString(xpp); } else if (name.equalsIgnoreCase(ElemKeystrokeSequence)) { ctxATSeq = ReadString(xpp); } else { ReadUnknown(xpp); } break; case EntryHistory: if (name.equalsIgnoreCase(ElemEntry)) { ctxEntry = new PwEntryV4(); ctxHistoryBase.history.add(ctxEntry); entryInHistory = true; return SwitchContext(ctx, KdbContext.Entry, xpp); } else { ReadUnknown(xpp); } break; case RootDeletedObjects: if (name.equalsIgnoreCase(ElemDeletedObject)) { ctxDeletedObject = new PwDeletedObject(); db.deletedObjects.add(ctxDeletedObject); return SwitchContext(ctx, KdbContext.DeletedObject, xpp); } else { ReadUnknown(xpp); } break; case DeletedObject: if (name.equalsIgnoreCase(ElemUuid)) { ctxDeletedObject.uuid = ReadUuid(xpp); } else if (name.equalsIgnoreCase(ElemDeletionTime)) { ctxDeletedObject.setDeletionTime(ReadTime(xpp)); } else { ReadUnknown(xpp); } break; default: ReadUnknown(xpp); break; } return ctx; }