dissect.database 1.1.dev8__tar.gz → 1.1.dev10__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dissect_database-1.1.dev8/dissect.database.egg-info → dissect_database-1.1.dev10}/PKG-INFO +1 -1
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/ntds.py +1 -1
- dissect_database-1.1.dev10/dissect/database/sqlite3/encryption/__init__.py +10 -0
- dissect_database-1.1.dev10/dissect/database/sqlite3/encryption/sqlcipher/__init__.py +10 -0
- dissect_database-1.1.dev10/dissect/database/sqlite3/encryption/sqlcipher/exception.py +7 -0
- dissect_database-1.1.dev10/dissect/database/sqlite3/encryption/sqlcipher/sqlcipher.py +301 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/sqlite3/sqlite3.py +3 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10/dissect.database.egg-info}/PKG-INFO +1 -1
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect.database.egg-info/SOURCES.txt +5 -0
- dissect_database-1.1.dev10/tests/sqlite3/test_sqlcipher.py +96 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/COPYRIGHT +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/LICENSE +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/MANIFEST.in +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/README.md +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/__init__.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/bsd/__init__.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/bsd/c_db.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/bsd/c_db.pyi +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/bsd/db.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/bsd/tools/__init__.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/bsd/tools/c_rpm.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/bsd/tools/c_rpm.pyi +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/bsd/tools/rpm.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/__init__.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/btree.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/c_ese.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/c_ese.pyi +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/compression.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/cursor.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ese.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/exception.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/index.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/lcmapstring.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/__init__.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/c_ds.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/c_ds.pyi +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/c_pek.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/c_pek.pyi +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/c_sd.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/c_sd.pyi +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/database.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/__init__.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/applicationsettings.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/attributeschema.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/builtindomain.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/certificationauthority.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/classschema.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/classstore.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/computer.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/configuration.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/container.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/controlaccessright.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/crldistributionpoint.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/crossref.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/crossrefcontainer.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/dfsconfiguration.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/displayspecifier.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/dmd.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/dnsnode.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/dnszone.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/domain.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/domaindns.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/domainpolicy.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/dsuisettings.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/filelinktracking.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/foreignsecurityprincipal.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/group.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/grouppolicycontainer.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/infrastructureupdate.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/intersitetransport.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/intersitetransportcontainer.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/ipsecbase.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/ipsecfilter.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/ipsecisakmppolicy.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/ipsecnegotiationpolicy.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/ipsecnfa.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/ipsecpolicy.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/leaf.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/linktrackobjectmovetable.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/linktrackvolumetable.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/locality.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/lostandfound.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msauthz_centralaccesspolicies.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msauthz_centralaccessrules.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msdfsr_content.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msdfsr_contentset.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msdfsr_globalsettings.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msdfsr_localsettings.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msdfsr_member.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msdfsr_replicationgroup.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msdfsr_subscriber.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msdfsr_subscription.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msdfsr_topology.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msdns_serversettings.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msds_authnpolicies.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msds_authnpolicysilos.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msds_claimstransformationpolicies.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msds_claimtype.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msds_claimtypepropertybase.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msds_claimtypes.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msds_optionalfeature.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msds_passwordsettingscontainer.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msds_quotacontainer.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msds_resourceproperties.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msds_resourceproperty.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msds_resourcepropertylist.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msds_shadowprincipalcontainer.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msds_valuetype.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msimaging_psps.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/mskds_provserverconfiguration.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msmqenterprisesettings.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/mspki_enterpriseoid.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/mspki_privatekeyrecoveryagent.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/msspp_activationobjectscontainer.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/mstpm_informationobjectscontainer.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/ntdsconnection.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/ntdsdsa.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/ntdsservice.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/ntdssitesettings.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/ntfrssettings.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/object.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/organizationalperson.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/organizationalunit.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/person.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/physicallocation.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/pkicertificatetemplate.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/pkienrollmentservice.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/querypolicy.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/ridmanager.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/ridset.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/rpccontainer.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/rrasadministrationdictionary.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/samserver.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/secret.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/securityobject.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/server.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/serverscontainer.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/site.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/sitelink.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/sitescontainer.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/subnetcontainer.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/subschema.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/top.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/trusteddomain.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/user.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/pek.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/query.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/schema.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/sd.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/util.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/page.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/record.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/sorting_table.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/table.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/tools/__init__.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/tools/certlog.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/tools/impacket.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/tools/sru.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/tools/ual.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/util.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/exception.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/sqlite3/__init__.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/sqlite3/c_sqlite3.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/sqlite3/c_sqlite3.pyi +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/sqlite3/exception.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/sqlite3/util.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/sqlite3/wal.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect.database.egg-info/dependency_links.txt +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect.database.egg-info/requires.txt +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect.database.egg-info/top_level.txt +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/pyproject.toml +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/setup.cfg +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/__init__.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/_docs/Makefile +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/_docs/conf.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/_docs/index.rst +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/_tools/sqlite3/__init__.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/_tools/sqlite3/generate_sqlite.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/_util.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/bsd/__init__.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/bsd/conftest.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/bsd/test_db.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/bsd/test_rpm.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/conftest.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/ese/__init__.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/ese/conftest.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/ese/ntds/__init__.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/ese/ntds/conftest.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/ese/ntds/test_benchmark.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/ese/ntds/test_ntds.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/ese/ntds/test_pek.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/ese/ntds/test_query.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/ese/ntds/test_schema.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/ese/ntds/test_sd.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/ese/ntds/test_util.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/ese/test_cursor.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/ese/test_ese.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/ese/test_index.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/ese/test_page.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/ese/test_record.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/ese/test_table.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/ese/tools/__init__.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/ese/tools/test_certlog.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/ese/tools/test_sru.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/ese/tools/test_ual.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/sqlite3/__init__.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/sqlite3/conftest.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/sqlite3/test_default_values.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/sqlite3/test_row.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/sqlite3/test_sqlite3.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/sqlite3/test_util.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tests/sqlite3/test_wal.py +0 -0
- {dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/tox.ini +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dissect.database
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.dev10
|
|
4
4
|
Summary: A Dissect module implementing parsers for various database formats, including Berkeley DB, Microsofts Extensible Storage Engine (ESE) and SQLite3
|
|
5
5
|
Author-email: Dissect Team <dissect@fox-it.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -80,7 +80,7 @@ class NTDS:
|
|
|
80
80
|
|
|
81
81
|
def users(self) -> Iterator[User]:
|
|
82
82
|
"""Get all user objects from the database."""
|
|
83
|
-
yield from self.search(objectCategory="person")
|
|
83
|
+
yield from self.search(objectCategory="person", objectClass="user")
|
|
84
84
|
|
|
85
85
|
def computers(self) -> Iterator[Computer]:
|
|
86
86
|
"""Get all computer objects from the database."""
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import hmac
|
|
5
|
+
from functools import lru_cache
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import BinaryIO
|
|
8
|
+
|
|
9
|
+
from dissect.util.stream import AlignedStream
|
|
10
|
+
|
|
11
|
+
from dissect.database.sqlite3.encryption.sqlcipher.exception import SQLCipherError
|
|
12
|
+
from dissect.database.sqlite3.exception import InvalidDatabase
|
|
13
|
+
from dissect.database.sqlite3.sqlite3 import SQLITE3_HEADER_MAGIC, SQLite3
|
|
14
|
+
|
|
15
|
+
try:
|
|
16
|
+
from Crypto.Cipher import AES
|
|
17
|
+
|
|
18
|
+
HAS_CRYPTO = True
|
|
19
|
+
|
|
20
|
+
except ImportError:
|
|
21
|
+
HAS_CRYPTO = False
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class SQLCipher(SQLite3):
|
|
25
|
+
"""SQLCipher Community Edition implementation.
|
|
26
|
+
|
|
27
|
+
Instantiate with a subclass from :class:`SQLCipher4`, :class:`SQLCipher3`, :class:`SQLCipher2`
|
|
28
|
+
or :class:`SQLCipher1`.
|
|
29
|
+
|
|
30
|
+
Decrypts a SQLCipher database from the given path or file-like oject.
|
|
31
|
+
|
|
32
|
+
Example usage:
|
|
33
|
+
>>> from dissect.database.sqlite3.encryption import SQLCipher4
|
|
34
|
+
>>> db = SQLCipher4(Path("file.db"), "passphrase")
|
|
35
|
+
>>> row = db.table("MyTable").row(0)
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
fh (Path | BinaryIO): The path or file-like object to open.
|
|
39
|
+
passphrase (str | bytes): String or bytes passphrase.
|
|
40
|
+
salt (bytes): Optionally provide the 16-byte salt directly.
|
|
41
|
+
plaintext_header_size (int): Size of plaintext header to use.
|
|
42
|
+
page_size (int): Override size of each page.
|
|
43
|
+
kdf_iter (int): Override amount of KDF iterations.
|
|
44
|
+
kdf_algo (str | hashlib._Hash): Override KDF digest alrorithm.
|
|
45
|
+
hmac_algo (str | hashlib._Hash): Override HMAC digest algorithm.
|
|
46
|
+
no_kdf (bool): Disable KDF from passphrase, use as raw key.
|
|
47
|
+
verify_hmac (bool): Optionally verify digest of every page.
|
|
48
|
+
|
|
49
|
+
Raises:
|
|
50
|
+
SQLCipherError: If decryption failed using the provided arguments.
|
|
51
|
+
|
|
52
|
+
References:
|
|
53
|
+
- https://www.zetetic.net/sqlcipher/design/
|
|
54
|
+
- https://github.com/sqlcipher/sqlcipher
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
DEFAULT_PAGE_SIZE: int
|
|
58
|
+
DEFAULT_KDF_ITER: int
|
|
59
|
+
DEFAULT_KDF_ALGO: str
|
|
60
|
+
DEFAULT_HMAC_ALGO: str | None
|
|
61
|
+
|
|
62
|
+
def __init__(
|
|
63
|
+
self,
|
|
64
|
+
fh: Path | BinaryIO,
|
|
65
|
+
passphrase: str | bytes,
|
|
66
|
+
*,
|
|
67
|
+
salt: bytes | None = None,
|
|
68
|
+
plaintext_header_size: int | None = None,
|
|
69
|
+
page_size: int | None = None,
|
|
70
|
+
kdf_iter: int | None = None,
|
|
71
|
+
kdf_algo: str | None = None,
|
|
72
|
+
hmac_algo: str | None = None,
|
|
73
|
+
no_kdf: bool = False,
|
|
74
|
+
verify_hmac: bool = False,
|
|
75
|
+
):
|
|
76
|
+
if not HAS_CRYPTO:
|
|
77
|
+
raise RuntimeError("Missing dependency pycryptodome")
|
|
78
|
+
|
|
79
|
+
if isinstance(fh, Path):
|
|
80
|
+
cipher_fh = fh.open("rb")
|
|
81
|
+
cipher_path = fh
|
|
82
|
+
else:
|
|
83
|
+
cipher_fh = fh
|
|
84
|
+
cipher_path = None
|
|
85
|
+
|
|
86
|
+
self.cipher_fh = cipher_fh
|
|
87
|
+
self.cipher_path = cipher_path
|
|
88
|
+
self.cipher_page_size = page_size or self.DEFAULT_PAGE_SIZE
|
|
89
|
+
self.kdf_iter = kdf_iter or self.DEFAULT_KDF_ITER
|
|
90
|
+
self.kdf_algo = kdf_algo or self.DEFAULT_KDF_ALGO
|
|
91
|
+
self.hmac_algo = hmac_algo or self.DEFAULT_HMAC_ALGO
|
|
92
|
+
self.verify_hmac = verify_hmac
|
|
93
|
+
|
|
94
|
+
if not hasattr(self.cipher_fh, "read"):
|
|
95
|
+
raise ValueError("Provided file handle cannot be read from")
|
|
96
|
+
|
|
97
|
+
if isinstance(passphrase, str):
|
|
98
|
+
passphrase = passphrase.encode()
|
|
99
|
+
|
|
100
|
+
if not passphrase:
|
|
101
|
+
raise SQLCipherError("No passphrase provided")
|
|
102
|
+
|
|
103
|
+
if isinstance(self.hmac_algo, str):
|
|
104
|
+
self.hmac_algo = hashlib.new(self.hmac_algo)
|
|
105
|
+
|
|
106
|
+
if isinstance(self.kdf_algo, str):
|
|
107
|
+
self.kdf_algo = hashlib.new(self.kdf_algo)
|
|
108
|
+
|
|
109
|
+
# Part of the header can be plaintext. We can infer that or it can be passed upon initialization.
|
|
110
|
+
# https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_plaintext_header_size
|
|
111
|
+
if plaintext_header_size:
|
|
112
|
+
self.plaintext_header_size = plaintext_header_size
|
|
113
|
+
|
|
114
|
+
# The default and recommended plaintext header size is 32 bytes.
|
|
115
|
+
elif (header_or_salt := self.cipher_fh.read(16)) == SQLITE3_HEADER_MAGIC:
|
|
116
|
+
self.plaintext_header_size = 32
|
|
117
|
+
else:
|
|
118
|
+
self.plaintext_header_size = None
|
|
119
|
+
|
|
120
|
+
if self.plaintext_header_size and not salt:
|
|
121
|
+
raise SQLCipherError("Plaintext header has no salt, please provide salt manually")
|
|
122
|
+
|
|
123
|
+
self.salt = salt or header_or_salt
|
|
124
|
+
self.passphrase = passphrase
|
|
125
|
+
|
|
126
|
+
if no_kdf:
|
|
127
|
+
self.key = self.passphrase
|
|
128
|
+
else:
|
|
129
|
+
self.key = derive_key(
|
|
130
|
+
self.passphrase, self.salt, self.kdf_iter, self.kdf_algo.name if self.kdf_algo else None
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# The hmac key is derived using the raw or derived database key with it's own salt and two kdf iterations.
|
|
134
|
+
self.hmac_salt = bytes(i ^ 0x3A for i in self.salt)
|
|
135
|
+
self.hmac_key = derive_key(self.key, self.hmac_salt, 2, self.hmac_algo.name if self.hmac_algo else None)
|
|
136
|
+
|
|
137
|
+
# Initialize the decrypted SQLite3 stream as a file-like object and see if that works.
|
|
138
|
+
try:
|
|
139
|
+
super().__init__(self.stream(), wal=None, checkpoint=None)
|
|
140
|
+
except (InvalidDatabase, SQLCipherError) as e:
|
|
141
|
+
raise SQLCipherError("Decryption of SQLCipher database failed or is not a database") from e
|
|
142
|
+
|
|
143
|
+
# Sanity check to prevent further issues down the line.
|
|
144
|
+
if self.header.page_size != self.cipher_page_size or self.header.schema_format_number not in (1, 2, 3, 4):
|
|
145
|
+
raise SQLCipherError("Decryption of SQLCipher database failed or is not a database")
|
|
146
|
+
|
|
147
|
+
def __repr__(self) -> str:
|
|
148
|
+
return (
|
|
149
|
+
f"<{self.__class__.__name__} "
|
|
150
|
+
f"fh={self.cipher_path or self.cipher_fh} "
|
|
151
|
+
f"wal={self.wal} "
|
|
152
|
+
f"checkpoint={bool(self.checkpoint)} "
|
|
153
|
+
f"pages={self.header.page_count}>"
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
def close(self) -> None:
|
|
157
|
+
"""Close the database."""
|
|
158
|
+
super().close()
|
|
159
|
+
# Only close DB handle if we opened it using a path
|
|
160
|
+
if self.cipher_path is not None:
|
|
161
|
+
self.cipher_fh.close()
|
|
162
|
+
|
|
163
|
+
def stream(self) -> SQLCipherStream:
|
|
164
|
+
"""Create a transparent decryption stream."""
|
|
165
|
+
return SQLCipherStream(self)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class SQLCipherStream(AlignedStream):
|
|
169
|
+
"""Implements a transparent decryption stream for SQLCipher databases."""
|
|
170
|
+
|
|
171
|
+
def __init__(self, sqlcipher: SQLCipher):
|
|
172
|
+
super().__init__(None, sqlcipher.cipher_page_size)
|
|
173
|
+
|
|
174
|
+
self.fh = sqlcipher.cipher_fh
|
|
175
|
+
self.sqlcipher = sqlcipher
|
|
176
|
+
|
|
177
|
+
self._read_page = lru_cache(4096)(self._read_page)
|
|
178
|
+
|
|
179
|
+
def _read(self, offset: int, length: int) -> bytes:
|
|
180
|
+
"""Calculates which pages to read from based on the given offset and length. Returns decrypted bytes."""
|
|
181
|
+
|
|
182
|
+
start_page = offset // self.align
|
|
183
|
+
num_pages = length // self.align
|
|
184
|
+
return b"".join(
|
|
185
|
+
self._read_page(num + 1, self.sqlcipher.verify_hmac) for num in range(start_page, start_page + num_pages)
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
def _read_page(self, page_num: int, verify_hmac: bool = False) -> bytes:
|
|
189
|
+
"""Decrypt and read from the given SQLCipher page number.
|
|
190
|
+
|
|
191
|
+
References:
|
|
192
|
+
- https://github.com/sqlcipher/sqlcipher-tools/blob/master/decrypt.c
|
|
193
|
+
"""
|
|
194
|
+
|
|
195
|
+
if page_num < 1:
|
|
196
|
+
raise ValueError("The first page number is 1")
|
|
197
|
+
|
|
198
|
+
fh = self.sqlcipher.cipher_fh
|
|
199
|
+
page_size = self.sqlcipher.cipher_page_size
|
|
200
|
+
|
|
201
|
+
# Calculate the absolute offset in the SQLCipher file handle by multiplying the page number with
|
|
202
|
+
# the SQLCipher page size.
|
|
203
|
+
offset = (page_num - 1) * page_size
|
|
204
|
+
|
|
205
|
+
# Calculate size of the page iv (always 16 bytes) plus the hmac digest size.
|
|
206
|
+
hmac_algo = self.sqlcipher.hmac_algo
|
|
207
|
+
digest_size = hmac_algo.digest_size if hmac_algo else 0
|
|
208
|
+
align = 16 + digest_size
|
|
209
|
+
|
|
210
|
+
# Calculate the size of the encrypted data by substracting the iv and hmac size from the page size.
|
|
211
|
+
# The sum of the iv and hmac size needs to be adjusted to 16 byte blocks.
|
|
212
|
+
if align % 16 != 0:
|
|
213
|
+
align = (align + 15) & ~15
|
|
214
|
+
enc_size = page_size - align
|
|
215
|
+
|
|
216
|
+
# By default, the first page 'contains' the database salt (in place of SQLITE_HEAER_MAGIC) so we substract those
|
|
217
|
+
# first 16 bytes from the page size and update the ciphertext offset and size accordingly.
|
|
218
|
+
header_offset = 0
|
|
219
|
+
header = b""
|
|
220
|
+
if page_num == 1:
|
|
221
|
+
header_offset = self.sqlcipher.plaintext_header_size or 16
|
|
222
|
+
enc_size -= header_offset
|
|
223
|
+
offset += header_offset
|
|
224
|
+
|
|
225
|
+
# Prepare the plaintext header of the SQLite3 database if this is the first page, or read the plaintext
|
|
226
|
+
# header according to the plaintext_header_size variable.
|
|
227
|
+
if header_offset == 16:
|
|
228
|
+
header = SQLITE3_HEADER_MAGIC
|
|
229
|
+
elif header_offset:
|
|
230
|
+
fh.seek(0)
|
|
231
|
+
header = fh.read(header_offset)
|
|
232
|
+
|
|
233
|
+
# The last part of the page contains the iv and optionally a hmac digest.
|
|
234
|
+
fh.seek(offset + enc_size)
|
|
235
|
+
iv = fh.read(16)
|
|
236
|
+
page_hmac = fh.read(digest_size) if digest_size else None
|
|
237
|
+
|
|
238
|
+
fh.seek(offset)
|
|
239
|
+
ciphertext = fh.read(enc_size)
|
|
240
|
+
|
|
241
|
+
if len(iv) != 16 or not ciphertext:
|
|
242
|
+
raise EOFError
|
|
243
|
+
|
|
244
|
+
# Optionally verify the hmac signature with the page's ciphertext. Assumes default CIPHER_FLAG_LE_PGNO.
|
|
245
|
+
# https://github.com/sqlcipher/sqlcipher-tools/blob/master/verify.c
|
|
246
|
+
# https://github.com/sqlcipher/sqlcipher/blob/master/src/sqlcipher.c @ sqlcipher_page_hmac
|
|
247
|
+
if verify_hmac:
|
|
248
|
+
if not hmac_algo:
|
|
249
|
+
raise ValueError("verify_hmac is set to True but no HMAC algorithm is selected")
|
|
250
|
+
|
|
251
|
+
hmac_msg = ciphertext + iv + page_num.to_bytes(4, "little")
|
|
252
|
+
calc_hmac = hmac.digest(self.sqlcipher.hmac_key, hmac_msg, hmac_algo.name)
|
|
253
|
+
|
|
254
|
+
if calc_hmac != page_hmac:
|
|
255
|
+
raise SQLCipherError(
|
|
256
|
+
f"HMAC digest mismatch for page {page_num} (expected {page_hmac.hex()}, got {calc_hmac.hex()})"
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
# Decrypt the ciphertext using AES CBC and append null bytes so the plaintext aligns with the page size.
|
|
260
|
+
cipher = AES.new(self.sqlcipher.key, AES.MODE_CBC, iv)
|
|
261
|
+
plaintext = cipher.decrypt(ciphertext) + (align * b"\x00")
|
|
262
|
+
|
|
263
|
+
# Return the plaintext prepended by the optional plaintext header.
|
|
264
|
+
return header + plaintext
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
class SQLCipher4(SQLCipher):
|
|
268
|
+
DEFAULT_PAGE_SIZE = 4096
|
|
269
|
+
DEFAULT_KDF_ITER = 256_000
|
|
270
|
+
DEFAULT_KDF_ALGO = "SHA512"
|
|
271
|
+
DEFAULT_HMAC_ALGO = "SHA512"
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
class SQLCipher3(SQLCipher):
|
|
275
|
+
DEFAULT_PAGE_SIZE = 1024
|
|
276
|
+
DEFAULT_KDF_ITER = 64_000
|
|
277
|
+
DEFAULT_KDF_ALGO = "SHA1"
|
|
278
|
+
DEFAULT_HMAC_ALGO = "SHA1"
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
class SQLCipher2(SQLCipher):
|
|
282
|
+
DEFAULT_PAGE_SIZE = 1024
|
|
283
|
+
DEFAULT_KDF_ITER = 4000
|
|
284
|
+
DEFAULT_KDF_ALGO = "SHA1"
|
|
285
|
+
DEFAULT_HMAC_ALGO = "SHA1"
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
class SQLCipher1(SQLCipher):
|
|
289
|
+
DEFAULT_PAGE_SIZE = 1024
|
|
290
|
+
DEFAULT_KDF_ITER = 4000
|
|
291
|
+
DEFAULT_KDF_ALGO = "SHA1"
|
|
292
|
+
DEFAULT_HMAC_ALGO = None
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def derive_key(passphrase: bytes, salt: bytes, kdf_iter: int, kdf_algo: str | None) -> bytes:
|
|
296
|
+
"""Derive the database key as SQLCipher would using PBKDF2."""
|
|
297
|
+
|
|
298
|
+
if not kdf_iter or not kdf_algo:
|
|
299
|
+
return passphrase
|
|
300
|
+
|
|
301
|
+
return hashlib.pbkdf2_hmac(kdf_algo, passphrase, salt, kdf_iter, 32)
|
{dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/sqlite3/sqlite3.py
RENAMED
|
@@ -131,6 +131,9 @@ class SQLite3:
|
|
|
131
131
|
|
|
132
132
|
self.page = lru_cache(256)(self.page)
|
|
133
133
|
|
|
134
|
+
def __repr__(self) -> str:
|
|
135
|
+
return f"<SQLite3 path={self.path!s} fh={self.fh!s} wal={self.wal!s} checkpoint={bool(self.checkpoint)!r} pages={self.header.page_count!r}>" # noqa: E501
|
|
136
|
+
|
|
134
137
|
def __enter__(self) -> Self:
|
|
135
138
|
"""Return ``self`` upon entering the runtime context."""
|
|
136
139
|
return self
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dissect.database
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.dev10
|
|
4
4
|
Summary: A Dissect module implementing parsers for various database formats, including Berkeley DB, Microsofts Extensible Storage Engine (ESE) and SQLite3
|
|
5
5
|
Author-email: Dissect Team <dissect@fox-it.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
{dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect.database.egg-info/SOURCES.txt
RENAMED
|
@@ -164,6 +164,10 @@ dissect/database/sqlite3/exception.py
|
|
|
164
164
|
dissect/database/sqlite3/sqlite3.py
|
|
165
165
|
dissect/database/sqlite3/util.py
|
|
166
166
|
dissect/database/sqlite3/wal.py
|
|
167
|
+
dissect/database/sqlite3/encryption/__init__.py
|
|
168
|
+
dissect/database/sqlite3/encryption/sqlcipher/__init__.py
|
|
169
|
+
dissect/database/sqlite3/encryption/sqlcipher/exception.py
|
|
170
|
+
dissect/database/sqlite3/encryption/sqlcipher/sqlcipher.py
|
|
167
171
|
tests/__init__.py
|
|
168
172
|
tests/_util.py
|
|
169
173
|
tests/conftest.py
|
|
@@ -201,6 +205,7 @@ tests/sqlite3/__init__.py
|
|
|
201
205
|
tests/sqlite3/conftest.py
|
|
202
206
|
tests/sqlite3/test_default_values.py
|
|
203
207
|
tests/sqlite3/test_row.py
|
|
208
|
+
tests/sqlite3/test_sqlcipher.py
|
|
204
209
|
tests/sqlite3/test_sqlite3.py
|
|
205
210
|
tests/sqlite3/test_util.py
|
|
206
211
|
tests/sqlite3/test_wal.py
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from dissect.database.sqlite3.encryption.sqlcipher.exception import SQLCipherError
|
|
8
|
+
from dissect.database.sqlite3.encryption.sqlcipher.sqlcipher import SQLCipher1, SQLCipher2, SQLCipher3, SQLCipher4
|
|
9
|
+
from tests._util import absolute_path
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from collections.abc import Callable
|
|
13
|
+
|
|
14
|
+
from dissect.database.sqlite3.sqlite3 import SQLite3
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _assert_sqlite_db(sqlite: SQLite3) -> None:
|
|
18
|
+
table = sqlite.table("Movies")
|
|
19
|
+
assert table.sql == (
|
|
20
|
+
'CREATE TABLE "Movies" (\n'
|
|
21
|
+
'\t"ID"\tINTEGER,\n'
|
|
22
|
+
'\t"Title"\tTEXT NOT NULL,\n'
|
|
23
|
+
'\t"Year"\tINTEGER NOT NULL,\n'
|
|
24
|
+
'\t"Director"\tTEXT NOT NULL,\n'
|
|
25
|
+
'\t"Rating"\tINTEGER,\n'
|
|
26
|
+
'\tPRIMARY KEY("ID" AUTOINCREMENT)\n'
|
|
27
|
+
")"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
movies = list(table.rows())
|
|
31
|
+
assert len(movies) == 11
|
|
32
|
+
|
|
33
|
+
assert movies[-1].ID == 11
|
|
34
|
+
assert movies[-1].Title == "The Good, the Bad and the Ugly"
|
|
35
|
+
assert movies[-1].Year == 1966
|
|
36
|
+
assert movies[-1].Director == "Sergio Leone"
|
|
37
|
+
assert movies[-1].Rating == 8.8
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@pytest.mark.parametrize(
|
|
41
|
+
("cipher", "kwargs", "path_str"),
|
|
42
|
+
[
|
|
43
|
+
# Defaults per major version
|
|
44
|
+
pytest.param(SQLCipher4, {"verify_hmac": True}, "aes256_hmac_sha512_kdf_256000.sqlite", id="version-4-default"),
|
|
45
|
+
pytest.param(SQLCipher3, {"verify_hmac": True}, "aes256_hmac_sha1_kdf_64000.sqlite", id="version-3-default"),
|
|
46
|
+
pytest.param(SQLCipher2, {"verify_hmac": True}, "aes256_hmac_sha1_kdf_4000.sqlite", id="version-2-default"),
|
|
47
|
+
pytest.param(SQLCipher1, {}, "aes256_hmac_none_kdf_4000.sqlite", id="version-1-default"),
|
|
48
|
+
# Custom parameters
|
|
49
|
+
pytest.param(
|
|
50
|
+
SQLCipher4,
|
|
51
|
+
{
|
|
52
|
+
"page_size": 8192,
|
|
53
|
+
"hmac_algo": "sha256",
|
|
54
|
+
"kdf_algo": "sha1",
|
|
55
|
+
"kdf_iter": 1337,
|
|
56
|
+
},
|
|
57
|
+
"aes256_hmac_sha256_kdf_sha1_1337_page_8kb.sqlite",
|
|
58
|
+
id="version-4-custom-hmac-sha256-kdf-sha1-1337-page-8kb",
|
|
59
|
+
),
|
|
60
|
+
],
|
|
61
|
+
)
|
|
62
|
+
def test_decrypt_community(cipher: Callable, path_str: str, kwargs: dict) -> None:
|
|
63
|
+
"""Test if we can parse a SQLCipher (4.5.6 community) encrypted database."""
|
|
64
|
+
|
|
65
|
+
path = absolute_path("_data/sqlite3/encryption/sqlcipher/" + path_str)
|
|
66
|
+
|
|
67
|
+
with pytest.raises(SQLCipherError, match="Decryption of SQLCipher database failed"):
|
|
68
|
+
cipher(path, "invalid passphrase", **kwargs)
|
|
69
|
+
|
|
70
|
+
# Test context manager
|
|
71
|
+
with cipher(path, "passphrase", **kwargs) as sqlcipher:
|
|
72
|
+
assert sqlcipher.stream().read(20) in (
|
|
73
|
+
b"SQLite format 3\x00\x04\x00\x01\x01", # 1024
|
|
74
|
+
b"SQLite format 3\x00\x10\x00\x01\x01", # 4096
|
|
75
|
+
b"SQLite format 3\x00\x20\x00\x01\x01", # 8192
|
|
76
|
+
)
|
|
77
|
+
_assert_sqlite_db(sqlcipher)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_decrypt_community_plaintext_header() -> None:
|
|
81
|
+
"""Test if we can parse and decrypt a SQLCipher 4.5.6 database with a 32-byte plaintext header."""
|
|
82
|
+
|
|
83
|
+
path = absolute_path("_data/sqlite3/encryption/sqlcipher/aes256_hmac_sha512_kdf_256000_plain_header.sqlite")
|
|
84
|
+
salt = bytes.fromhex("01010101010101010101010101010101")
|
|
85
|
+
|
|
86
|
+
with pytest.raises(SQLCipherError, match="No passphrase provided"):
|
|
87
|
+
SQLCipher4(path, "")
|
|
88
|
+
|
|
89
|
+
with pytest.raises(SQLCipherError, match="Plaintext header has no salt, please provide salt manually"):
|
|
90
|
+
SQLCipher4(path, "invalid passphrase")
|
|
91
|
+
|
|
92
|
+
with pytest.raises(SQLCipherError, match="Decryption of SQLCipher database failed"):
|
|
93
|
+
SQLCipher4(path, "invalid passphrase", salt=salt)
|
|
94
|
+
|
|
95
|
+
sqlcipher = SQLCipher4(path, "passphrase", salt=salt, verify_hmac=True)
|
|
96
|
+
_assert_sqlite_db(sqlcipher)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/bsd/tools/__init__.py
RENAMED
|
File without changes
|
{dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/bsd/tools/c_rpm.py
RENAMED
|
File without changes
|
{dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/bsd/tools/c_rpm.pyi
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/compression.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/lcmapstring.py
RENAMED
|
File without changes
|
{dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/c_pek.pyi
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/database.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/dmd.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/domain.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dissect_database-1.1.dev8 → dissect_database-1.1.dev10}/dissect/database/ese/ntds/objects/group.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|