diff --git a/ChangeLog.md b/ChangeLog.md index 6e8c73137d2c82a7f71e8328aea635449d9ab414..ef333122ba2edf092ba58eaf910d518d7080451a 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,6 @@ # Changelog for tahoe-lafs-immutable-uploader -## Unreleased changes +## 0.1.0 + +* Initial release. +* Support for encoding data using Tahoe-LAFS' CHK protocol. diff --git a/LICENSE b/LICENSE index e637cdee2980fde6271b1e8bfa394f4b22e06c61..2195f6bff11a0703b8850841faa600440c06f6ab 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright Author name here (c) 2020 +PrivateStorage.io Inc. (c) 2020-2022 All rights reserved. diff --git a/README.md b/README.md index 0d4e04125a23fab34bdd9cb705ec5384cd1c2dda..2e90f5c200421f6da8762fe74b8326934aec754d 100644 --- a/README.md +++ b/README.md @@ -1 +1,24 @@ -# tahoe-lafs-immutable-uploader +# Tahoe-CHK + +## What is it? + +Tahoe-CHK is a Haskell implementation of the [Tahoe-LAFS](https://tahoe-lafs.org/) CHK crytographic protocol. +It aims for bit-for-bit compatibility with the original Python implementation. + +It will not include an implementation of any network protocol for transferring CHK shares. +However, its APIs are intended to be easy to integrate with such an implementation. + +### What is the current state? + +* CHK encoding is fully implemented. +* CHK decoding is not yet implemented. + +## Why does it exist? + +A Haskell implementation can be used in places the original Python implementation cannot be +(for example, runtime environments where it is difficult to have a Python interpreter). +Additionally, +with the benefit of the experience gained from creating and maintaining the Python implementation, +a number of implementation decisions can be made differently to produce a more efficient, more flexible, simpler implementation and API. +Also, +the Python implementation claims no public library API for users outside of the Tahoe-LAFS project itself. diff --git a/Setup.hs b/Setup.hs deleted file mode 100644 index 9a994af677b0dfd41b4e3b76b3e7e604003d64e1..0000000000000000000000000000000000000000 --- a/Setup.hs +++ /dev/null @@ -1,2 +0,0 @@ -import Distribution.Simple -main = defaultMain diff --git a/app/Main.hs b/app/Main.hs index 2f83ffe9f3ccdae85ba8f76e8313c5aab7652cfc..b11b8fc4b9ddc0de42cf7486d9e0d79e00b051ce 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -29,13 +29,13 @@ import Options.Applicative ( import Data.ByteString.Base32 ( decodeBase32Unpadded, ) -import Types ( +import Tahoe.CHK.Types ( Parameters (..), Required, Total, ) -import Upload ( +import Tahoe.CHK.Upload ( UploadResult (uploadResultReadCap, uploadResultShareMap), defaultParameters, filesystemStorageServer, @@ -45,7 +45,7 @@ import Upload ( store, ) -import Capability ( +import Tahoe.CHK.Capability ( CHK (CHKReader), dangerRealShow, ) diff --git a/flake.nix b/flake.nix index f760f8322ce3bbdb3a650ed4b8f5e4f7748c94fe..14c98eba736565e9e52cba3337523a9087e9cebb 100644 --- a/flake.nix +++ b/flake.nix @@ -29,7 +29,7 @@ pkgs = nixpkgs.legacyPackages.${system}; src = ./.; compilerVersion = "ghc8107"; - packageName = "tahoe-lafs-immutable-uploader"; + packageName = "tahoe-chk"; }; in { checks = hslib.checks {}; diff --git a/src/Lib.hs b/src/Lib.hs deleted file mode 100644 index d36ff2714d5b36319784afa370e0f0d111d57ef1..0000000000000000000000000000000000000000 --- a/src/Lib.hs +++ /dev/null @@ -1,6 +0,0 @@ -module Lib - ( someFunc - ) where - -someFunc :: IO () -someFunc = putStrLn "someFunc" diff --git a/src/Netstring.hs b/src/Netstring.hs deleted file mode 100644 index 200dbe63bb40f5df476d66d9ff4dfad3e270eb18..0000000000000000000000000000000000000000 --- a/src/Netstring.hs +++ /dev/null @@ -1,14 +0,0 @@ -module Netstring - ( netstring - ) where - -import qualified Data.ByteString as B -import qualified Data.ByteString.Char8 as C8 - -netstring :: B.ByteString -> B.ByteString -netstring xs = B.concat - [ C8.pack . show . B.length $ xs - , ":" - , xs - , "," - ] diff --git a/src/CHK.hs b/src/Tahoe/CHK.hs similarity index 99% rename from src/CHK.hs rename to src/Tahoe/CHK.hs index d936b5f06f26ebb52e9189c73d386dee9d7a5b07..6b2697c3280fe91ea316aa7da829dd4f4e389701 100644 --- a/src/CHK.hs +++ b/src/Tahoe/CHK.hs @@ -55,7 +55,7 @@ -- encoding used to create the CHK and (largely) necessary for either -- decoding or verifying the integrity of the contained data. -module CHK ( +module Tahoe.CHK ( makeChkEncoder, chkEncrypt, ) where @@ -83,7 +83,7 @@ import Data.ByteString.Lazy ( toStrict, ) -import qualified ZFEC +import qualified Tahoe.CHK.ZFEC as ZFEC import Crypto.Classes ( zeroIV, @@ -105,11 +105,11 @@ import Crypto.Hash ( hashUpdate, ) -import Netstring ( +import Tahoe.CHK.Netstring ( netstring, ) -import Crypto ( +import Tahoe.CHK.Crypto ( blockHash, ciphertextSegmentHash, ciphertextTag, @@ -117,7 +117,7 @@ import Crypto ( uriExtensionHash, ) -import Merkle ( +import Tahoe.CHK.Merkle ( MerkleTree, breadthFirstList, makeTreePartial, @@ -125,18 +125,18 @@ import Merkle ( neededHashes, ) -import URIExtension ( +import Tahoe.CHK.URIExtension ( URIExtension (..), uriExtensionToBytes, ) -import Util ( +import Tahoe.CHK.Util ( nextMultiple, nextPowerOf, ) -import qualified Capability as Cap -import Types +import qualified Tahoe.CHK.Capability as Cap +import Tahoe.CHK.Types data CHKEncodingError = -- | There was an error during ZFEC encoding. diff --git a/src/Capability.hs b/src/Tahoe/CHK/Capability.hs similarity index 98% rename from src/Capability.hs rename to src/Tahoe/CHK/Capability.hs index 156b35c66a42ed0d5371d54d4d22fb97717ef0cb..ea8f2408d643c8bc331b37c3035d3d846a9e77dc 100644 --- a/src/Capability.hs +++ b/src/Tahoe/CHK/Capability.hs @@ -1,6 +1,6 @@ {-# LANGUAGE NamedFieldPuns #-} -module Capability (CHK (..), Reader (..), Verifier (..), makeReader, pCapability, pVerifier, pReader, dangerRealShow) where +module Tahoe.CHK.Capability (CHK (..), Reader (..), Verifier (..), makeReader, pCapability, pVerifier, pReader, dangerRealShow) where import qualified Data.ByteString as B import qualified Data.ByteString.Base32 as B @@ -16,11 +16,11 @@ import Text.Megaparsec (ErrorFancy (ErrorFail), Parsec, count, fancyFailure, one import Text.Megaparsec.Char (char, string) import Text.Megaparsec.Char.Lexer (decimal) -import Crypto (storageIndexHash) import Crypto.Cipher.AES128 ( AESKey128, ) import Crypto.Classes (buildKey) +import Tahoe.CHK.Crypto (storageIndexHash) {- | Define a type in which we will perform parsing. There is no custom error data (Void) and we are parsing T.Text. diff --git a/src/Crypto.hs b/src/Tahoe/CHK/Crypto.hs similarity index 96% rename from src/Crypto.hs rename to src/Tahoe/CHK/Crypto.hs index 3b86c2953e8a43b682f9e334fe4c1ad143f75bae..76997526a22241b408abaa43aaa6f538e6bba648 100644 --- a/src/Crypto.hs +++ b/src/Tahoe/CHK/Crypto.hs @@ -1,4 +1,4 @@ -module Crypto ( +module Tahoe.CHK.Crypto ( sha256, sha256d, taggedHash, @@ -36,17 +36,17 @@ import Crypto.Cipher.AES128 ( AESKey128, ) -import Netstring ( +import Tahoe.CHK.Netstring ( netstring, ) -import URIExtension ( +import Tahoe.CHK.URIExtension ( URIExtension, showBytes, uriExtensionToBytes, ) -import Types +import Tahoe.CHK.Types toBytes :: Digest SHA256 -> B.ByteString toBytes = B.pack . BA.unpack diff --git a/src/Merkle.hs b/src/Tahoe/CHK/Merkle.hs similarity index 98% rename from src/Merkle.hs rename to src/Tahoe/CHK/Merkle.hs index c9b7b81fcf30100381ba468995bec3c4d6c2021b..f6c780c6f9d210a32c950022a62e962b64c3b67a 100644 --- a/src/Merkle.hs +++ b/src/Tahoe/CHK/Merkle.hs @@ -1,7 +1,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} -module Merkle ( +module Tahoe.CHK.Merkle ( MerkleTree (MerkleNode, MerkleLeaf), Direction (..), leafNumberToNodeNumber, @@ -42,12 +42,12 @@ import Data.ByteString.Base32 ( encodeBase32Unpadded, ) -import Crypto ( +import Tahoe.CHK.Crypto ( taggedHash, taggedPairHash, ) -import Util ( +import Tahoe.CHK.Util ( nextPowerOf, toBinary, ) diff --git a/src/Tahoe/CHK/Netstring.hs b/src/Tahoe/CHK/Netstring.hs new file mode 100644 index 0000000000000000000000000000000000000000..05466a4c2cc8a73a4d49faa981c2f6bc1d5bf44d --- /dev/null +++ b/src/Tahoe/CHK/Netstring.hs @@ -0,0 +1,15 @@ +module Tahoe.CHK.Netstring ( + netstring, +) where + +import qualified Data.ByteString as B +import qualified Data.ByteString.Char8 as C8 + +netstring :: B.ByteString -> B.ByteString +netstring xs = + B.concat + [ C8.pack . show . B.length $ xs + , ":" + , xs + , "," + ] diff --git a/src/Types.hs b/src/Tahoe/CHK/Types.hs similarity index 98% rename from src/Types.hs rename to src/Tahoe/CHK/Types.hs index dc1287d2ab5396a8d078fd3d1c27c79866689f9a..480f4ba3346c432e6c7f8866b36381b8110f4923 100644 --- a/src/Types.hs +++ b/src/Tahoe/CHK/Types.hs @@ -1,4 +1,4 @@ -module Types where +module Tahoe.CHK.Types where import Data.Word ( Word16, diff --git a/src/URIExtension.hs b/src/Tahoe/CHK/URIExtension.hs similarity index 97% rename from src/URIExtension.hs rename to src/Tahoe/CHK/URIExtension.hs index 4562b4d51d0a0ba0f23a4d0e71c3781ac5d6ef48..b26f186e5b02785a1da8d1fc43db0c4bf0c5a44b 100644 --- a/src/URIExtension.hs +++ b/src/Tahoe/CHK/URIExtension.hs @@ -1,4 +1,4 @@ -module URIExtension ( +module Tahoe.CHK.URIExtension ( URIExtension (..), uriExtensionToBytes, showBytes, @@ -27,9 +27,9 @@ import Data.List ( sort, ) -import Types +import Tahoe.CHK.Types -import Netstring ( +import Tahoe.CHK.Netstring ( netstring, ) diff --git a/src/Upload.hs b/src/Tahoe/CHK/Upload.hs similarity index 98% rename from src/Upload.hs rename to src/Tahoe/CHK/Upload.hs index f67ddc32172bfa1569d872cded9a6f1ceeaffc5b..5a72e612b24f55303f73315a016b2c8b1a322c66 100644 --- a/src/Upload.hs +++ b/src/Tahoe/CHK/Upload.hs @@ -1,6 +1,6 @@ {-# LANGUAGE ScopedTypeVariables #-} -module Upload ( +module Tahoe.CHK.Upload ( UploadResult (uploadResultReadCap, uploadResultExistingShares, uploadResultShareMap), Parameters (Parameters), defaultParameters, @@ -71,7 +71,7 @@ import Crypto.Classes ( buildKeyIO, ) -import qualified Capability as Cap +import qualified Tahoe.CHK.Capability as Cap import System.FilePath.Posix ( (</>), @@ -101,16 +101,16 @@ import Crypto.Cipher.AES128 ( AESKey128, ) -import Crypto ( +import Tahoe.CHK.Crypto ( convergenceEncryptionHashLazy, storageIndexHash, ) -import Types +import Tahoe.CHK.Types -import Util (nextMultiple) +import Tahoe.CHK.Util (nextMultiple) -import CHK ( +import Tahoe.CHK ( chkEncrypt, makeChkEncoder, ) diff --git a/src/Util.hs b/src/Tahoe/CHK/Util.hs similarity index 97% rename from src/Util.hs rename to src/Tahoe/CHK/Util.hs index eb0c1124026b0e3519901492fc8b733f7412367a..fdee66f9076f42b93b362188e62e92957d7d2ec2 100644 --- a/src/Util.hs +++ b/src/Tahoe/CHK/Util.hs @@ -1,4 +1,4 @@ -module Util where +module Tahoe.CHK.Util where -- The smallest multiple of `multiplier` which is >= `value`. nextMultiple :: (Integral m, Integral v) => m -> v -> v diff --git a/src/ZFEC.hs b/src/Tahoe/CHK/ZFEC.hs similarity index 99% rename from src/ZFEC.hs rename to src/Tahoe/CHK/ZFEC.hs index 289380c3626ca4c6cf523ee1b91ffcede15432ad..0b4d455fcbd2a96354932e4d055b8219005808ce 100644 --- a/src/ZFEC.hs +++ b/src/Tahoe/CHK/ZFEC.hs @@ -4,7 +4,7 @@ {- | Bindings to the Botan3 implementation of ZFEC. | Work-in-progress. -} -module ZFEC (encode, encodePadded, decode, decodePadded) where +module Tahoe.CHK.ZFEC (encode, encodePadded, decode, decodePadded) where import Control.Monad (join, zipWithM) import qualified Data.ByteString as B diff --git a/tahoe-lafs-immutable-uploader.cabal b/tahoe-chk.cabal similarity index 56% rename from tahoe-lafs-immutable-uploader.cabal rename to tahoe-chk.cabal index db46f275c4d8ba654ddf0144cc0a5c21cf0b649c..17457e5d4d588836416ed2b06324d20b4e243b8a 100644 --- a/tahoe-lafs-immutable-uploader.cabal +++ b/tahoe-chk.cabal @@ -1,27 +1,17 @@ cabal-version: 1.12 - --- This file has been generated from package.yaml by hpack version 0.33.0. --- --- see: https://github.com/sol/hpack --- --- hash: 9bdeb16269c9d03dfb74635c2280f169d57160a4866c8ba2ab8f996c571202ae - -name: tahoe-lafs-immutable-uploader +name: tahoe-chk version: 0.1.0.0 -description: - Please see the README on GitHub at <https://github.com/githubuser/tahoe-lafs-immutable-uploader#readme> - -homepage: - https://github.com/githubuser/tahoe-lafs-immutable-uploader#readme - -bug-reports: - https://github.com/githubuser/tahoe-lafs-immutable-uploader/issues +synopsis: + Please see the README on GitHub at <https://gitlab.com/exarkun/chk.hs/-/blob/master/README.md> -author: Author name here -maintainer: example@example.com -copyright: 2020 Author name here +homepage: https://gitlab.com/exarkun/chk.hs +bug-reports: https://gitlab.com/exarkun/chk.hs/-/issues +author: Jean-Paul Calderone +maintainer: PrivateStorage.io, Inc. +copyright: 2020-2022 PrivateStorage.io, Inc. license: BSD3 license-file: LICENSE +category: Cryptography,Library,Parsers,Security build-type: Simple extra-source-files: ChangeLog.md @@ -29,23 +19,22 @@ extra-source-files: source-repository head type: git - location: https://github.com/githubuser/tahoe-lafs-immutable-uploader + location: https://gitlab.com/exarkun/chk.hs.git library exposed-modules: - Capability - CHK - Crypto - Lib - Merkle - Netstring - Types - Upload - URIExtension - Util - ZFEC + Tahoe.CHK + Tahoe.CHK.Capability + Tahoe.CHK.Crypto + Tahoe.CHK.Merkle + Tahoe.CHK.Netstring + Tahoe.CHK.Types + Tahoe.CHK.Upload + Tahoe.CHK.URIExtension + Tahoe.CHK.Util + Tahoe.CHK.ZFEC - other-modules: Paths_tahoe_lafs_immutable_uploader + other-modules: Paths_tahoe_chk ghc-options: -Wall pkgconfig-depends: botan-3 hs-source-dirs: src @@ -74,28 +63,28 @@ library default-language: Haskell2010 -executable tahoe-lafs-encrypt-chk +executable tahoe-chk-encrypt main-is: Main.hs - other-modules: Paths_tahoe_lafs_immutable_uploader + other-modules: Paths_tahoe_chk hs-source-dirs: app default-extensions: OverloadedStrings ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N build-depends: - base >=4.7 && <5 - , base32 >=0.2 + base >=4.7 && <5 + , base32 >=0.2 , bytestring , optparse-applicative - , tahoe-lafs-immutable-uploader + , tahoe-chk , text default-language: Haskell2010 -test-suite tahoe-lafs-immutable-uploader-test +test-suite tahoe-chk-tests type: exitcode-stdio-1.0 main-is: Spec.hs other-modules: NullServer - Paths_tahoe_lafs_immutable_uploader + Paths_tahoe_chk SpecCHK SpecCrypto SpecMerkle @@ -111,8 +100,8 @@ test-suite tahoe-lafs-immutable-uploader-test ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N build-depends: aeson - , base >=4.7 && <5 - , base32 >=0.2 + , base >=4.7 && <5 + , base32 >=0.2 , base64 , bytestring , cereal @@ -124,7 +113,7 @@ test-suite tahoe-lafs-immutable-uploader-test , hedgehog , megaparsec , scientific - , tahoe-lafs-immutable-uploader + , tahoe-chk , tasty , tasty-hedgehog , tasty-hunit diff --git a/test/NullServer.hs b/test/NullServer.hs index 47fa1fbca369f3b2b3794ac7f487fee7f0655075..deaefbf82e9e3c15b3a4dca5f79eacebc2d5eed7 100644 --- a/test/NullServer.hs +++ b/test/NullServer.hs @@ -1,6 +1,6 @@ module NullServer where -import Types ( +import Tahoe.CHK.Types ( StorageServer (..), ) diff --git a/test/SpecCHK.hs b/test/SpecCHK.hs index 91c0f9135fdddab9d51947d8a32b0565a7075058..1d6d5b97014c190f67753d62689639f9132a9502 100644 --- a/test/SpecCHK.hs +++ b/test/SpecCHK.hs @@ -10,7 +10,7 @@ import Control.Arrow ( import Data.ByteString.Base64 (encodeBase64) -import Hedgehog +import Hedgehog (Property, assert, diff, forAll, property) import qualified Hedgehog.Gen as Gen import qualified Hedgehog.Range as Range @@ -36,13 +36,13 @@ import Test.Tasty.HUnit ( assertFailure, testCase, ) -import Test.Tasty.Hedgehog +import Test.Tasty.Hedgehog (testProperty) -import Types ( +import Tahoe.CHK.Types ( Parameters (..), ) -import Upload ( +import Tahoe.CHK.Upload ( UploadResult (..), adjustSegmentSize, getConvergentKey, @@ -50,11 +50,11 @@ import Upload ( store, ) -import CHK ( +import Tahoe.CHK ( chkEncrypt, ) -import Capability (CHK (CHKReader), dangerRealShow, pCapability, pReader) +import Tahoe.CHK.Capability (dangerRealShow, pCapability, pReader) import Crypto.Cipher.AES128 ( AESKey128, diff --git a/test/SpecCrypto.hs b/test/SpecCrypto.hs index 09b9fd6d4421266252a34efbaa67341bd255ea5b..c74e82dda5ad4dc403aa2bf28bd6e1ca33ca8e5e 100644 --- a/test/SpecCrypto.hs +++ b/test/SpecCrypto.hs @@ -20,14 +20,14 @@ import Test.Tasty.HUnit ( testCase, ) -import Crypto ( +import Tahoe.CHK.Crypto ( convergenceEncryptionTag, sha256, sha256d, storageIndexHash, ) -import Types ( +import Tahoe.CHK.Types ( Parameters (..), ) diff --git a/test/SpecMerkle.hs b/test/SpecMerkle.hs index 58541d386208b57719fbd35750b6dc26ae7740ad..a746aabd4a6860d7113a5237bb3f01358e591001 100644 --- a/test/SpecMerkle.hs +++ b/test/SpecMerkle.hs @@ -39,11 +39,11 @@ import Data.ByteString.Base32 ( encodeBase32Unpadded, ) -import Crypto ( +import Tahoe.CHK.Crypto ( taggedHash, ) -import Merkle ( +import Tahoe.CHK.Merkle ( Direction (..), MerkleTree (MerkleLeaf, MerkleNode), breadthFirstList, diff --git a/test/SpecUpload.hs b/test/SpecUpload.hs index b72d1935c9cdb15949b5685747d55e4f1b8d54a3..c1c70a81f0fa58b5a05cb8f730c0bdd43c5c94af 100644 --- a/test/SpecUpload.hs +++ b/test/SpecUpload.hs @@ -23,13 +23,13 @@ import Test.Tasty.HUnit ( import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL -import Types ( +import Tahoe.CHK.Types ( Parameters (..), Required, Size, ) -import Upload ( +import Tahoe.CHK.Upload ( adjustSegmentSize, getConvergentKey, ) diff --git a/test/SpecZFEC.hs b/test/SpecZFEC.hs index 7a257ca54f467414c6d3cfa7e59b3b4a93b92713..50ab7865be71a9fb18bb971dcbddfc5500dce086 100644 --- a/test/SpecZFEC.hs +++ b/test/SpecZFEC.hs @@ -19,7 +19,7 @@ import qualified Hedgehog.Gen as Gen import qualified Hedgehog.Range as Range import Test.Tasty.Hedgehog -import qualified ZFEC +import qualified Tahoe.CHK.ZFEC as ZFEC tests :: TestTree tests = diff --git a/test/Vectors.hs b/test/Vectors.hs index bb6ed5165385858b2b76c1fae989bb8f5dc96c55..c97dc03a81f5753e6510579398269af3e7b158de 100644 --- a/test/Vectors.hs +++ b/test/Vectors.hs @@ -35,7 +35,7 @@ import Data.ByteString.Base64 ( encodeBase64, ) -import Types ( +import Tahoe.CHK.Types ( Parameters (..), )