From 6e8261886b16256e67fda5928e909fbac520fe05 Mon Sep 17 00:00:00 2001 From: Leumor <116955025+leumor@users.noreply.github.com> Date: Mon, 23 Mar 2026 23:08:20 +0000 Subject: [PATCH] refactor(modules): Extract foundation crypto keys Move the crypt and keys main-source boundary into a new internal leaf module and re-home the remaining support blockers needed for the extraction. Keep the new BucketTools bridge compatible with inherited file-bucket split methods and pooled RAF restore, and add focused regression tests for those reflective paths. --- build.gradle.kts | 2 + foundation-crypto-keys/build.gradle.kts | 17 + .../gradle/owned-output-patterns.txt | 7 + .../network/crypta/crypt/AEADCryptBucket.java | 0 .../network/crypta/crypt/AEADInputStream.java | 0 .../crypta/crypt/AEADOutputStream.java | 0 .../AEADVerificationFailedException.java | 0 .../network/crypta/crypt/BlockCipher.java | 0 .../network/crypta/crypt/BlockCiphers.java | 0 .../crypta/crypt/BucketInputStreams.java | 0 .../crypta/crypt/CRCChecksumChecker.java | 0 .../network/crypta/crypt/CTRBlockCipher.java | 0 .../network/crypta/crypt/ChecksumChecker.java | 0 .../crypta/crypt/ChecksumFailedException.java | 0 .../crypta/crypt/ChecksumOutputStream.java | 0 .../network/crypta/crypt/CryptByteBuffer.java | 0 .../crypta/crypt/CryptByteBufferType.java | 0 .../crypta/crypt/CryptFormatException.java | 0 .../network/crypta/crypt/CryptoElement.java | 0 .../java/network/crypta/crypt/CryptoKey.java | 0 .../network/crypta/crypt/CryptoRandoms.java | 0 .../crypta/crypt/CryptoResumeContext.java | 0 .../crypta/crypt/CryptoResumeContexts.java | 0 .../java/network/crypta/crypt/DSAGroup.java | 0 .../network/crypta/crypt/DSAPrivateKey.java | 0 .../network/crypta/crypt/DSAPublicKey.java | 0 .../crypta/crypt/DummyRandomSource.java | 0 .../main/java/network/crypta/crypt/ECDH.java | 0 .../crypta/crypt/ECDHLightContext.java | 0 .../main/java/network/crypta/crypt/ECDSA.java | 0 .../crypta/crypt/Ed2MessageDigest.java | 0 .../crypt/EncryptedRandomAccessBucket.java | 0 .../crypt/EncryptedRandomAccessBuffer.java | 0 .../EncryptedRandomAccessBufferType.java | 0 .../network/crypta/crypt/EntropySource.java | 0 .../java/network/crypta/crypt/Global.java | 0 .../main/java/network/crypta/crypt/HMAC.java | 0 .../main/java/network/crypta/crypt/Hash.java | 0 .../java/network/crypta/crypt/HashResult.java | 0 .../java/network/crypta/crypt/HashType.java | 0 .../java/network/crypta/crypt/JceLoader.java | 0 .../crypt/KeyAgreementSchemeContext.java | 0 .../network/crypta/crypt/KeyGenUtils.java | 0 .../network/crypta/crypt/KeyPairType.java | 0 .../java/network/crypta/crypt/KeyType.java | 0 .../java/network/crypta/crypt/MACType.java | 0 .../network/crypta/crypt/MasterSecret.java | 0 .../network/crypta/crypt/MessageAuthCode.java | 0 .../crypta/crypt/MultiHashDigester.java | 0 .../crypta/crypt/MultiHashInputStream.java | 0 .../crypta/crypt/MultiHashOutputStream.java | 0 .../java/network/crypta/crypt/PCFBMode.java | 0 .../crypta/crypt/PersistentRandomSource.java | 0 .../network/crypta/crypt/RandomSource.java | 0 .../java/network/crypta/crypt/SHA256.java | 0 .../crypt/UnsupportedCipherException.java | 0 .../crypt/UnsupportedTypeException.java | 0 .../main/java/network/crypta/crypt/Util.java | 0 .../java/network/crypta/crypt/Yarrow.java | 0 .../crypta/crypt/ciphers/Rijndael.java | 0 .../crypt/ciphers/RijndaelAlgorithm.java | 0 .../crypta/crypt/ciphers/package-info.java | 0 .../network/crypta/crypt/package-info.java | 0 .../network/crypta/keys/BaseClientKey.java | 0 .../crypta/keys/BlockEncodeParams.java | 0 .../java/network/crypta/keys/CHKBlock.java | 0 .../crypta/keys/CHKDecodeException.java | 0 .../crypta/keys/CHKEncodeException.java | 0 .../crypta/keys/CHKVerifyException.java | 0 .../java/network/crypta/keys/ChkKeySizes.java | 0 .../java/network/crypta/keys/ClientCHK.java | 0 .../network/crypta/keys/ClientCHKBlock.java | 0 .../keys/ClientCHKEncodeAlgorithms.java | 0 .../crypta/keys/ClientCHKEncodeParams.java | 0 .../crypta/keys/ClientCHKEncodePayload.java | 0 .../java/network/crypta/keys/ClientKSK.java | 0 .../java/network/crypta/keys/ClientKey.java | 0 .../network/crypta/keys/ClientKeyBlock.java | 0 .../java/network/crypta/keys/ClientSSK.java | 0 .../network/crypta/keys/ClientSSKBlock.java | 0 .../crypta/keys/CompressionLimits.java | 0 .../crypta/keys/DecompressionParams.java | 0 .../java/network/crypta/keys/FreenetURI.java | 0 .../crypta/keys/InsertableClientSSK.java | 0 .../network/crypta/keys/InsertableUSK.java | 0 .../main/java/network/crypta/keys/Key.java | 0 .../java/network/crypta/keys/KeyBlock.java | 0 .../crypta/keys/KeyDecodeException.java | 0 .../crypta/keys/KeyEncodeException.java | 0 .../crypta/keys/KeyVerifyException.java | 0 .../java/network/crypta/keys/NodeCHK.java | 0 .../java/network/crypta/keys/NodeSSK.java | 0 .../crypta/keys/PubkeyVerifyException.java | 0 .../java/network/crypta/keys/SSKBlock.java | 0 .../crypta/keys/SSKDecodeException.java | 0 .../crypta/keys/SSKEncodeException.java | 0 .../crypta/keys/SSKVerifyException.java | 0 .../network/crypta/keys/TooBigException.java | 0 .../main/java/network/crypta/keys/USK.java | 0 .../network/crypta/keys/package-info.java | 0 .../crypta/support/io/BucketTools.java | 416 ++++++++++++++++-- .../support/io/PrependLengthOutputStream.java | 0 foundation-support/build.gradle.kts | 1 + .../gradle/owned-output-patterns.txt | 5 + .../java/network/crypta/support/Loader.java | 0 .../support/SimpleReadOnlyArrayBucket.java | 0 .../crypta/support/StringValidityChecker.java | 0 .../crypta/support/io/FilenameGenerator.java | 27 +- .../crypta/support/io/FilenameSanitizer.java | 0 settings.gradle.kts | 1 + .../crypta/support/io/BucketToolsTest.java | 33 ++ .../io/PooledFileRandomAccessBufferTest.java | 36 ++ 112 files changed, 497 insertions(+), 48 deletions(-) create mode 100644 foundation-crypto-keys/build.gradle.kts create mode 100644 foundation-crypto-keys/gradle/owned-output-patterns.txt rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/AEADCryptBucket.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/AEADInputStream.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/AEADOutputStream.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/AEADVerificationFailedException.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/BlockCipher.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/BlockCiphers.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/BucketInputStreams.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/CRCChecksumChecker.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/CTRBlockCipher.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/ChecksumChecker.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/ChecksumFailedException.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/ChecksumOutputStream.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/CryptByteBuffer.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/CryptByteBufferType.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/CryptFormatException.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/CryptoElement.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/CryptoKey.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/CryptoRandoms.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/CryptoResumeContext.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/CryptoResumeContexts.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/DSAGroup.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/DSAPrivateKey.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/DSAPublicKey.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/DummyRandomSource.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/ECDH.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/ECDHLightContext.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/ECDSA.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/Ed2MessageDigest.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/EncryptedRandomAccessBucket.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/EncryptedRandomAccessBuffer.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/EncryptedRandomAccessBufferType.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/EntropySource.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/Global.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/HMAC.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/Hash.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/HashResult.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/HashType.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/JceLoader.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/KeyAgreementSchemeContext.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/KeyGenUtils.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/KeyPairType.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/KeyType.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/MACType.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/MasterSecret.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/MessageAuthCode.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/MultiHashDigester.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/MultiHashInputStream.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/MultiHashOutputStream.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/PCFBMode.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/PersistentRandomSource.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/RandomSource.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/SHA256.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/UnsupportedCipherException.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/UnsupportedTypeException.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/Util.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/Yarrow.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/ciphers/Rijndael.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/ciphers/RijndaelAlgorithm.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/ciphers/package-info.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/crypt/package-info.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/BaseClientKey.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/BlockEncodeParams.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/CHKBlock.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/CHKDecodeException.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/CHKEncodeException.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/CHKVerifyException.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/ChkKeySizes.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/ClientCHK.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/ClientCHKBlock.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/ClientCHKEncodeAlgorithms.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/ClientCHKEncodeParams.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/ClientCHKEncodePayload.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/ClientKSK.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/ClientKey.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/ClientKeyBlock.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/ClientSSK.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/ClientSSKBlock.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/CompressionLimits.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/DecompressionParams.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/FreenetURI.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/InsertableClientSSK.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/InsertableUSK.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/Key.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/KeyBlock.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/KeyDecodeException.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/KeyEncodeException.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/KeyVerifyException.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/NodeCHK.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/NodeSSK.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/PubkeyVerifyException.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/SSKBlock.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/SSKDecodeException.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/SSKEncodeException.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/SSKVerifyException.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/TooBigException.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/USK.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/keys/package-info.java (100%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/support/io/BucketTools.java (66%) rename {src => foundation-crypto-keys/src}/main/java/network/crypta/support/io/PrependLengthOutputStream.java (100%) rename {src => foundation-support/src}/main/java/network/crypta/support/Loader.java (100%) rename {src => foundation-support/src}/main/java/network/crypta/support/SimpleReadOnlyArrayBucket.java (100%) rename {src => foundation-support/src}/main/java/network/crypta/support/StringValidityChecker.java (100%) rename {src => foundation-support/src}/main/java/network/crypta/support/io/FilenameGenerator.java (93%) rename {src => foundation-support/src}/main/java/network/crypta/support/io/FilenameSanitizer.java (100%) diff --git a/build.gradle.kts b/build.gradle.kts index 98ab1e9fe5c..a798ee68ea6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -18,6 +18,7 @@ val internalLeafProjects = listOf( project(":foundation-support"), project(":foundation-store-contracts"), + project(":foundation-crypto-keys"), project(":foundation-config"), project(":foundation-fs"), project(":foundation-compat"), @@ -37,6 +38,7 @@ dependencies { // implementation implementation(project(":foundation-support")) implementation(project(":foundation-store-contracts")) + implementation(project(":foundation-crypto-keys")) implementation(project(":foundation-config")) implementation(project(":foundation-fs")) implementation(project(":foundation-compat")) diff --git a/foundation-crypto-keys/build.gradle.kts b/foundation-crypto-keys/build.gradle.kts new file mode 100644 index 00000000000..0aa0ff82d40 --- /dev/null +++ b/foundation-crypto-keys/build.gradle.kts @@ -0,0 +1,17 @@ +plugins { + id("cryptad.java-kotlin-conventions") + id("cryptad.spotless") + `java-library` +} + +version = rootProject.version + +dependencies { + api(project(":foundation-support")) + api(project(":foundation-store-contracts")) + implementation(project(":foundation-fs")) + implementation(project(":thirdparty-legacy")) + implementation(libs.bcprov) + implementation(libs.slf4jApi) + compileOnly(libs.jetbrainsAnnotations) +} diff --git a/foundation-crypto-keys/gradle/owned-output-patterns.txt b/foundation-crypto-keys/gradle/owned-output-patterns.txt new file mode 100644 index 00000000000..57aae631005 --- /dev/null +++ b/foundation-crypto-keys/gradle/owned-output-patterns.txt @@ -0,0 +1,7 @@ +# Aggregated main outputs that must be pruned on non-clean builds because :foundation-crypto-keys owns them now. +# Keep this list limited to the crypto/key boundary plus the small adjacent support-io helpers moved with it. + +network/crypta/crypt/** +network/crypta/keys/** +network/crypta/support/io/BucketTools* +network/crypta/support/io/PrependLengthOutputStream* diff --git a/src/main/java/network/crypta/crypt/AEADCryptBucket.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/AEADCryptBucket.java similarity index 100% rename from src/main/java/network/crypta/crypt/AEADCryptBucket.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/AEADCryptBucket.java diff --git a/src/main/java/network/crypta/crypt/AEADInputStream.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/AEADInputStream.java similarity index 100% rename from src/main/java/network/crypta/crypt/AEADInputStream.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/AEADInputStream.java diff --git a/src/main/java/network/crypta/crypt/AEADOutputStream.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/AEADOutputStream.java similarity index 100% rename from src/main/java/network/crypta/crypt/AEADOutputStream.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/AEADOutputStream.java diff --git a/src/main/java/network/crypta/crypt/AEADVerificationFailedException.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/AEADVerificationFailedException.java similarity index 100% rename from src/main/java/network/crypta/crypt/AEADVerificationFailedException.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/AEADVerificationFailedException.java diff --git a/src/main/java/network/crypta/crypt/BlockCipher.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/BlockCipher.java similarity index 100% rename from src/main/java/network/crypta/crypt/BlockCipher.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/BlockCipher.java diff --git a/src/main/java/network/crypta/crypt/BlockCiphers.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/BlockCiphers.java similarity index 100% rename from src/main/java/network/crypta/crypt/BlockCiphers.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/BlockCiphers.java diff --git a/src/main/java/network/crypta/crypt/BucketInputStreams.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/BucketInputStreams.java similarity index 100% rename from src/main/java/network/crypta/crypt/BucketInputStreams.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/BucketInputStreams.java diff --git a/src/main/java/network/crypta/crypt/CRCChecksumChecker.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/CRCChecksumChecker.java similarity index 100% rename from src/main/java/network/crypta/crypt/CRCChecksumChecker.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/CRCChecksumChecker.java diff --git a/src/main/java/network/crypta/crypt/CTRBlockCipher.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/CTRBlockCipher.java similarity index 100% rename from src/main/java/network/crypta/crypt/CTRBlockCipher.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/CTRBlockCipher.java diff --git a/src/main/java/network/crypta/crypt/ChecksumChecker.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/ChecksumChecker.java similarity index 100% rename from src/main/java/network/crypta/crypt/ChecksumChecker.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/ChecksumChecker.java diff --git a/src/main/java/network/crypta/crypt/ChecksumFailedException.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/ChecksumFailedException.java similarity index 100% rename from src/main/java/network/crypta/crypt/ChecksumFailedException.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/ChecksumFailedException.java diff --git a/src/main/java/network/crypta/crypt/ChecksumOutputStream.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/ChecksumOutputStream.java similarity index 100% rename from src/main/java/network/crypta/crypt/ChecksumOutputStream.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/ChecksumOutputStream.java diff --git a/src/main/java/network/crypta/crypt/CryptByteBuffer.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/CryptByteBuffer.java similarity index 100% rename from src/main/java/network/crypta/crypt/CryptByteBuffer.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/CryptByteBuffer.java diff --git a/src/main/java/network/crypta/crypt/CryptByteBufferType.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/CryptByteBufferType.java similarity index 100% rename from src/main/java/network/crypta/crypt/CryptByteBufferType.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/CryptByteBufferType.java diff --git a/src/main/java/network/crypta/crypt/CryptFormatException.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/CryptFormatException.java similarity index 100% rename from src/main/java/network/crypta/crypt/CryptFormatException.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/CryptFormatException.java diff --git a/src/main/java/network/crypta/crypt/CryptoElement.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/CryptoElement.java similarity index 100% rename from src/main/java/network/crypta/crypt/CryptoElement.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/CryptoElement.java diff --git a/src/main/java/network/crypta/crypt/CryptoKey.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/CryptoKey.java similarity index 100% rename from src/main/java/network/crypta/crypt/CryptoKey.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/CryptoKey.java diff --git a/src/main/java/network/crypta/crypt/CryptoRandoms.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/CryptoRandoms.java similarity index 100% rename from src/main/java/network/crypta/crypt/CryptoRandoms.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/CryptoRandoms.java diff --git a/src/main/java/network/crypta/crypt/CryptoResumeContext.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/CryptoResumeContext.java similarity index 100% rename from src/main/java/network/crypta/crypt/CryptoResumeContext.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/CryptoResumeContext.java diff --git a/src/main/java/network/crypta/crypt/CryptoResumeContexts.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/CryptoResumeContexts.java similarity index 100% rename from src/main/java/network/crypta/crypt/CryptoResumeContexts.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/CryptoResumeContexts.java diff --git a/src/main/java/network/crypta/crypt/DSAGroup.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/DSAGroup.java similarity index 100% rename from src/main/java/network/crypta/crypt/DSAGroup.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/DSAGroup.java diff --git a/src/main/java/network/crypta/crypt/DSAPrivateKey.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/DSAPrivateKey.java similarity index 100% rename from src/main/java/network/crypta/crypt/DSAPrivateKey.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/DSAPrivateKey.java diff --git a/src/main/java/network/crypta/crypt/DSAPublicKey.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/DSAPublicKey.java similarity index 100% rename from src/main/java/network/crypta/crypt/DSAPublicKey.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/DSAPublicKey.java diff --git a/src/main/java/network/crypta/crypt/DummyRandomSource.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/DummyRandomSource.java similarity index 100% rename from src/main/java/network/crypta/crypt/DummyRandomSource.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/DummyRandomSource.java diff --git a/src/main/java/network/crypta/crypt/ECDH.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/ECDH.java similarity index 100% rename from src/main/java/network/crypta/crypt/ECDH.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/ECDH.java diff --git a/src/main/java/network/crypta/crypt/ECDHLightContext.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/ECDHLightContext.java similarity index 100% rename from src/main/java/network/crypta/crypt/ECDHLightContext.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/ECDHLightContext.java diff --git a/src/main/java/network/crypta/crypt/ECDSA.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/ECDSA.java similarity index 100% rename from src/main/java/network/crypta/crypt/ECDSA.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/ECDSA.java diff --git a/src/main/java/network/crypta/crypt/Ed2MessageDigest.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/Ed2MessageDigest.java similarity index 100% rename from src/main/java/network/crypta/crypt/Ed2MessageDigest.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/Ed2MessageDigest.java diff --git a/src/main/java/network/crypta/crypt/EncryptedRandomAccessBucket.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/EncryptedRandomAccessBucket.java similarity index 100% rename from src/main/java/network/crypta/crypt/EncryptedRandomAccessBucket.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/EncryptedRandomAccessBucket.java diff --git a/src/main/java/network/crypta/crypt/EncryptedRandomAccessBuffer.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/EncryptedRandomAccessBuffer.java similarity index 100% rename from src/main/java/network/crypta/crypt/EncryptedRandomAccessBuffer.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/EncryptedRandomAccessBuffer.java diff --git a/src/main/java/network/crypta/crypt/EncryptedRandomAccessBufferType.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/EncryptedRandomAccessBufferType.java similarity index 100% rename from src/main/java/network/crypta/crypt/EncryptedRandomAccessBufferType.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/EncryptedRandomAccessBufferType.java diff --git a/src/main/java/network/crypta/crypt/EntropySource.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/EntropySource.java similarity index 100% rename from src/main/java/network/crypta/crypt/EntropySource.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/EntropySource.java diff --git a/src/main/java/network/crypta/crypt/Global.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/Global.java similarity index 100% rename from src/main/java/network/crypta/crypt/Global.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/Global.java diff --git a/src/main/java/network/crypta/crypt/HMAC.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/HMAC.java similarity index 100% rename from src/main/java/network/crypta/crypt/HMAC.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/HMAC.java diff --git a/src/main/java/network/crypta/crypt/Hash.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/Hash.java similarity index 100% rename from src/main/java/network/crypta/crypt/Hash.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/Hash.java diff --git a/src/main/java/network/crypta/crypt/HashResult.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/HashResult.java similarity index 100% rename from src/main/java/network/crypta/crypt/HashResult.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/HashResult.java diff --git a/src/main/java/network/crypta/crypt/HashType.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/HashType.java similarity index 100% rename from src/main/java/network/crypta/crypt/HashType.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/HashType.java diff --git a/src/main/java/network/crypta/crypt/JceLoader.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/JceLoader.java similarity index 100% rename from src/main/java/network/crypta/crypt/JceLoader.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/JceLoader.java diff --git a/src/main/java/network/crypta/crypt/KeyAgreementSchemeContext.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/KeyAgreementSchemeContext.java similarity index 100% rename from src/main/java/network/crypta/crypt/KeyAgreementSchemeContext.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/KeyAgreementSchemeContext.java diff --git a/src/main/java/network/crypta/crypt/KeyGenUtils.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/KeyGenUtils.java similarity index 100% rename from src/main/java/network/crypta/crypt/KeyGenUtils.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/KeyGenUtils.java diff --git a/src/main/java/network/crypta/crypt/KeyPairType.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/KeyPairType.java similarity index 100% rename from src/main/java/network/crypta/crypt/KeyPairType.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/KeyPairType.java diff --git a/src/main/java/network/crypta/crypt/KeyType.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/KeyType.java similarity index 100% rename from src/main/java/network/crypta/crypt/KeyType.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/KeyType.java diff --git a/src/main/java/network/crypta/crypt/MACType.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/MACType.java similarity index 100% rename from src/main/java/network/crypta/crypt/MACType.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/MACType.java diff --git a/src/main/java/network/crypta/crypt/MasterSecret.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/MasterSecret.java similarity index 100% rename from src/main/java/network/crypta/crypt/MasterSecret.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/MasterSecret.java diff --git a/src/main/java/network/crypta/crypt/MessageAuthCode.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/MessageAuthCode.java similarity index 100% rename from src/main/java/network/crypta/crypt/MessageAuthCode.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/MessageAuthCode.java diff --git a/src/main/java/network/crypta/crypt/MultiHashDigester.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/MultiHashDigester.java similarity index 100% rename from src/main/java/network/crypta/crypt/MultiHashDigester.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/MultiHashDigester.java diff --git a/src/main/java/network/crypta/crypt/MultiHashInputStream.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/MultiHashInputStream.java similarity index 100% rename from src/main/java/network/crypta/crypt/MultiHashInputStream.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/MultiHashInputStream.java diff --git a/src/main/java/network/crypta/crypt/MultiHashOutputStream.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/MultiHashOutputStream.java similarity index 100% rename from src/main/java/network/crypta/crypt/MultiHashOutputStream.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/MultiHashOutputStream.java diff --git a/src/main/java/network/crypta/crypt/PCFBMode.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/PCFBMode.java similarity index 100% rename from src/main/java/network/crypta/crypt/PCFBMode.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/PCFBMode.java diff --git a/src/main/java/network/crypta/crypt/PersistentRandomSource.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/PersistentRandomSource.java similarity index 100% rename from src/main/java/network/crypta/crypt/PersistentRandomSource.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/PersistentRandomSource.java diff --git a/src/main/java/network/crypta/crypt/RandomSource.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/RandomSource.java similarity index 100% rename from src/main/java/network/crypta/crypt/RandomSource.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/RandomSource.java diff --git a/src/main/java/network/crypta/crypt/SHA256.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/SHA256.java similarity index 100% rename from src/main/java/network/crypta/crypt/SHA256.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/SHA256.java diff --git a/src/main/java/network/crypta/crypt/UnsupportedCipherException.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/UnsupportedCipherException.java similarity index 100% rename from src/main/java/network/crypta/crypt/UnsupportedCipherException.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/UnsupportedCipherException.java diff --git a/src/main/java/network/crypta/crypt/UnsupportedTypeException.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/UnsupportedTypeException.java similarity index 100% rename from src/main/java/network/crypta/crypt/UnsupportedTypeException.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/UnsupportedTypeException.java diff --git a/src/main/java/network/crypta/crypt/Util.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/Util.java similarity index 100% rename from src/main/java/network/crypta/crypt/Util.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/Util.java diff --git a/src/main/java/network/crypta/crypt/Yarrow.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/Yarrow.java similarity index 100% rename from src/main/java/network/crypta/crypt/Yarrow.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/Yarrow.java diff --git a/src/main/java/network/crypta/crypt/ciphers/Rijndael.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/ciphers/Rijndael.java similarity index 100% rename from src/main/java/network/crypta/crypt/ciphers/Rijndael.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/ciphers/Rijndael.java diff --git a/src/main/java/network/crypta/crypt/ciphers/RijndaelAlgorithm.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/ciphers/RijndaelAlgorithm.java similarity index 100% rename from src/main/java/network/crypta/crypt/ciphers/RijndaelAlgorithm.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/ciphers/RijndaelAlgorithm.java diff --git a/src/main/java/network/crypta/crypt/ciphers/package-info.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/ciphers/package-info.java similarity index 100% rename from src/main/java/network/crypta/crypt/ciphers/package-info.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/ciphers/package-info.java diff --git a/src/main/java/network/crypta/crypt/package-info.java b/foundation-crypto-keys/src/main/java/network/crypta/crypt/package-info.java similarity index 100% rename from src/main/java/network/crypta/crypt/package-info.java rename to foundation-crypto-keys/src/main/java/network/crypta/crypt/package-info.java diff --git a/src/main/java/network/crypta/keys/BaseClientKey.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/BaseClientKey.java similarity index 100% rename from src/main/java/network/crypta/keys/BaseClientKey.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/BaseClientKey.java diff --git a/src/main/java/network/crypta/keys/BlockEncodeParams.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/BlockEncodeParams.java similarity index 100% rename from src/main/java/network/crypta/keys/BlockEncodeParams.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/BlockEncodeParams.java diff --git a/src/main/java/network/crypta/keys/CHKBlock.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/CHKBlock.java similarity index 100% rename from src/main/java/network/crypta/keys/CHKBlock.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/CHKBlock.java diff --git a/src/main/java/network/crypta/keys/CHKDecodeException.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/CHKDecodeException.java similarity index 100% rename from src/main/java/network/crypta/keys/CHKDecodeException.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/CHKDecodeException.java diff --git a/src/main/java/network/crypta/keys/CHKEncodeException.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/CHKEncodeException.java similarity index 100% rename from src/main/java/network/crypta/keys/CHKEncodeException.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/CHKEncodeException.java diff --git a/src/main/java/network/crypta/keys/CHKVerifyException.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/CHKVerifyException.java similarity index 100% rename from src/main/java/network/crypta/keys/CHKVerifyException.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/CHKVerifyException.java diff --git a/src/main/java/network/crypta/keys/ChkKeySizes.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/ChkKeySizes.java similarity index 100% rename from src/main/java/network/crypta/keys/ChkKeySizes.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/ChkKeySizes.java diff --git a/src/main/java/network/crypta/keys/ClientCHK.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/ClientCHK.java similarity index 100% rename from src/main/java/network/crypta/keys/ClientCHK.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/ClientCHK.java diff --git a/src/main/java/network/crypta/keys/ClientCHKBlock.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/ClientCHKBlock.java similarity index 100% rename from src/main/java/network/crypta/keys/ClientCHKBlock.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/ClientCHKBlock.java diff --git a/src/main/java/network/crypta/keys/ClientCHKEncodeAlgorithms.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/ClientCHKEncodeAlgorithms.java similarity index 100% rename from src/main/java/network/crypta/keys/ClientCHKEncodeAlgorithms.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/ClientCHKEncodeAlgorithms.java diff --git a/src/main/java/network/crypta/keys/ClientCHKEncodeParams.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/ClientCHKEncodeParams.java similarity index 100% rename from src/main/java/network/crypta/keys/ClientCHKEncodeParams.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/ClientCHKEncodeParams.java diff --git a/src/main/java/network/crypta/keys/ClientCHKEncodePayload.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/ClientCHKEncodePayload.java similarity index 100% rename from src/main/java/network/crypta/keys/ClientCHKEncodePayload.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/ClientCHKEncodePayload.java diff --git a/src/main/java/network/crypta/keys/ClientKSK.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/ClientKSK.java similarity index 100% rename from src/main/java/network/crypta/keys/ClientKSK.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/ClientKSK.java diff --git a/src/main/java/network/crypta/keys/ClientKey.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/ClientKey.java similarity index 100% rename from src/main/java/network/crypta/keys/ClientKey.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/ClientKey.java diff --git a/src/main/java/network/crypta/keys/ClientKeyBlock.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/ClientKeyBlock.java similarity index 100% rename from src/main/java/network/crypta/keys/ClientKeyBlock.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/ClientKeyBlock.java diff --git a/src/main/java/network/crypta/keys/ClientSSK.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/ClientSSK.java similarity index 100% rename from src/main/java/network/crypta/keys/ClientSSK.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/ClientSSK.java diff --git a/src/main/java/network/crypta/keys/ClientSSKBlock.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/ClientSSKBlock.java similarity index 100% rename from src/main/java/network/crypta/keys/ClientSSKBlock.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/ClientSSKBlock.java diff --git a/src/main/java/network/crypta/keys/CompressionLimits.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/CompressionLimits.java similarity index 100% rename from src/main/java/network/crypta/keys/CompressionLimits.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/CompressionLimits.java diff --git a/src/main/java/network/crypta/keys/DecompressionParams.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/DecompressionParams.java similarity index 100% rename from src/main/java/network/crypta/keys/DecompressionParams.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/DecompressionParams.java diff --git a/src/main/java/network/crypta/keys/FreenetURI.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/FreenetURI.java similarity index 100% rename from src/main/java/network/crypta/keys/FreenetURI.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/FreenetURI.java diff --git a/src/main/java/network/crypta/keys/InsertableClientSSK.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/InsertableClientSSK.java similarity index 100% rename from src/main/java/network/crypta/keys/InsertableClientSSK.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/InsertableClientSSK.java diff --git a/src/main/java/network/crypta/keys/InsertableUSK.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/InsertableUSK.java similarity index 100% rename from src/main/java/network/crypta/keys/InsertableUSK.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/InsertableUSK.java diff --git a/src/main/java/network/crypta/keys/Key.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/Key.java similarity index 100% rename from src/main/java/network/crypta/keys/Key.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/Key.java diff --git a/src/main/java/network/crypta/keys/KeyBlock.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/KeyBlock.java similarity index 100% rename from src/main/java/network/crypta/keys/KeyBlock.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/KeyBlock.java diff --git a/src/main/java/network/crypta/keys/KeyDecodeException.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/KeyDecodeException.java similarity index 100% rename from src/main/java/network/crypta/keys/KeyDecodeException.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/KeyDecodeException.java diff --git a/src/main/java/network/crypta/keys/KeyEncodeException.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/KeyEncodeException.java similarity index 100% rename from src/main/java/network/crypta/keys/KeyEncodeException.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/KeyEncodeException.java diff --git a/src/main/java/network/crypta/keys/KeyVerifyException.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/KeyVerifyException.java similarity index 100% rename from src/main/java/network/crypta/keys/KeyVerifyException.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/KeyVerifyException.java diff --git a/src/main/java/network/crypta/keys/NodeCHK.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/NodeCHK.java similarity index 100% rename from src/main/java/network/crypta/keys/NodeCHK.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/NodeCHK.java diff --git a/src/main/java/network/crypta/keys/NodeSSK.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/NodeSSK.java similarity index 100% rename from src/main/java/network/crypta/keys/NodeSSK.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/NodeSSK.java diff --git a/src/main/java/network/crypta/keys/PubkeyVerifyException.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/PubkeyVerifyException.java similarity index 100% rename from src/main/java/network/crypta/keys/PubkeyVerifyException.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/PubkeyVerifyException.java diff --git a/src/main/java/network/crypta/keys/SSKBlock.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/SSKBlock.java similarity index 100% rename from src/main/java/network/crypta/keys/SSKBlock.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/SSKBlock.java diff --git a/src/main/java/network/crypta/keys/SSKDecodeException.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/SSKDecodeException.java similarity index 100% rename from src/main/java/network/crypta/keys/SSKDecodeException.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/SSKDecodeException.java diff --git a/src/main/java/network/crypta/keys/SSKEncodeException.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/SSKEncodeException.java similarity index 100% rename from src/main/java/network/crypta/keys/SSKEncodeException.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/SSKEncodeException.java diff --git a/src/main/java/network/crypta/keys/SSKVerifyException.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/SSKVerifyException.java similarity index 100% rename from src/main/java/network/crypta/keys/SSKVerifyException.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/SSKVerifyException.java diff --git a/src/main/java/network/crypta/keys/TooBigException.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/TooBigException.java similarity index 100% rename from src/main/java/network/crypta/keys/TooBigException.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/TooBigException.java diff --git a/src/main/java/network/crypta/keys/USK.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/USK.java similarity index 100% rename from src/main/java/network/crypta/keys/USK.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/USK.java diff --git a/src/main/java/network/crypta/keys/package-info.java b/foundation-crypto-keys/src/main/java/network/crypta/keys/package-info.java similarity index 100% rename from src/main/java/network/crypta/keys/package-info.java rename to foundation-crypto-keys/src/main/java/network/crypta/keys/package-info.java diff --git a/src/main/java/network/crypta/support/io/BucketTools.java b/foundation-crypto-keys/src/main/java/network/crypta/support/io/BucketTools.java similarity index 66% rename from src/main/java/network/crypta/support/io/BucketTools.java rename to foundation-crypto-keys/src/main/java/network/crypta/support/io/BucketTools.java index 07a57cd3586..e67d1d060a4 100644 --- a/src/main/java/network/crypta/support/io/BucketTools.java +++ b/foundation-crypto-keys/src/main/java/network/crypta/support/io/BucketTools.java @@ -5,13 +5,19 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.security.MessageDigest; +import java.security.SecureRandom; import java.util.ArrayList; import java.util.List; +import java.util.Random; import network.crypta.crypt.AEADCryptBucket; import network.crypta.crypt.EncryptedRandomAccessBucket; import network.crypta.crypt.EncryptedRandomAccessBuffer; @@ -41,6 +47,32 @@ public class BucketTools { private static final int BUFFER_SIZE = 64 * 1024; private static final String MOVED_LITERAL = " (moved "; private static final String UNABLE_TO_READ_FROM_LITERAL = "): unable to read from "; + // Remaining root-owned bucket/buffer implementations are restored reflectively during the + // extraction so this leaf no longer compiles against root main sources. + private static final String FILE_BUCKET_CLASS = "network.crypta.support.io.FileBucket"; + private static final String PERSISTENT_TEMP_FILE_BUCKET_CLASS = + "network.crypta.support.io.PersistentTempFileBucket"; + private static final String DELAYED_FREE_BUCKET_CLASS = + "network.crypta.support.io.DelayedFreeBucket"; + private static final String DELAYED_FREE_RANDOM_ACCESS_BUCKET_CLASS = + "network.crypta.support.io.DelayedFreeRandomAccessBucket"; + private static final String NO_FREE_BUCKET_CLASS = "network.crypta.support.io.NoFreeBucket"; + private static final String PADDED_EPHEMERALLY_ENCRYPTED_BUCKET_CLASS = + "network.crypta.support.io.PaddedEphemerallyEncryptedBucket"; + private static final String PADDED_BUCKET_CLASS = "network.crypta.support.io.PaddedBucket"; + private static final String PADDED_RANDOM_ACCESS_BUCKET_CLASS = + "network.crypta.support.io.PaddedRandomAccessBucket"; + private static final String RAF_BUCKET_CLASS = "network.crypta.support.io.RAFBucket"; + private static final String POOLED_FILE_RANDOM_ACCESS_BUFFER_CLASS = + "network.crypta.support.io.PooledFileRandomAccessBuffer"; + private static final String FILE_RANDOM_ACCESS_BUFFER_CLASS = + "network.crypta.support.io.FileRandomAccessBuffer"; + private static final String READ_ONLY_RANDOM_ACCESS_BUFFER_CLASS = + "network.crypta.support.io.ReadOnlyRandomAccessBuffer"; + private static final String DELAYED_FREE_RANDOM_ACCESS_BUFFER_CLASS = + "network.crypta.support.io.DelayedFreeRandomAccessBuffer"; + private static final String PADDED_RANDOM_ACCESS_BUFFER_CLASS = + "network.crypta.support.io.PaddedRandomAccessBuffer"; private BucketTools() {} @@ -471,8 +503,8 @@ public static void copyFrom(Bucket bucket, InputStream is, long truncateLength) /** * Splits a bucket into a sequence of read-only chunk buckets. * - *

When {@code origData} is a {@link FileBucket} and {@code persistent} is {@code true}, the - * method uses the underlying file and returns efficient {@link ReadOnlyFileSliceBucket}s. In all + *

When {@code origData} is a {@code FileBucket} and {@code persistent} is {@code true}, the + * method uses the underlying file and returns efficient {@code ReadOnlyFileSliceBucket}s. In all * other cases it creates new buckets via {@code bf} and copies the data into them. * *

This method allocates a temporary buffer of size {@code splitSize}. @@ -491,13 +523,14 @@ public static void copyFrom(Bucket bucket, InputStream is, long truncateLength) public static Bucket[] split( Bucket origData, int splitSize, BucketFactory bf, boolean freeData, boolean persistent) throws IOException { - if (origData instanceof FileBucket bucket) { + if (isReflectiveInstance(origData, FILE_BUCKET_CLASS)) { if (freeData) { LOG.error( "Asked to free data when splitting a FileBucket ?!?!? Not freeing as this would clobber" + " the split result..."); } - Bucket[] buckets = bucket.split(splitSize); + Bucket[] buckets = + invokeBucketArrayMethod(origData, "split", new Class[] {int.class}, splitSize); if (persistent) return buckets; } long length = origData.size(); @@ -595,16 +628,18 @@ public static boolean equalBuckets(Bucket a, Bucket b) throws IOException { long size = a.size(); try (InputStream aIn = a.getInputStreamUnbuffered(); InputStream bIn = b.getInputStreamUnbuffered()) { - return FileUtil.equalStreams(aIn, bIn, size); + return equalStreams(aIn, bIn, size); } } // Note: Random-based test helpers live under src/test (FileTestUtils). + private static final SecureRandom FILL_SEED_GENERATOR = new SecureRandom(); + /** * Fills a bucket with pseudo-random bytes. * - *

Uses {@link FileUtil#fill(OutputStream, long)} to write {@code length} bytes. The data is + *

Uses the legacy Mersenne-Twister-backed filler to write {@code length} bytes. The data is * suitable for testing or obfuscating patterns but is not cryptographically secure. * * @param bucket the destination bucket @@ -613,7 +648,7 @@ public static boolean equalBuckets(Bucket a, Bucket b) throws IOException { */ public static void fill(Bucket bucket, long length) throws IOException { try (OutputStream os = bucket.getOutputStreamUnbuffered()) { - FileUtil.fill(os, length); + fill(os, length); } } @@ -666,6 +701,157 @@ public static long copyTo( } } + private static void fill(OutputStream os, long length) throws IOException { + byte[] seed = new byte[16]; + FILL_SEED_GENERATOR.nextBytes(seed); + writeRandomBytes(os, MersenneTwister.createUnsynchronized(seed), length); + } + + private static void writeRandomBytes(OutputStream os, Random random, long length) + throws IOException { + byte[] buffer = new byte[(int) Math.min(length, BUFFER_SIZE)]; + long remaining = length; + while (remaining > 0) { + random.nextBytes(buffer); + int writeLength = (int) Math.min(remaining, BUFFER_SIZE); + os.write(buffer, 0, writeLength); + remaining -= writeLength; + } + } + + private static boolean equalStreams(InputStream a, InputStream b, long size) throws IOException { + byte[] aBuffer = new byte[BUFFER_SIZE]; + byte[] bBuffer = new byte[BUFFER_SIZE]; + DataInputStream aIn = new DataInputStream(a); + DataInputStream bIn = new DataInputStream(b); + long checked = 0; + while (checked < size) { + int toRead = (int) Math.min(BUFFER_SIZE, size - checked); + aIn.readFully(aBuffer, 0, toRead); + bIn.readFully(bBuffer, 0, toRead); + if (!MessageDigest.isEqual(aBuffer, bBuffer)) return false; + checked += toRead; + } + return true; + } + + private static boolean isReflectiveInstance(Object value, String className) { + for (Class type = value.getClass(); type != null; type = type.getSuperclass()) { + if (type.getName().equals(className)) { + return true; + } + } + return false; + } + + private static boolean magicMatches(String className, int magic) { + try { + Field magicField = loadClass(className).getDeclaredField("MAGIC"); + magicField.setAccessible(true); + return magicField.getInt(null) == magic; + } catch (ReflectiveOperationException e) { + return false; + } + } + + private static Class loadClass(String className) throws ClassNotFoundException { + return Class.forName(className, true, BucketTools.class.getClassLoader()); + } + + private static Bucket[] invokeBucketArrayMethod( + Object target, String methodName, Class[] parameterTypes, Object... args) + throws IOException { + return invokeReflectiveMethod(target, methodName, Bucket[].class, parameterTypes, args); + } + + private static RandomAccessBucket invokeRandomAccessBucketMethod( + Object target, String methodName, Class[] parameterTypes, Object... args) + throws IOException { + return invokeReflectiveMethod( + target, methodName, RandomAccessBucket.class, parameterTypes, args); + } + + private static T invokeReflectiveMethod( + Object target, + String methodName, + Class expectedType, + Class[] parameterTypes, + Object... args) + throws IOException { + try { + Method method = target.getClass().getMethod(methodName, parameterTypes); + method.setAccessible(true); + return expectedType.cast(method.invoke(target, args)); + } catch (InvocationTargetException e) { + rethrowIoCause(target.getClass().getName() + "#" + methodName, e.getCause()); + throw new IllegalStateException("Unreachable"); + } catch (ReflectiveOperationException e) { + throw new IllegalStateException( + "Failed to invoke " + methodName + " on " + target.getClass().getName(), e); + } + } + + private static Bucket instantiateBucket( + String className, Class[] parameterTypes, Object... args) + throws IOException, StorageFormatException, ResumeFailedException { + return instantiateReflectively(className, Bucket.class, parameterTypes, args); + } + + private static LockableRandomAccessBuffer instantiateRandomAccessBuffer( + String className, Class[] parameterTypes, Object... args) + throws IOException, StorageFormatException, ResumeFailedException { + return instantiateReflectively( + className, LockableRandomAccessBuffer.class, parameterTypes, args); + } + + private static T instantiateReflectively( + String className, Class expectedType, Class[] parameterTypes, Object... args) + throws IOException, StorageFormatException, ResumeFailedException { + try { + Constructor constructor = loadClass(className).getDeclaredConstructor(parameterTypes); + constructor.setAccessible(true); + return expectedType.cast(constructor.newInstance(args)); + } catch (InvocationTargetException e) { + rethrowReflectiveCause(className, e.getCause()); + throw new IllegalStateException("Unreachable"); + } catch (ReflectiveOperationException e) { + throw new IllegalStateException("Failed to instantiate " + className, e); + } + } + + private static void rethrowIoCause(String description, Throwable cause) throws IOException { + if (cause instanceof IOException ioException) { + throw ioException; + } + if (cause instanceof RuntimeException runtimeException) { + throw runtimeException; + } + if (cause instanceof Error error) { + throw error; + } + throw new IllegalStateException("Failed to invoke " + description, cause); + } + + private static void rethrowReflectiveCause(String className, Throwable cause) + throws IOException, StorageFormatException, ResumeFailedException { + if (cause instanceof IOException ioException) { + throw ioException; + } + if (cause instanceof StorageFormatException storageFormatException) { + throw storageFormatException; + } + if (cause instanceof ResumeFailedException resumeFailedException) { + throw resumeFailedException; + } + if (cause instanceof RuntimeException runtimeException) { + throw runtimeException; + } + if (cause instanceof Error error) { + throw error; + } + throw new IllegalStateException("Failed to instantiate " + className, cause); + } + /** * Restores a {@link Bucket} from a binary stream written by {@code Bucket.storeTo()}. * @@ -688,26 +874,121 @@ public static Bucket restoreFrom( MasterSecret masterKey) throws IOException, StorageFormatException, ResumeFailedException { int magic = dis.readInt(); - return switch (magic) { - case AEADCryptBucket.MAGIC -> new AEADCryptBucket(dis, fg, persistentFileTracker, masterKey); - case FileBucket.MAGIC -> new FileBucket(dis); - case PersistentTempFileBucket.MAGIC -> new PersistentTempFileBucket(dis); - case DelayedFreeBucket.MAGIC -> - new DelayedFreeBucket(dis, fg, persistentFileTracker, masterKey); - case DelayedFreeRandomAccessBucket.MAGIC -> - new DelayedFreeRandomAccessBucket(dis, fg, persistentFileTracker, masterKey); - case NoFreeBucket.MAGIC -> new NoFreeBucket(dis, fg, persistentFileTracker, masterKey); - case PaddedEphemerallyEncryptedBucket.MAGIC -> - new PaddedEphemerallyEncryptedBucket(dis, fg, persistentFileTracker, masterKey); - case ReadOnlyFileSliceBucket.MAGIC -> new ReadOnlyFileSliceBucket(dis); - case PaddedBucket.MAGIC -> new PaddedBucket(dis, fg, persistentFileTracker, masterKey); - case PaddedRandomAccessBucket.MAGIC -> - new PaddedRandomAccessBucket(dis, fg, persistentFileTracker, masterKey); - case RAFBucket.MAGIC -> new RAFBucket(dis, fg, persistentFileTracker, masterKey); - case EncryptedRandomAccessBucket.MAGIC -> - new EncryptedRandomAccessBucket(dis, fg, persistentFileTracker, masterKey); - default -> throw new StorageFormatException("Unknown magic value for bucket " + magic); - }; + if (magic == AEADCryptBucket.MAGIC) { + return new AEADCryptBucket(dis, fg, persistentFileTracker, masterKey); + } + if (magicMatches(FILE_BUCKET_CLASS, magic)) { + return instantiateBucket(FILE_BUCKET_CLASS, new Class[] {DataInputStream.class}, dis); + } + if (magicMatches(PERSISTENT_TEMP_FILE_BUCKET_CLASS, magic)) { + return instantiateBucket( + PERSISTENT_TEMP_FILE_BUCKET_CLASS, new Class[] {DataInputStream.class}, dis); + } + if (magicMatches(DELAYED_FREE_BUCKET_CLASS, magic)) { + return instantiateBucket( + DELAYED_FREE_BUCKET_CLASS, + new Class[] { + DataInputStream.class, + FilenameGenerator.class, + PersistentFileTracker.class, + MasterSecret.class + }, + dis, + fg, + persistentFileTracker, + masterKey); + } + if (magicMatches(DELAYED_FREE_RANDOM_ACCESS_BUCKET_CLASS, magic)) { + return instantiateBucket( + DELAYED_FREE_RANDOM_ACCESS_BUCKET_CLASS, + new Class[] { + DataInputStream.class, + FilenameGenerator.class, + PersistentFileTracker.class, + MasterSecret.class + }, + dis, + fg, + persistentFileTracker, + masterKey); + } + if (magicMatches(NO_FREE_BUCKET_CLASS, magic)) { + return instantiateBucket( + NO_FREE_BUCKET_CLASS, + new Class[] { + DataInputStream.class, + FilenameGenerator.class, + PersistentFileTracker.class, + MasterSecret.class + }, + dis, + fg, + persistentFileTracker, + masterKey); + } + if (magicMatches(PADDED_EPHEMERALLY_ENCRYPTED_BUCKET_CLASS, magic)) { + return instantiateBucket( + PADDED_EPHEMERALLY_ENCRYPTED_BUCKET_CLASS, + new Class[] { + DataInputStream.class, + FilenameGenerator.class, + PersistentFileTracker.class, + MasterSecret.class + }, + dis, + fg, + persistentFileTracker, + masterKey); + } + if (magic == ReadOnlyFileSliceBucket.MAGIC) { + return new ReadOnlyFileSliceBucket(dis); + } + if (magicMatches(PADDED_BUCKET_CLASS, magic)) { + return instantiateBucket( + PADDED_BUCKET_CLASS, + new Class[] { + DataInputStream.class, + FilenameGenerator.class, + PersistentFileTracker.class, + MasterSecret.class + }, + dis, + fg, + persistentFileTracker, + masterKey); + } + if (magicMatches(PADDED_RANDOM_ACCESS_BUCKET_CLASS, magic)) { + return instantiateBucket( + PADDED_RANDOM_ACCESS_BUCKET_CLASS, + new Class[] { + DataInputStream.class, + FilenameGenerator.class, + PersistentFileTracker.class, + MasterSecret.class + }, + dis, + fg, + persistentFileTracker, + masterKey); + } + if (magicMatches(RAF_BUCKET_CLASS, magic)) { + return instantiateBucket( + RAF_BUCKET_CLASS, + new Class[] { + DataInputStream.class, + FilenameGenerator.class, + PersistentFileTracker.class, + MasterSecret.class + }, + dis, + fg, + persistentFileTracker, + masterKey); + } + if (magic == EncryptedRandomAccessBucket.MAGIC) { + return new EncryptedRandomAccessBucket(dis, fg, persistentFileTracker, masterKey); + } + throw new StorageFormatException("Unknown magic value for bucket " + magic); } /** @@ -729,26 +1010,72 @@ public static LockableRandomAccessBuffer restoreRAFFrom( MasterSecret masterSecret) throws IOException, StorageFormatException, ResumeFailedException { int magic = dis.readInt(); - return switch (magic) { - case PooledFileRandomAccessBuffer.MAGIC -> - new PooledFileRandomAccessBuffer(dis, fg, persistentFileTracker); - case FileRandomAccessBuffer.MAGIC -> new FileRandomAccessBuffer(dis); - case ReadOnlyRandomAccessBuffer.MAGIC -> - new ReadOnlyRandomAccessBuffer(dis, fg, persistentFileTracker, masterSecret); - case DelayedFreeRandomAccessBuffer.MAGIC -> - new DelayedFreeRandomAccessBuffer(dis, fg, persistentFileTracker, masterSecret); - case EncryptedRandomAccessBuffer.MAGIC -> - EncryptedRandomAccessBuffer.create(dis, fg, persistentFileTracker, masterSecret); - case PaddedRandomAccessBuffer.MAGIC -> - new PaddedRandomAccessBuffer(dis, fg, persistentFileTracker, masterSecret); - default -> throw new StorageFormatException("Unknown magic value for RAF " + magic); - }; + if (magicMatches(POOLED_FILE_RANDOM_ACCESS_BUFFER_CLASS, magic)) { + return instantiateRandomAccessBuffer( + POOLED_FILE_RANDOM_ACCESS_BUFFER_CLASS, + new Class[] { + DataInputStream.class, PersistentFilenameGenerator.class, PersistentFileTracker.class + }, + dis, + fg, + persistentFileTracker); + } + if (magicMatches(FILE_RANDOM_ACCESS_BUFFER_CLASS, magic)) { + return instantiateRandomAccessBuffer( + FILE_RANDOM_ACCESS_BUFFER_CLASS, new Class[] {DataInputStream.class}, dis); + } + if (magicMatches(READ_ONLY_RANDOM_ACCESS_BUFFER_CLASS, magic)) { + return instantiateRandomAccessBuffer( + READ_ONLY_RANDOM_ACCESS_BUFFER_CLASS, + new Class[] { + DataInputStream.class, + FilenameGenerator.class, + PersistentFileTracker.class, + MasterSecret.class + }, + dis, + fg, + persistentFileTracker, + masterSecret); + } + if (magicMatches(DELAYED_FREE_RANDOM_ACCESS_BUFFER_CLASS, magic)) { + return instantiateRandomAccessBuffer( + DELAYED_FREE_RANDOM_ACCESS_BUFFER_CLASS, + new Class[] { + DataInputStream.class, + FilenameGenerator.class, + PersistentFileTracker.class, + MasterSecret.class + }, + dis, + fg, + persistentFileTracker, + masterSecret); + } + if (magic == EncryptedRandomAccessBuffer.MAGIC) { + return EncryptedRandomAccessBuffer.create(dis, fg, persistentFileTracker, masterSecret); + } + if (magicMatches(PADDED_RANDOM_ACCESS_BUFFER_CLASS, magic)) { + return instantiateRandomAccessBuffer( + PADDED_RANDOM_ACCESS_BUFFER_CLASS, + new Class[] { + DataInputStream.class, + FilenameGenerator.class, + PersistentFileTracker.class, + MasterSecret.class + }, + dis, + fg, + persistentFileTracker, + masterSecret); + } + throw new StorageFormatException("Unknown magic value for RAF " + magic); } /** * Ensures a {@link Bucket} is a {@link RandomAccessBucket}, copying if necessary. * - *

If the bucket already supports random access, it is returned as-is. If it is a {@link + *

If the bucket already supports random access, it is returned as-is. If it is a {@code * DelayedFreeBucket}, the method first asks it to provide a random-access view. Otherwise, a new * random-access bucket is created via {@code bf}, the content is copied, and the original bucket * is freed. @@ -761,8 +1088,9 @@ public static LockableRandomAccessBuffer restoreRAFFrom( public static RandomAccessBucket toRandomAccessBucket(Bucket bucket, BucketFactory bf) throws IOException { if (bucket instanceof RandomAccessBucket accessBucket) return accessBucket; - if (bucket instanceof DelayedFreeBucket freeBucket) { - RandomAccessBucket ret = freeBucket.toRandomAccessBucket(); + if (isReflectiveInstance(bucket, DELAYED_FREE_BUCKET_CLASS)) { + RandomAccessBucket ret = + invokeRandomAccessBucketMethod(bucket, "toRandomAccessBucket", new Class[0]); if (ret != null) return ret; } RandomAccessBucket ret = bf.makeBucket(bucket.size()); diff --git a/src/main/java/network/crypta/support/io/PrependLengthOutputStream.java b/foundation-crypto-keys/src/main/java/network/crypta/support/io/PrependLengthOutputStream.java similarity index 100% rename from src/main/java/network/crypta/support/io/PrependLengthOutputStream.java rename to foundation-crypto-keys/src/main/java/network/crypta/support/io/PrependLengthOutputStream.java diff --git a/foundation-support/build.gradle.kts b/foundation-support/build.gradle.kts index d2164d4c62a..18595b36f55 100644 --- a/foundation-support/build.gradle.kts +++ b/foundation-support/build.gradle.kts @@ -9,5 +9,6 @@ dependencies { implementation(libs.slf4jApi) implementation(project(":thirdparty-legacy")) implementation(libs.commonsCompress) + implementation(files(rootProject.file("libs/wrapper.jar"))) compileOnly(libs.jetbrainsAnnotations) } diff --git a/foundation-support/gradle/owned-output-patterns.txt b/foundation-support/gradle/owned-output-patterns.txt index b594d64c2df..2507e7fcf53 100644 --- a/foundation-support/gradle/owned-output-patterns.txt +++ b/foundation-support/gradle/owned-output-patterns.txt @@ -18,6 +18,9 @@ network/crypta/support/HTMLNode* network/crypta/support/HexUtil* network/crypta/support/IllegalBase64Exception* network/crypta/support/PriorityAwareExecutor* +network/crypta/support/Loader* +network/crypta/support/SimpleReadOnlyArrayBucket* +network/crypta/support/StringValidityChecker* network/crypta/support/SimpleFieldSet* network/crypta/support/Ticker* network/crypta/support/TimeUtil* @@ -40,6 +43,8 @@ network/crypta/support/io/DiskSpaceChecker* network/crypta/support/io/HeaderStreams* network/crypta/support/io/IOUtils* network/crypta/support/io/InetAddressComparator* +network/crypta/support/io/FilenameGenerator* +network/crypta/support/io/FilenameSanitizer* network/crypta/support/io/InsufficientDiskSpaceException* network/crypta/support/io/LineReader* network/crypta/support/io/LineReadingInputStream* diff --git a/src/main/java/network/crypta/support/Loader.java b/foundation-support/src/main/java/network/crypta/support/Loader.java similarity index 100% rename from src/main/java/network/crypta/support/Loader.java rename to foundation-support/src/main/java/network/crypta/support/Loader.java diff --git a/src/main/java/network/crypta/support/SimpleReadOnlyArrayBucket.java b/foundation-support/src/main/java/network/crypta/support/SimpleReadOnlyArrayBucket.java similarity index 100% rename from src/main/java/network/crypta/support/SimpleReadOnlyArrayBucket.java rename to foundation-support/src/main/java/network/crypta/support/SimpleReadOnlyArrayBucket.java diff --git a/src/main/java/network/crypta/support/StringValidityChecker.java b/foundation-support/src/main/java/network/crypta/support/StringValidityChecker.java similarity index 100% rename from src/main/java/network/crypta/support/StringValidityChecker.java rename to foundation-support/src/main/java/network/crypta/support/StringValidityChecker.java diff --git a/src/main/java/network/crypta/support/io/FilenameGenerator.java b/foundation-support/src/main/java/network/crypta/support/io/FilenameGenerator.java similarity index 93% rename from src/main/java/network/crypta/support/io/FilenameGenerator.java rename to foundation-support/src/main/java/network/crypta/support/io/FilenameGenerator.java index 156377db470..f3751bffd55 100644 --- a/src/main/java/network/crypta/support/io/FilenameGenerator.java +++ b/foundation-support/src/main/java/network/crypta/support/io/FilenameGenerator.java @@ -71,7 +71,7 @@ public FilenameGenerator(Random random, boolean wipeFiles, File dir, String pref this.random = random; this.prefix = prefix; tmpDir = - FileUtil.getCanonicalFile( + canonicalFile( Objects.requireNonNullElseGet( dir, () -> new File(System.getProperty("java.io.tmpdir")))); if (!tmpDir.exists() && !tmpDir.mkdir() && !tmpDir.isDirectory()) { @@ -223,7 +223,7 @@ public File getDir() { * @return {@code true} if the file is under the generator's directory and begins with the prefix */ boolean matches(File file) { - return FileUtil.equals(file.getParentFile(), tmpDir) && file.getName().startsWith(prefix); + return canonicalEquals(file.getParentFile(), tmpDir) && file.getName().startsWith(prefix); } /** @@ -235,7 +235,7 @@ boolean matches(File file) { * original file is returned and an error is logged. * *

Note: the exact move semantics (atomicity across filesystems, overwrite behavior, etc.) are - * governed by {@link FileUtil#moveTo(File, File, boolean)}. + * governed by {@link AtomicFileMoves#moveTo(File, File, boolean)}. * * @param file source file; must be non-null * @param id identifier to use when constructing the target file name @@ -246,10 +246,29 @@ public File maybeMove(File file, long id) { if (matches(file)) return file; File newFile = getFilename(id); LOG.info("Moving tempfile {} to {}", file, newFile); - if (FileUtil.moveTo(file, newFile, false)) return newFile; + if (AtomicFileMoves.moveTo(file, newFile, false)) return newFile; else { LOG.error("Unable to move old temporary file {} to {}", file, newFile); return file; } } + + private static File canonicalFile(File file) { + String name = file.getPath(); + if (File.pathSeparatorChar == '\\') { + name = name.toLowerCase(Locale.ROOT); + } + file = new File(name); + try { + return file.getAbsoluteFile().getCanonicalFile(); + } catch (IOException _) { + return file.getAbsoluteFile(); + } + } + + private static boolean canonicalEquals(File a, File b) { + if (Objects.equals(a, b)) return true; + if (a == null || b == null) return false; + return canonicalFile(a).equals(canonicalFile(b)); + } } diff --git a/src/main/java/network/crypta/support/io/FilenameSanitizer.java b/foundation-support/src/main/java/network/crypta/support/io/FilenameSanitizer.java similarity index 100% rename from src/main/java/network/crypta/support/io/FilenameSanitizer.java rename to foundation-support/src/main/java/network/crypta/support/io/FilenameSanitizer.java diff --git a/settings.gradle.kts b/settings.gradle.kts index 03e243485d1..3ffa4a37ab5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -26,6 +26,7 @@ rootProject.name = "cryptad" include( ":foundation-support", ":foundation-store-contracts", + ":foundation-crypto-keys", ":foundation-config", ":foundation-fs", ":foundation-compat", diff --git a/src/test/java/network/crypta/support/io/BucketToolsTest.java b/src/test/java/network/crypta/support/io/BucketToolsTest.java index b743ebc2c5a..a27019b9249 100644 --- a/src/test/java/network/crypta/support/io/BucketToolsTest.java +++ b/src/test/java/network/crypta/support/io/BucketToolsTest.java @@ -5,6 +5,7 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; +import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.StandardCharsets; @@ -20,6 +21,7 @@ import network.crypta.testsupport.FileTestUtils; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.function.Executable; +import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.CsvSource; @@ -28,6 +30,7 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -47,6 +50,8 @@ */ class BucketToolsTest { + @TempDir File tempDir; + // ---------- copy(Bucket, Bucket) ---------- @Test @@ -525,6 +530,34 @@ void split_whenLengthTooLarge_throwsIAEBeforeAlloc() { } } + @Test + void split_whenPersistentFileBucket_returnsReadOnlyFileSliceBuckets() throws Exception { + // Arrange + File backingFile = new File(tempDir, "split.bin"); + byte[] data = "abcdefghij".getBytes(StandardCharsets.UTF_8); + try (FileBucket src = new FileBucket(backingFile, false, false, false, false)) { + try (OutputStream os = src.getOutputStreamUnbuffered()) { + os.write(data); + } + + // Act + Bucket[] parts = BucketTools.split(src, 4, new ArrayBucketFactory(), false, true); + + // Assert + assertEquals(3, parts.length); + try (Bucket p0 = parts[0]; + Bucket p1 = parts[1]; + Bucket p2 = parts[2]) { + assertInstanceOf(ReadOnlyFileSliceBucket.class, p0); + assertInstanceOf(ReadOnlyFileSliceBucket.class, p1); + assertInstanceOf(ReadOnlyFileSliceBucket.class, p2); + assertArrayEquals("abcd".getBytes(StandardCharsets.UTF_8), BucketTools.toByteArray(p0)); + assertArrayEquals("efgh".getBytes(StandardCharsets.UTF_8), BucketTools.toByteArray(p1)); + assertArrayEquals("ij".getBytes(StandardCharsets.UTF_8), BucketTools.toByteArray(p2)); + } + } + } + // ---------- pad(byte[], ...) and pad(Bucket, ...) ---------- @Test diff --git a/src/test/java/network/crypta/support/io/PooledFileRandomAccessBufferTest.java b/src/test/java/network/crypta/support/io/PooledFileRandomAccessBufferTest.java index ffba9d6c3ef..7a5f07f52af 100644 --- a/src/test/java/network/crypta/support/io/PooledFileRandomAccessBufferTest.java +++ b/src/test/java/network/crypta/support/io/PooledFileRandomAccessBufferTest.java @@ -12,6 +12,7 @@ import java.util.stream.Stream; import network.crypta.client.async.ClientContext; import network.crypta.support.api.LockableRandomAccessBuffer.RAFLock; +import network.crypta.support.api.LockableRandomAccessBuffer; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Tag; @@ -27,6 +28,7 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -385,6 +387,40 @@ dis, mock(PersistentFilenameGenerator.class), mock(PersistentFileTracker.class)) } } + @Test + void restoreRAFFrom_whenStoredPooledBuffer_usesPersistentFilenameGeneratorConstructor() + throws Exception { + // Arrange + File original = new File(tempDir, "buckettools-round.bin"); + Files.write(original.toPath(), new byte[] {1, 2, 3, 4}); + FilenameGenerator fg = + new FilenameGenerator(new java.util.Random(1234L), false, tempDir, "buckettools-"); + PersistentFileTracker tracker = mock(PersistentFileTracker.class); + + try (PooledFileRandomAccessBuffer orig = + new PooledFileRandomAccessBuffer( + original, false, -1, -1L, false, new PooledFileRandomAccessBuffer.FDTracker(4))) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baos)) { + orig.storeTo(dos); + } + + // Act + try (DataInputStream dis = new DataInputStream(new ByteArrayInputStream(baos.toByteArray())); + LockableRandomAccessBuffer restored = + BucketTools.restoreRAFFrom(dis, fg, tracker, null)) { + + // Assert + PooledFileRandomAccessBuffer copy = + assertInstanceOf(PooledFileRandomAccessBuffer.class, restored); + assertEquals(orig.size(), copy.size()); + assertEquals( + original.getCanonicalPath(), + requireNonNull(copy.file, "restored file").getCanonicalPath()); + } + } + } + @Test void load_whenPersistentTempFileMissingButMoved_registersWithTrackerAndUsesNewFile() throws Exception {