Merge pull request #6 from oupson/2-mount-a-gocryptfs-filesystem-on-linux
2 mount a gocryptfs filesystem on linux
This commit is contained in:
commit
004a9b4b54
|
@ -50,18 +50,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "0.7.18"
|
version = "0.7.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
|
checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.57"
|
version = "1.0.65"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
|
checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atty"
|
name = "atty"
|
||||||
|
@ -100,9 +100,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
version = "0.10.2"
|
version = "0.10.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
|
checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
@ -116,6 +116,12 @@ dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
@ -143,16 +149,16 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "3.1.18"
|
version = "3.2.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b"
|
checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
"atty",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
"clap_lex",
|
"clap_lex",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"lazy_static",
|
"once_cell",
|
||||||
"strsim",
|
"strsim",
|
||||||
"termcolor",
|
"termcolor",
|
||||||
"textwrap",
|
"textwrap",
|
||||||
|
@ -160,9 +166,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "3.1.18"
|
version = "3.2.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "25320346e922cffe59c0bbc5410c8d8784509efb321488971081313cb1e1a33c"
|
checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro-error",
|
"proc-macro-error",
|
||||||
|
@ -173,27 +179,27 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_lex"
|
name = "clap_lex"
|
||||||
version = "0.2.0"
|
version = "0.2.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213"
|
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"os_str_bytes",
|
"os_str_bytes",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.2"
|
version = "0.2.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"
|
checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
version = "0.1.3"
|
version = "0.1.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8"
|
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
"typenum",
|
"typenum",
|
||||||
|
@ -210,9 +216,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.3"
|
version = "0.10.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
|
checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer",
|
"block-buffer",
|
||||||
"crypto-common",
|
"crypto-common",
|
||||||
|
@ -230,9 +236,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "env_logger"
|
name = "env_logger"
|
||||||
version = "0.9.0"
|
version = "0.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
|
checksum = "c90bf5f19754d10198ccb95b70664fc925bd1fc090a0fd9a6ebc54acc8cd6272"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
"atty",
|
||||||
"humantime",
|
"humantime",
|
||||||
|
@ -242,10 +248,25 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "fuser"
|
||||||
version = "0.14.5"
|
version = "0.11.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
|
checksum = "104ed58f182bc2975062cd3fab229e82b5762de420e26cf5645f661402694599"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"memchr",
|
||||||
|
"page_size",
|
||||||
|
"smallvec",
|
||||||
|
"users",
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"typenum",
|
"typenum",
|
||||||
"version_check",
|
"version_check",
|
||||||
|
@ -253,9 +274,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.6"
|
version = "0.2.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
|
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -274,9 +295,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.11.2"
|
version = "0.12.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
|
@ -319,9 +340,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "1.8.1"
|
version = "1.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
|
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
|
@ -339,21 +360,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.2"
|
version = "1.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
|
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lazy_static"
|
|
||||||
version = "1.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.126"
|
version = "0.2.134"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
|
@ -370,6 +385,12 @@ version = "2.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opaque-debug"
|
name = "opaque-debug"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
@ -378,9 +399,19 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "os_str_bytes"
|
name = "os_str_bytes"
|
||||||
version = "6.0.1"
|
version = "6.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "029d8d0b2f198229de29dca79676f2738ff952edf3fde542eb8bf94d8c21b435"
|
checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "page_size"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eebde548fbbf1ea81a99b128872779c437752fb99f217c45245e1a61dcd9edcd"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "password-hash"
|
name = "password-hash"
|
||||||
|
@ -440,36 +471,36 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.39"
|
version = "1.0.46"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
|
checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.18"
|
version = "1.0.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
|
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_core"
|
name = "rand_core"
|
||||||
version = "0.6.3"
|
version = "0.6.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.5.6"
|
version = "1.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1"
|
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
@ -478,9 +509,19 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.6.26"
|
version = "0.6.27"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
|
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rpassword"
|
||||||
|
version = "7.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "26b763cb66df1c928432cc35053f8bd4cec3335d8559fc16010017d16b3c1680"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustcryptfs"
|
name = "rustcryptfs"
|
||||||
|
@ -490,11 +531,24 @@ dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"log",
|
"log",
|
||||||
|
"rpassword",
|
||||||
"rustcryptfs-lib",
|
"rustcryptfs-lib",
|
||||||
|
"rustcryptfs-mount",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustcryptfs-fuse"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"fuser",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"rustcryptfs-lib",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustcryptfs-lib"
|
name = "rustcryptfs-lib"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -512,11 +566,18 @@ dependencies = [
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustcryptfs-mount"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"rustcryptfs-fuse",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.10"
|
version = "1.0.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
|
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "salsa20"
|
name = "salsa20"
|
||||||
|
@ -542,18 +603,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.137"
|
version = "1.0.145"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
|
checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.137"
|
version = "1.0.145"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
|
checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -562,9 +623,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.81"
|
version = "1.0.85"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
|
checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
|
@ -573,15 +634,21 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha2"
|
name = "sha2"
|
||||||
version = "0.10.2"
|
version = "0.10.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676"
|
checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cpufeatures",
|
"cpufeatures",
|
||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
|
@ -596,15 +663,27 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.95"
|
version = "1.0.102"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942"
|
checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "synstructure"
|
||||||
|
version = "0.12.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "termcolor"
|
name = "termcolor"
|
||||||
version = "1.1.3"
|
version = "1.1.3"
|
||||||
|
@ -616,24 +695,24 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "textwrap"
|
name = "textwrap"
|
||||||
version = "0.15.0"
|
version = "0.15.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
|
checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.31"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
|
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.31"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
|
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -648,9 +727,15 @@ checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.0"
|
version = "1.0.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
|
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "universal-hash"
|
name = "universal-hash"
|
||||||
|
@ -662,6 +747,16 @@ dependencies = [
|
||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "users"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
|
@ -670,9 +765,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.10.2+wasi-snapshot-preview1"
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
|
@ -704,3 +799,24 @@ name = "winapi-x86_64-pc-windows-gnu"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "332f188cc1bcf1fe1064b8c58d150f497e697f49774aa846f2dc949d9a25f236"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a0fbc82b82efe24da867ee52e015e58178684bd9dd64c34e66bdf21da2582a9f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"syn",
|
||||||
|
"synstructure",
|
||||||
|
]
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"rustcryptfs",
|
"rustcryptfs",
|
||||||
"rustcryptfs-lib"
|
"rustcryptfs-lib",
|
||||||
|
"rustcryptfs-fuse",
|
||||||
|
"rustcryptfs-mount"
|
||||||
]
|
]
|
||||||
|
|
|
@ -1 +1,10 @@
|
||||||
# RustCryptFS
|
# RustCryptFS
|
||||||
|
An implementation of [gocryptfs](https://github.com/rfjakob/gocryptfs) in Rust.
|
||||||
|
|
||||||
|
## Supported plaforms and features
|
||||||
|
- [x] Linux (via FUSE)
|
||||||
|
- [x] read
|
||||||
|
- [ ] write
|
||||||
|
- [ ] Windows
|
||||||
|
- [ ] read
|
||||||
|
- [ ] write
|
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "rustcryptfs-fuse"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
fuser = { version = "0.11", default-features = false }
|
||||||
|
log = "0.4"
|
||||||
|
rustcryptfs-lib = { path = "../rustcryptfs-lib" }
|
||||||
|
thiserror = "1.0"
|
||||||
|
libc = "0.2"
|
|
@ -0,0 +1,382 @@
|
||||||
|
use std::{
|
||||||
|
collections::BTreeMap,
|
||||||
|
ffi::OsStr,
|
||||||
|
fs::{File, FileType as StdFileType},
|
||||||
|
io::{Error as IoError, Read, Result as IoResult, Seek, SeekFrom},
|
||||||
|
ops::Add,
|
||||||
|
os::unix::prelude::{FileTypeExt, MetadataExt, OsStrExt, PermissionsExt},
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
time::{Duration, UNIX_EPOCH},
|
||||||
|
};
|
||||||
|
|
||||||
|
use fuser::{FileAttr, FileType, Filesystem, FUSE_ROOT_ID};
|
||||||
|
use rustcryptfs_lib::{content::ContentEnc, GocryptFs};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
error::{ErrorExt, Result},
|
||||||
|
inode_cache::{InodeCache, InodeCacheExt},
|
||||||
|
};
|
||||||
|
|
||||||
|
trait OptionExt<R> {
|
||||||
|
fn enoent(self) -> IoResult<R>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> OptionExt<R> for Option<R> {
|
||||||
|
fn enoent(self) -> IoResult<R> {
|
||||||
|
match self {
|
||||||
|
Some(r) => Ok(r),
|
||||||
|
None => Err(IoError::from_raw_os_error(libc::ENOENT)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const BLOCK_SIZE: u64 = 4096;
|
||||||
|
|
||||||
|
pub struct EncryptedFs {
|
||||||
|
fs: GocryptFs,
|
||||||
|
inode_cache: InodeCache,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EncryptedFs {
|
||||||
|
pub fn new<P>(path: P, password: &str) -> Result<Self>
|
||||||
|
where
|
||||||
|
P: AsRef<Path>,
|
||||||
|
{
|
||||||
|
let path = path.as_ref();
|
||||||
|
|
||||||
|
log::info!("Opening dir ...");
|
||||||
|
let fs = GocryptFs::open(path, password)?;
|
||||||
|
|
||||||
|
println!("Filesystem mounted and ready.");
|
||||||
|
|
||||||
|
let mut inode_cache = BTreeMap::new();
|
||||||
|
inode_cache.insert(FUSE_ROOT_ID, path.to_path_buf());
|
||||||
|
|
||||||
|
Ok(Self { fs, inode_cache })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mount<P>(self, mountpoint: P) -> std::io::Result<()>
|
||||||
|
where
|
||||||
|
P: AsRef<Path>,
|
||||||
|
{
|
||||||
|
fuser::mount2(self, mountpoint, &[])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_file_type(file_type: StdFileType) -> FileType {
|
||||||
|
if file_type.is_file() {
|
||||||
|
FileType::RegularFile
|
||||||
|
} else if file_type.is_dir() {
|
||||||
|
FileType::Directory
|
||||||
|
} else if file_type.is_symlink() {
|
||||||
|
FileType::Symlink
|
||||||
|
} else if file_type.is_socket() {
|
||||||
|
FileType::Socket
|
||||||
|
} else if file_type.is_char_device() {
|
||||||
|
FileType::CharDevice
|
||||||
|
} else if file_type.is_block_device() {
|
||||||
|
FileType::BlockDevice
|
||||||
|
} else if file_type.is_fifo() {
|
||||||
|
FileType::NamedPipe
|
||||||
|
} else {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_path(&self, ino: u64) -> Option<&PathBuf> {
|
||||||
|
self.inode_cache.get_path(ino)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_attr<P>(path: P, ino: u64) -> std::io::Result<FileAttr>
|
||||||
|
where
|
||||||
|
P: AsRef<Path>,
|
||||||
|
{
|
||||||
|
let meta = std::fs::metadata(&path)?;
|
||||||
|
|
||||||
|
let file_type = Self::get_file_type(meta.file_type());
|
||||||
|
|
||||||
|
let file_size = if meta.is_file() {
|
||||||
|
ContentEnc::get_real_size(meta.size())
|
||||||
|
} else {
|
||||||
|
meta.size()
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(FileAttr {
|
||||||
|
ino,
|
||||||
|
size: file_size,
|
||||||
|
blocks: (file_size + BLOCK_SIZE - 1) / BLOCK_SIZE,
|
||||||
|
atime: meta.accessed()?,
|
||||||
|
mtime: meta.modified()?,
|
||||||
|
ctime: UNIX_EPOCH.add(Duration::new(meta.ctime() as u64, 0)),
|
||||||
|
crtime: UNIX_EPOCH.add(Duration::new(meta.ctime() as u64, 0)),
|
||||||
|
kind: file_type,
|
||||||
|
perm: meta.permissions().mode() as u16,
|
||||||
|
nlink: meta.nlink() as u32,
|
||||||
|
uid: meta.uid(),
|
||||||
|
gid: meta.gid(),
|
||||||
|
rdev: 0,
|
||||||
|
blksize: BLOCK_SIZE as u32,
|
||||||
|
flags: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup_impl(
|
||||||
|
&mut self,
|
||||||
|
parent: u64,
|
||||||
|
name: &std::ffi::OsStr,
|
||||||
|
) -> rustcryptfs_lib::error::Result<(Duration, FileAttr, u64)> {
|
||||||
|
let parent = self.get_path(parent).enoent()?;
|
||||||
|
let iv = std::fs::read(parent.join("gocryptfs.diriv"))?;
|
||||||
|
let dir_decoder = self.fs.filename_decoder().get_cipher_for_dir(&iv);
|
||||||
|
|
||||||
|
let encrypted_name = dir_decoder.encrypt_filename(&name.to_string_lossy())?;
|
||||||
|
|
||||||
|
let encrypted_name = match &encrypted_name {
|
||||||
|
rustcryptfs_lib::filename::EncodedFilename::ShortFilename(s) => s,
|
||||||
|
rustcryptfs_lib::filename::EncodedFilename::LongFilename(l) => l.filename(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let file_path = parent.join(encrypted_name);
|
||||||
|
|
||||||
|
if file_path.exists() {
|
||||||
|
let (ino, file_path) = self.inode_cache.get_or_insert_inode(file_path);
|
||||||
|
|
||||||
|
Ok((Duration::new(0, 0), Self::get_attr(file_path, ino)?, 0))
|
||||||
|
} else {
|
||||||
|
Err(IoError::from_raw_os_error(libc::ENOENT).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_dir_impl(
|
||||||
|
&mut self,
|
||||||
|
ino: u64,
|
||||||
|
offset: i64,
|
||||||
|
reply: &mut fuser::ReplyDirectory,
|
||||||
|
) -> rustcryptfs_lib::error::Result<()> {
|
||||||
|
let folder_path = &self.inode_cache.get_path(ino).enoent()?.clone();
|
||||||
|
let iv = std::fs::read(folder_path.join("gocryptfs.diriv"))?;
|
||||||
|
|
||||||
|
let dir_decoder = self.fs.filename_decoder().get_cipher_for_dir(&iv);
|
||||||
|
|
||||||
|
if offset == 0 {
|
||||||
|
let ino_parent = if ino == FUSE_ROOT_ID {
|
||||||
|
FUSE_ROOT_ID
|
||||||
|
} else {
|
||||||
|
let parent = folder_path.parent().enoent()?;
|
||||||
|
self.inode_cache
|
||||||
|
.iter()
|
||||||
|
.find_map(|(ino, p)| if p == parent { Some(*ino) } else { None })
|
||||||
|
.enoent()?
|
||||||
|
};
|
||||||
|
|
||||||
|
if !reply.add(ino, 1, FileType::Directory, ".") {
|
||||||
|
if reply.add(ino_parent, 2, FileType::Directory, "..") {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (index, (meta, encrypted_name, name)) in std::fs::read_dir(folder_path)?
|
||||||
|
.flat_map(|e| e.ok())
|
||||||
|
.flat_map(|dir| match extract_name(&dir, folder_path, &dir_decoder) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!(
|
||||||
|
"Failed to extract name of entry {:?} : {}",
|
||||||
|
dir.file_name(),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.skip(offset as usize)
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
let (inode, _) = self
|
||||||
|
.inode_cache
|
||||||
|
.get_or_insert_inode(folder_path.join(&encrypted_name));
|
||||||
|
|
||||||
|
let file_type = Self::get_file_type(meta.file_type());
|
||||||
|
|
||||||
|
let buffer_full: bool = reply.add(
|
||||||
|
inode,
|
||||||
|
offset + index as i64 + 1 + 2,
|
||||||
|
file_type,
|
||||||
|
OsStr::from_bytes(name.as_bytes()),
|
||||||
|
);
|
||||||
|
|
||||||
|
if buffer_full {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_impl(
|
||||||
|
&mut self,
|
||||||
|
ino: u64,
|
||||||
|
offset: i64,
|
||||||
|
size: usize,
|
||||||
|
) -> rustcryptfs_lib::error::Result<Vec<u8>> {
|
||||||
|
let file_path = self.get_path(ino).enoent()?;
|
||||||
|
let mut file = File::open(file_path)?;
|
||||||
|
let decoder = self.fs.content_decoder();
|
||||||
|
|
||||||
|
let mut buf = [0u8; 18];
|
||||||
|
let n = file.read(&mut buf)?;
|
||||||
|
let id = if n < 18 { None } else { Some(&buf[2..]) };
|
||||||
|
|
||||||
|
let mut block_index = offset as u64 / 4096;
|
||||||
|
|
||||||
|
let mut buffer = Vec::with_capacity(size);
|
||||||
|
|
||||||
|
let mut rem = size;
|
||||||
|
|
||||||
|
let mut buf = [0u8; 4096 + 32];
|
||||||
|
|
||||||
|
file.seek(SeekFrom::Start(18 + block_index * (4096 + 32)))?;
|
||||||
|
|
||||||
|
{
|
||||||
|
let n = file.read(&mut buf)?;
|
||||||
|
|
||||||
|
let res = decoder.decrypt_block(&buf[..n], block_index, id)?;
|
||||||
|
|
||||||
|
let seek = (offset as u64 - block_index * 4096) as usize;
|
||||||
|
buffer.extend_from_slice(&res[seek..]);
|
||||||
|
|
||||||
|
block_index += 1;
|
||||||
|
|
||||||
|
rem -= res.len() - seek;
|
||||||
|
}
|
||||||
|
|
||||||
|
while rem > 0 {
|
||||||
|
let n = file.read(&mut buf)?;
|
||||||
|
|
||||||
|
if n == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = decoder.decrypt_block(&buf[..n], block_index, id)?;
|
||||||
|
|
||||||
|
let size = res.len().min(rem);
|
||||||
|
|
||||||
|
buffer.extend_from_slice(&res[..size]);
|
||||||
|
|
||||||
|
block_index += 1;
|
||||||
|
|
||||||
|
rem -= size;
|
||||||
|
}
|
||||||
|
Ok(buffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Filesystem for EncryptedFs {
|
||||||
|
fn access(
|
||||||
|
&mut self,
|
||||||
|
_req: &fuser::Request<'_>,
|
||||||
|
ino: u64,
|
||||||
|
_mask: i32,
|
||||||
|
reply: fuser::ReplyEmpty,
|
||||||
|
) {
|
||||||
|
if let Some(_path) = self.get_path(ino) {
|
||||||
|
reply.ok()
|
||||||
|
} else {
|
||||||
|
reply.error(libc::ENOENT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getattr(&mut self, _req: &fuser::Request<'_>, ino: u64, reply: fuser::ReplyAttr) {
|
||||||
|
if let Some(path) = self.get_path(ino) {
|
||||||
|
match Self::get_attr(path, ino) {
|
||||||
|
Ok(attr) => reply.attr(&Duration::new(0, 0), &attr),
|
||||||
|
Err(e) => reply.error(e.raw_os_error().unwrap()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reply.error(libc::ENOENT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup(
|
||||||
|
&mut self,
|
||||||
|
_req: &fuser::Request<'_>,
|
||||||
|
parent: u64,
|
||||||
|
name: &std::ffi::OsStr,
|
||||||
|
reply: fuser::ReplyEntry,
|
||||||
|
) {
|
||||||
|
match self.lookup_impl(parent, name) {
|
||||||
|
Ok((ttl, attr, generation)) => reply.entry(&ttl, &attr, generation),
|
||||||
|
Err(e) => {
|
||||||
|
log::debug!("error on lookup : {}", e);
|
||||||
|
reply.error(e.to_raw_code())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn readdir(
|
||||||
|
&mut self,
|
||||||
|
_req: &fuser::Request<'_>,
|
||||||
|
ino: u64,
|
||||||
|
_fh: u64,
|
||||||
|
offset: i64,
|
||||||
|
mut reply: fuser::ReplyDirectory,
|
||||||
|
) {
|
||||||
|
match self.read_dir_impl(ino, offset, &mut reply) {
|
||||||
|
Ok(()) => reply.ok(),
|
||||||
|
Err(e) => {
|
||||||
|
log::debug!("error on readdir : {}", e);
|
||||||
|
reply.error(e.to_raw_code())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(
|
||||||
|
&mut self,
|
||||||
|
_req: &fuser::Request<'_>,
|
||||||
|
ino: u64,
|
||||||
|
_fh: u64,
|
||||||
|
offset: i64,
|
||||||
|
size: u32,
|
||||||
|
_flags: i32,
|
||||||
|
_lock_owner: Option<u64>,
|
||||||
|
reply: fuser::ReplyData,
|
||||||
|
) {
|
||||||
|
match self.read_impl(ino, offset, size as usize) {
|
||||||
|
Ok(data) => reply.data(&data),
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("read : {}", e);
|
||||||
|
reply.error(e.to_raw_code())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_name(
|
||||||
|
dir: &std::fs::DirEntry,
|
||||||
|
folder_path: &Path,
|
||||||
|
dir_decoder: &rustcryptfs_lib::filename::DirFilenameCipher,
|
||||||
|
) -> rustcryptfs_lib::error::Result<Option<(std::fs::Metadata, String, String)>> {
|
||||||
|
let filename = dir.file_name();
|
||||||
|
let filename = filename.to_string_lossy();
|
||||||
|
if filename != "gocryptfs.conf" && filename != "gocryptfs.diriv" {
|
||||||
|
if filename.starts_with("gocryptfs.longname.") {
|
||||||
|
if !filename.ends_with(".name") {
|
||||||
|
let filename =
|
||||||
|
std::fs::read_to_string(folder_path.join(format!("{}.name", filename)))?;
|
||||||
|
let decrypted_filename = dir_decoder.decode_filename(filename.as_str())?;
|
||||||
|
Ok(Some((dir.metadata()?, filename, decrypted_filename)))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let decrypted_filename = dir_decoder.decode_filename(&*filename)?;
|
||||||
|
Ok(Some((
|
||||||
|
dir.metadata()?,
|
||||||
|
filename.to_string(),
|
||||||
|
decrypted_filename,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
use rustcryptfs_lib::filename::FilenameCipherError;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error(transparent)]
|
||||||
|
IoError(#[from] std::io::Error),
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
RustCryptFsError(#[from] rustcryptfs_lib::error::Error),
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
RustCryptFsFilenameError(#[from] FilenameCipherError),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub(crate) trait ErrorExt {
|
||||||
|
fn to_raw_code(&self) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ErrorExt for rustcryptfs_lib::error::Error {
|
||||||
|
fn to_raw_code(&self) -> i32 {
|
||||||
|
match self {
|
||||||
|
rustcryptfs_lib::error::Error::FilenameCipherError(_) => libc::EIO,
|
||||||
|
rustcryptfs_lib::error::Error::ContentCipherError(_) => libc::EIO,
|
||||||
|
rustcryptfs_lib::error::Error::ConfigError(_) => todo!(),
|
||||||
|
rustcryptfs_lib::error::Error::JsonError(_) => todo!(),
|
||||||
|
rustcryptfs_lib::error::Error::IoError(e) => e.raw_os_error().unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
use std::{collections::BTreeMap, path::PathBuf};
|
||||||
|
|
||||||
|
pub(crate) type InodeCache = BTreeMap<u64, PathBuf>;
|
||||||
|
|
||||||
|
pub(crate) trait InodeCacheExt {
|
||||||
|
fn get_or_insert_inode(&mut self, file_path: PathBuf) -> (u64, PathBuf);
|
||||||
|
|
||||||
|
fn get_path(&self, ino: u64) -> Option<&PathBuf>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InodeCacheExt for InodeCache {
|
||||||
|
// TODO Try to avoid clone
|
||||||
|
fn get_or_insert_inode(&mut self, file_path: PathBuf) -> (u64, PathBuf) {
|
||||||
|
if let Some((ino, path)) =
|
||||||
|
self.iter()
|
||||||
|
.find_map(|(i, p)| if p.eq(&file_path) { Some((i, p)) } else { None })
|
||||||
|
{
|
||||||
|
(*ino, path.clone())
|
||||||
|
} else {
|
||||||
|
let ino = self.len() as u64 + 1;
|
||||||
|
self.insert(ino, file_path);
|
||||||
|
|
||||||
|
(ino, self.get(&ino).unwrap().clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_path(&self, ino: u64) -> Option<&PathBuf> {
|
||||||
|
// TODO CHECK PERM
|
||||||
|
self.get(&ino)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
mod encrypted_filesystem;
|
||||||
|
mod inode_cache;
|
||||||
|
|
||||||
|
pub mod error;
|
||||||
|
|
||||||
|
pub use encrypted_filesystem::EncryptedFs;
|
|
@ -0,0 +1,29 @@
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum ConfigError {
|
||||||
|
#[error(transparent)]
|
||||||
|
ScryptError(#[from] ScryptError),
|
||||||
|
#[error(transparent)]
|
||||||
|
HdkfError(#[from] hkdf::InvalidLength),
|
||||||
|
#[error("Failed to decode base64")]
|
||||||
|
Base64Error(#[from] base64::DecodeError),
|
||||||
|
#[error("Failed to decrypt master key")]
|
||||||
|
MasterKeyDecryptError(),
|
||||||
|
#[error("Invalid master key length")]
|
||||||
|
InvalidMasterKeyLengthError(),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<aes_gcm::Error> for ConfigError {
|
||||||
|
fn from(_: aes_gcm::Error) -> Self {
|
||||||
|
Self::MasterKeyDecryptError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum ScryptError {
|
||||||
|
#[error(transparent)]
|
||||||
|
InvalidParams(#[from] scrypt::errors::InvalidParams),
|
||||||
|
#[error(transparent)]
|
||||||
|
InvalidOutputLen(#[from] scrypt::errors::InvalidOutputLen),
|
||||||
|
}
|
|
@ -9,7 +9,9 @@ use aes_gcm::{
|
||||||
};
|
};
|
||||||
use hkdf::Hkdf;
|
use hkdf::Hkdf;
|
||||||
|
|
||||||
use crate::error::{FilenameDecryptError, Result, ScryptError};
|
mod error;
|
||||||
|
|
||||||
|
pub use error::*;
|
||||||
|
|
||||||
/// An enum that contain all the feature flag a gocryptfs config can have.
|
/// An enum that contain all the feature flag a gocryptfs config can have.
|
||||||
#[derive(serde::Deserialize, Debug, PartialEq, Eq, Hash)]
|
#[derive(serde::Deserialize, Debug, PartialEq, Eq, Hash)]
|
||||||
|
@ -69,7 +71,7 @@ impl CryptConf {
|
||||||
/// See gocryptfs documentation about [master key](https://nuetzlich.net/gocryptfs/forward_mode_crypto/#master-key-storage).
|
/// See gocryptfs documentation about [master key](https://nuetzlich.net/gocryptfs/forward_mode_crypto/#master-key-storage).
|
||||||
///
|
///
|
||||||
/// ![TODO NAME THIS IMAGE](https://nuetzlich.net/gocryptfs/img/master-key.svg)
|
/// ![TODO NAME THIS IMAGE](https://nuetzlich.net/gocryptfs/img/master-key.svg)
|
||||||
pub fn get_master_key(&self, password: &[u8]) -> Result<[u8; 32]> {
|
pub fn get_master_key(&self, password: &[u8]) -> Result<[u8; 32], ConfigError> {
|
||||||
let block = base64::decode(&self.encrypted_key)?;
|
let block = base64::decode(&self.encrypted_key)?;
|
||||||
let key = self.scrypt_object.get_hkdf_key(password)?;
|
let key = self.scrypt_object.get_hkdf_key(password)?;
|
||||||
|
|
||||||
|
@ -77,12 +79,14 @@ impl CryptConf {
|
||||||
let tag = &block[block.len() - 16..];
|
let tag = &block[block.len() - 16..];
|
||||||
let ciphertext = &block[16..block.len() - 16];
|
let ciphertext = &block[16..block.len() - 16];
|
||||||
|
|
||||||
let mut buf: [u8; 32] = ciphertext.try_into().expect("TODO");
|
let mut buf: [u8; 32] = ciphertext
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| ConfigError::InvalidMasterKeyLengthError())?;
|
||||||
|
|
||||||
let aes = AesGcm::<Aes256, cipher::consts::U16>::new(Key::from_slice(&key));
|
let aes = AesGcm::<Aes256, cipher::consts::U16>::new(Key::from_slice(&key));
|
||||||
|
|
||||||
aes.decrypt_in_place_detached(
|
aes.decrypt_in_place_detached(
|
||||||
GenericArray::from_slice(&nonce),
|
GenericArray::from_slice(nonce),
|
||||||
&[0u8, 0, 0, 0, 0, 0, 0, 0],
|
&[0u8, 0, 0, 0, 0, 0, 0, 0],
|
||||||
&mut buf,
|
&mut buf,
|
||||||
GenericArray::from_slice(tag),
|
GenericArray::from_slice(tag),
|
||||||
|
@ -133,19 +137,14 @@ pub struct ScryptObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScryptObject {
|
impl ScryptObject {
|
||||||
fn get_hkdf_key(&self, password: &[u8]) -> std::result::Result<Vec<u8>, FilenameDecryptError> {
|
fn get_hkdf_key(&self, password: &[u8]) -> Result<Vec<u8>, ConfigError> {
|
||||||
let mut key = [0u8; 32];
|
let mut key = [0u8; 32];
|
||||||
|
|
||||||
let params = scrypt::Params::new((self.n as f64).log2() as u8, self.r, self.p)
|
let params = scrypt::Params::new((self.n as f64).log2() as u8, self.r, self.p)
|
||||||
.map_err(|e| ScryptError::from(e))?;
|
.map_err(ScryptError::from)?;
|
||||||
|
|
||||||
scrypt::scrypt(
|
scrypt::scrypt(password, &base64::decode(&self.salt)?, ¶ms, &mut key)
|
||||||
password,
|
.map_err(ScryptError::from)?;
|
||||||
&base64::decode(&self.salt).unwrap(),
|
|
||||||
¶ms,
|
|
||||||
&mut key,
|
|
||||||
)
|
|
||||||
.map_err(|e| ScryptError::from(e))?;
|
|
||||||
|
|
||||||
let hdkf = Hkdf::<sha2::Sha256>::new(None, &key);
|
let hdkf = Hkdf::<sha2::Sha256>::new(None, &key);
|
||||||
hdkf.expand(b"AES-GCM file content encryption", &mut key)?;
|
hdkf.expand(b"AES-GCM file content encryption", &mut key)?;
|
|
@ -0,0 +1,19 @@
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum ContentCipherError {
|
||||||
|
#[error("Block is too short")]
|
||||||
|
BlockTooShort(),
|
||||||
|
#[error("all-zero nonce")]
|
||||||
|
AllZeroNonce(),
|
||||||
|
#[error("Failed to decrypt content")]
|
||||||
|
ContentDecryptError(),
|
||||||
|
#[error(transparent)]
|
||||||
|
HdkfError(#[from] hkdf::InvalidLength),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<aes_gcm::Error> for ContentCipherError {
|
||||||
|
fn from(_: aes_gcm::Error) -> Self {
|
||||||
|
Self::ContentDecryptError()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
//! Utilities for file encryption.
|
||||||
|
|
||||||
|
use aes_gcm::{aead::generic_array::GenericArray, aes::Aes256, AeadInPlace, AesGcm, NewAead};
|
||||||
|
use cipher::consts::{U16, U32};
|
||||||
|
use hkdf::Hkdf;
|
||||||
|
|
||||||
|
mod error;
|
||||||
|
|
||||||
|
pub use error::*;
|
||||||
|
|
||||||
|
type Aes256Gcm = AesGcm<Aes256, U16>;
|
||||||
|
|
||||||
|
/// ContentEnc implement all methods related to file encryption.
|
||||||
|
pub struct ContentEnc {
|
||||||
|
key: GenericArray<u8, U32>,
|
||||||
|
iv_len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContentEnc {
|
||||||
|
/// Init a new ContentEnc from the master key and the iv len.
|
||||||
|
pub fn new(master_key: &[u8], iv_len: u8) -> Result<Self, ContentCipherError> {
|
||||||
|
let mut key = [0u8; 32];
|
||||||
|
let hdkf = Hkdf::<sha2::Sha256>::new(None, master_key);
|
||||||
|
hdkf.expand(b"AES-GCM file content encryption", &mut key)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
key: GenericArray::from(key),
|
||||||
|
iv_len: iv_len as usize,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decrypt a encrypted block of len (iv_len + decrypted_block_size + iv_len), with the block number and the file id.
|
||||||
|
pub fn decrypt_block(
|
||||||
|
&self,
|
||||||
|
block: &[u8],
|
||||||
|
block_number: u64,
|
||||||
|
file_id: Option<&[u8]>,
|
||||||
|
) -> Result<Vec<u8>, ContentCipherError> {
|
||||||
|
// TODO NOT BOX
|
||||||
|
if block.is_empty() {
|
||||||
|
return Ok(block.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if block.iter().all(|f| *f == 0) {
|
||||||
|
todo!("black hole")
|
||||||
|
}
|
||||||
|
|
||||||
|
if block.len() < self.iv_len {
|
||||||
|
return Err(ContentCipherError::BlockTooShort());
|
||||||
|
}
|
||||||
|
|
||||||
|
let nonce = &block[..self.iv_len];
|
||||||
|
let tag = &block[block.len() - self.iv_len..];
|
||||||
|
let ciphertext = &block[self.iv_len..block.len() - self.iv_len];
|
||||||
|
|
||||||
|
if nonce.iter().all(|f| *f == 0) {
|
||||||
|
return Err(ContentCipherError::AllZeroNonce());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buf = Vec::from(ciphertext);
|
||||||
|
|
||||||
|
let mut aad = Vec::from(block_number.to_be_bytes());
|
||||||
|
if let Some(file_id) = file_id {
|
||||||
|
aad.extend(file_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
let aes = Aes256Gcm::new(&self.key);
|
||||||
|
|
||||||
|
aes.decrypt_in_place_detached(
|
||||||
|
GenericArray::from_slice(nonce),
|
||||||
|
&aad,
|
||||||
|
&mut buf,
|
||||||
|
GenericArray::from_slice(tag),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(buf.to_vec())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the decrypted size of a file, based on the encrypted size.
|
||||||
|
pub fn get_real_size(encrypted_size: u64) -> u64 {
|
||||||
|
if encrypted_size == 0 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
let x = (encrypted_size - 50) / 4128;
|
||||||
|
|
||||||
|
let y = (encrypted_size - 50) - x * 4128;
|
||||||
|
x * 4096 + y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::ContentEnc;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_real_size() {
|
||||||
|
assert_eq!(0, ContentEnc::get_real_size(0));
|
||||||
|
|
||||||
|
for real_size in 1..4096 * 4 + 1 {
|
||||||
|
let nbr_full_blocks = real_size / 4096;
|
||||||
|
let encrypted_size =
|
||||||
|
18 + nbr_full_blocks * (4096 + 32) + real_size - nbr_full_blocks * 4096 + 32;
|
||||||
|
|
||||||
|
assert_eq!(real_size, ContentEnc::get_real_size(encrypted_size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,72 +0,0 @@
|
||||||
use aes_gcm::{aead::generic_array::GenericArray, aes::Aes256, AeadInPlace, AesGcm, NewAead};
|
|
||||||
use cipher::consts::{U16, U32};
|
|
||||||
use hkdf::Hkdf;
|
|
||||||
|
|
||||||
use crate::error::{Result, DecryptError};
|
|
||||||
|
|
||||||
type Aes256Gcm = AesGcm<Aes256, U16>;
|
|
||||||
|
|
||||||
pub struct ContentEnc {
|
|
||||||
key: GenericArray<u8, U32>,
|
|
||||||
iv_len: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ContentEnc {
|
|
||||||
pub fn new(master_key: &[u8], iv_len: u8) -> Self {
|
|
||||||
let mut key = [0u8; 32];
|
|
||||||
let hdkf = Hkdf::<sha2::Sha256>::new(None, &master_key);
|
|
||||||
hdkf.expand(b"AES-GCM file content encryption", &mut key)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
key: GenericArray::from(key),
|
|
||||||
iv_len: iv_len as usize,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn decrypt_block(
|
|
||||||
&self,
|
|
||||||
block: &[u8],
|
|
||||||
block_number: u64,
|
|
||||||
file_id: Option<&[u8]>,
|
|
||||||
) -> Result<Vec<u8>> {
|
|
||||||
// TODO NOT BOX
|
|
||||||
if block.len() == 0 {
|
|
||||||
return Ok(block.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
if block.iter().all(|f| *f == 0) {
|
|
||||||
todo!("black hole")
|
|
||||||
}
|
|
||||||
|
|
||||||
if block.len() < self.iv_len {
|
|
||||||
return Err(DecryptError::BlockTooShort().into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let nonce = &block[..self.iv_len];
|
|
||||||
let tag = &block[block.len() - self.iv_len..];
|
|
||||||
let ciphertext = &block[self.iv_len..block.len() - self.iv_len];
|
|
||||||
|
|
||||||
if nonce.iter().all(|f| *f == 0) {
|
|
||||||
return Err(DecryptError::AllZeroNonce().into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut buf = Vec::from(ciphertext);
|
|
||||||
|
|
||||||
let mut aad = Vec::from(block_number.to_be_bytes());
|
|
||||||
if let Some(file_id) = file_id {
|
|
||||||
aad.extend(file_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
let aes = Aes256Gcm::new(&self.key);
|
|
||||||
|
|
||||||
aes.decrypt_in_place_detached(
|
|
||||||
GenericArray::from_slice(nonce),
|
|
||||||
&aad,
|
|
||||||
&mut buf,
|
|
||||||
GenericArray::from_slice(tag),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
return Ok(buf.to_vec());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,49 +1,20 @@
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::{config::ConfigError, content::ContentCipherError, filename::FilenameCipherError};
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
/// An error that wrap all the errors in this lib.
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("Failed to decrypt content")]
|
|
||||||
ContentDecryptError(),
|
|
||||||
#[error("Failed to decrypt filename")]
|
|
||||||
FilenameDecryptError(#[from] FilenameDecryptError),
|
|
||||||
#[error("Failed to decode base64")]
|
|
||||||
Base64Error(#[from] base64::DecodeError),
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
DecodeError(#[from] DecryptError),
|
FilenameCipherError(#[from] FilenameCipherError),
|
||||||
}
|
|
||||||
|
|
||||||
impl From<aes_gcm::Error> for Error {
|
|
||||||
fn from(_: aes_gcm::Error) -> Self {
|
|
||||||
Self::ContentDecryptError()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
pub enum DecryptError {
|
|
||||||
#[error("Block is too short")]
|
|
||||||
BlockTooShort(),
|
|
||||||
#[error("all-zero nonce")]
|
|
||||||
AllZeroNonce(),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
pub enum FilenameDecryptError {
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
ScryptError(#[from] ScryptError),
|
ContentCipherError(#[from] ContentCipherError),
|
||||||
#[error("Failed to decode base64")]
|
|
||||||
Base64Error(#[from] base64::DecodeError),
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
HdkfError(#[from] hkdf::InvalidLength),
|
ConfigError(#[from] ConfigError),
|
||||||
#[error("Failed to decrypt filename")]
|
#[error(transparent)]
|
||||||
DecryptError(),
|
JsonError(#[from] serde_json::Error),
|
||||||
}
|
#[error(transparent)]
|
||||||
|
IoError(#[from] std::io::Error),
|
||||||
#[derive(Debug, Error)]
|
|
||||||
pub enum ScryptError {
|
|
||||||
#[error(transparent)]
|
|
||||||
InvalidParams(#[from] scrypt::errors::InvalidParams),
|
|
||||||
#[error(transparent)]
|
|
||||||
InvalidOutputLen(#[from] scrypt::errors::InvalidOutputLen),
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
use cipher::{block_padding::Pkcs7, Iv, Key, KeyIvInit};
|
||||||
|
|
||||||
|
use super::{EmeCipher, EncodedFilename, FilenameCipherError, IntoDecodable};
|
||||||
|
|
||||||
|
/// DirFilenameCipher allow you to cipher and decipher filenames in a directory.
|
||||||
|
///
|
||||||
|
/// TODO : document structure of a gocryptfs dir or put a link.
|
||||||
|
pub struct DirFilenameCipher<'a, 'b> {
|
||||||
|
filename_key: &'a Key<EmeCipher>,
|
||||||
|
iv: &'b Iv<EmeCipher>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> DirFilenameCipher<'a, 'b> {
|
||||||
|
pub fn new(filename_key: &'a Key<EmeCipher>, iv: &'b Iv<EmeCipher>) -> Self {
|
||||||
|
Self { filename_key, iv }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decipher a filename.
|
||||||
|
///
|
||||||
|
/// Name muste be the name of the file if it is a short filename, or the content of the long .name file otherwise.
|
||||||
|
pub fn decode_filename<S>(&self, name: S) -> Result<String, FilenameCipherError>
|
||||||
|
where
|
||||||
|
S: IntoDecodable,
|
||||||
|
{
|
||||||
|
let cipher = EmeCipher::new(self.filename_key, self.iv);
|
||||||
|
|
||||||
|
let mut filename = base64::decode_config(name.to_decodable(), base64::URL_SAFE_NO_PAD)?;
|
||||||
|
let filename_decoded = cipher
|
||||||
|
.decrypt_padded_mut::<Pkcs7>(&mut filename)
|
||||||
|
.map_err(|_| FilenameCipherError::DecryptError())?;
|
||||||
|
|
||||||
|
Ok(String::from_utf8_lossy(filename_decoded).to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cipher a filename.
|
||||||
|
pub fn encrypt_filename(
|
||||||
|
&self,
|
||||||
|
plain_text_name: &str,
|
||||||
|
) -> Result<EncodedFilename, FilenameCipherError> {
|
||||||
|
let mut cipher = EmeCipher::new(self.filename_key, self.iv);
|
||||||
|
let mut res = [0u8; 2048];
|
||||||
|
|
||||||
|
let filename_encrypted = cipher
|
||||||
|
.encrypt_padded_b2b_mut::<Pkcs7>(plain_text_name.as_bytes(), &mut res)
|
||||||
|
.map_err(|_| FilenameCipherError::EncryptError())?;
|
||||||
|
|
||||||
|
let filename = base64::encode_config(filename_encrypted, base64::URL_SAFE_NO_PAD);
|
||||||
|
|
||||||
|
Ok(filename.into())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum FilenameCipherError {
|
||||||
|
#[error("Failed to decode base64")]
|
||||||
|
Base64Error(#[from] base64::DecodeError),
|
||||||
|
#[error(transparent)]
|
||||||
|
HdkfError(#[from] hkdf::InvalidLength),
|
||||||
|
#[error("Failed to decrypt filename")]
|
||||||
|
DecryptError(),
|
||||||
|
#[error("Failed to encrypt filename")]
|
||||||
|
EncryptError()
|
||||||
|
}
|
|
@ -1,49 +1,44 @@
|
||||||
use std::path::Path;
|
use sha2::{Digest, Sha256};
|
||||||
|
|
||||||
/// EncodedFilename
|
/// Represent an encrypted filename.
|
||||||
#[derive(Debug, PartialEq)]
|
///
|
||||||
|
/// An encrypted filename can have two forms : long or short.
|
||||||
|
/// TODO: Document
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum EncodedFilename {
|
pub enum EncodedFilename {
|
||||||
ShortFilename(String),
|
ShortFilename(String),
|
||||||
LongFilename(LongFilename),
|
LongFilename(LongFilename),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EncodedFilename {
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
fn new<P>(file: P) -> crate::error::Result<Self>
|
pub struct LongFilename {
|
||||||
where
|
filename: String,
|
||||||
P: AsRef<Path>,
|
filename_content: String,
|
||||||
{
|
|
||||||
let path = file.as_ref();
|
|
||||||
|
|
||||||
let filename = path.file_name().unwrap().to_str().expect("Failed to get filename");
|
|
||||||
|
|
||||||
if filename.starts_with("gocryptfs.longname.") {
|
|
||||||
if !filename.ends_with(".name") {
|
|
||||||
let long = std::fs::read_to_string(
|
|
||||||
path.parent().unwrap().join(format!("{}.name", filename)),
|
|
||||||
).unwrap();
|
|
||||||
Ok(EncodedFilename::LongFilename(LongFilename {
|
|
||||||
filename: filename.to_string(),
|
|
||||||
filename_content: long,
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
panic!()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(EncodedFilename::ShortFilename(filename.to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
impl LongFilename {
|
||||||
pub struct LongFilename {
|
pub fn filename(&self) -> &str {
|
||||||
pub filename: String,
|
self.filename.as_ref()
|
||||||
pub filename_content: String,
|
}
|
||||||
|
|
||||||
|
pub fn filename_content(&self) -> &str {
|
||||||
|
self.filename_content.as_ref()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<String> for EncodedFilename {
|
impl From<String> for EncodedFilename {
|
||||||
fn from(filename: String) -> Self {
|
fn from(filename: String) -> Self {
|
||||||
if filename.len() > 255 {
|
if filename.len() > 255 {
|
||||||
unimplemented!()
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.update(filename.as_bytes());
|
||||||
|
|
||||||
|
Self::LongFilename(LongFilename {
|
||||||
|
filename: format!(
|
||||||
|
"gocryptfs.longname.{}",
|
||||||
|
base64::encode_config(hasher.finalize(), base64::URL_SAFE_NO_PAD)
|
||||||
|
),
|
||||||
|
filename_content: filename,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
Self::ShortFilename(filename)
|
Self::ShortFilename(filename)
|
||||||
}
|
}
|
||||||
|
@ -51,11 +46,11 @@ impl From<String> for EncodedFilename {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait IntoDecodable {
|
pub trait IntoDecodable {
|
||||||
fn to_decodable<'s>(&'s self) -> &'s str;
|
fn to_decodable(&self) -> &str;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoDecodable for EncodedFilename {
|
impl IntoDecodable for EncodedFilename {
|
||||||
fn to_decodable<'s>(&'s self) -> &'s str {
|
fn to_decodable(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
Self::ShortFilename(s) => s.as_str(),
|
Self::ShortFilename(s) => s.as_str(),
|
||||||
Self::LongFilename(l) => l.filename_content.as_str(),
|
Self::LongFilename(l) => l.filename_content.as_str(),
|
||||||
|
@ -64,13 +59,13 @@ impl IntoDecodable for EncodedFilename {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoDecodable for String {
|
impl IntoDecodable for String {
|
||||||
fn to_decodable<'s>(&'s self) -> &'s str {
|
fn to_decodable(&self) -> &str {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntoDecodable for &'a str {
|
impl<'a> IntoDecodable for &'a str {
|
||||||
fn to_decodable<'s>(&'s self) -> &'s str {
|
fn to_decodable(&self) -> &str {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,30 @@
|
||||||
|
//! Utilities for filename encryption.
|
||||||
|
//!
|
||||||
use aes::Aes256;
|
use aes::Aes256;
|
||||||
use cipher::{block_padding::Pkcs7, inout::InOutBufReserved, Iv, Key, KeyIvInit};
|
use cipher::{Iv, Key};
|
||||||
use eme_mode::DynamicEme;
|
use eme_mode::DynamicEme;
|
||||||
use hkdf::Hkdf;
|
use hkdf::Hkdf;
|
||||||
|
|
||||||
use crate::error::FilenameDecryptError;
|
|
||||||
|
|
||||||
pub(crate) type EmeCipher = DynamicEme<Aes256>;
|
pub(crate) type EmeCipher = DynamicEme<Aes256>;
|
||||||
|
|
||||||
|
mod dir_filename_cipher;
|
||||||
|
mod error;
|
||||||
mod filename_encoded;
|
mod filename_encoded;
|
||||||
|
|
||||||
|
pub use dir_filename_cipher::*;
|
||||||
|
pub use error::*;
|
||||||
pub use filename_encoded::*;
|
pub use filename_encoded::*;
|
||||||
|
|
||||||
// TODO RENAME
|
/// FilenameCipher allow you to retrieve a DirFilenameCipher, used to cipher and decipher filenames.
|
||||||
pub struct FilenameDecoder {
|
pub struct FilenameCipher {
|
||||||
filename_key: Key<Aes256>,
|
filename_key: Key<Aes256>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FilenameDecoder {
|
impl FilenameCipher {
|
||||||
pub fn new(master_key: &[u8]) -> Result<Self, FilenameDecryptError> {
|
/// Create a new FilenameCipher, from the master key.
|
||||||
|
pub fn new(master_key: &[u8]) -> Result<Self, FilenameCipherError> {
|
||||||
let mut key = [0u8; 32];
|
let mut key = [0u8; 32];
|
||||||
let hdkf = Hkdf::<sha2::Sha256>::new(None, &master_key);
|
let hdkf = Hkdf::<sha2::Sha256>::new(None, master_key);
|
||||||
hdkf.expand(b"EME filename encryption", &mut key)?;
|
hdkf.expand(b"EME filename encryption", &mut key)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
@ -27,54 +32,10 @@ impl FilenameDecoder {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_decoder_for_dir<'a, 'b>(&'a self, iv: &'b [u8]) -> DirFilenameDecoder<'a, 'b> {
|
/// Get the cipher for a directory, allowing you to decipher files in this dir.
|
||||||
|
pub fn get_cipher_for_dir<'a, 'b>(&'a self, iv: &'b [u8]) -> DirFilenameCipher<'a, 'b> {
|
||||||
let iv = Iv::<EmeCipher>::from_slice(iv);
|
let iv = Iv::<EmeCipher>::from_slice(iv);
|
||||||
DirFilenameDecoder {
|
DirFilenameCipher::new(&self.filename_key, iv)
|
||||||
filename_key: &self.filename_key,
|
|
||||||
iv,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO RENAME
|
|
||||||
pub struct DirFilenameDecoder<'a, 'b> {
|
|
||||||
filename_key: &'a Key<EmeCipher>,
|
|
||||||
iv: &'b Iv<EmeCipher>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b> DirFilenameDecoder<'a, 'b> {
|
|
||||||
pub fn decode_filename<S>(&self, name: S) -> Result<String, FilenameDecryptError>
|
|
||||||
where
|
|
||||||
S: IntoDecodable,
|
|
||||||
{
|
|
||||||
let cipher = EmeCipher::new(self.filename_key, self.iv);
|
|
||||||
|
|
||||||
let mut filename = base64::decode_config(name.to_decodable(), base64::URL_SAFE_NO_PAD)?;
|
|
||||||
let filename_decoded = cipher
|
|
||||||
.decrypt_padded_mut::<Pkcs7>(&mut filename)
|
|
||||||
.map_err(|_| FilenameDecryptError::DecryptError())?;
|
|
||||||
|
|
||||||
Ok(String::from_utf8_lossy(filename_decoded).to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn encrypt_filename(
|
|
||||||
&self,
|
|
||||||
plain_text_name: &str,
|
|
||||||
) -> Result<EncodedFilename, FilenameDecryptError> {
|
|
||||||
let mut cipher = EmeCipher::new(self.filename_key, self.iv);
|
|
||||||
let mut res = [0u8; 2048];
|
|
||||||
|
|
||||||
let filename_encrypted = cipher
|
|
||||||
.encrypt_padded_inout_mut::<Pkcs7>(
|
|
||||||
InOutBufReserved::from_slices(plain_text_name.as_bytes(), &mut res).unwrap(),
|
|
||||||
)
|
|
||||||
.map_err(|_| FilenameDecryptError::DecryptError())?; // TODO RENAME ERROR
|
|
||||||
|
|
||||||
// TODO LONG FILENAME
|
|
||||||
|
|
||||||
let filename = base64::encode_config(filename_encrypted, base64::URL_SAFE_NO_PAD);
|
|
||||||
|
|
||||||
Ok(filename.into())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,15 +43,15 @@ impl<'a, 'b> DirFilenameDecoder<'a, 'b> {
|
||||||
mod test {
|
mod test {
|
||||||
use crate::filename::EncodedFilename;
|
use crate::filename::EncodedFilename;
|
||||||
|
|
||||||
use super::FilenameDecoder;
|
use super::FilenameCipher;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_encrypt() {
|
fn test_encrypt() {
|
||||||
let master_key = base64::decode("9gtUW9XiiefEgEXEkbONI6rnUsd2yh5UZZLG0V8Bxgk=").unwrap();
|
let master_key = base64::decode("9gtUW9XiiefEgEXEkbONI6rnUsd2yh5UZZLG0V8Bxgk=").unwrap();
|
||||||
let dir_iv = base64::decode("6ysCeWOp2euF1x39gth8KQ==").unwrap();
|
let dir_iv = base64::decode("6ysCeWOp2euF1x39gth8KQ==").unwrap();
|
||||||
|
|
||||||
let decoder = FilenameDecoder::new(&master_key).expect("Failed to get file decoder");
|
let decoder = FilenameCipher::new(&master_key).expect("Failed to get file decoder");
|
||||||
let dir_decoder = decoder.get_decoder_for_dir(&dir_iv);
|
let dir_decoder = decoder.get_cipher_for_dir(&dir_iv);
|
||||||
|
|
||||||
let encoded = dir_decoder
|
let encoded = dir_decoder
|
||||||
.encrypt_filename("7.mp4")
|
.encrypt_filename("7.mp4")
|
||||||
|
@ -107,8 +68,8 @@ mod test {
|
||||||
let master_key = base64::decode("9gtUW9XiiefEgEXEkbONI6rnUsd2yh5UZZLG0V8Bxgk=").unwrap();
|
let master_key = base64::decode("9gtUW9XiiefEgEXEkbONI6rnUsd2yh5UZZLG0V8Bxgk=").unwrap();
|
||||||
let dir_iv = base64::decode("6ysCeWOp2euF1x39gth8KQ==").unwrap();
|
let dir_iv = base64::decode("6ysCeWOp2euF1x39gth8KQ==").unwrap();
|
||||||
|
|
||||||
let decoder = FilenameDecoder::new(&master_key).expect("Failed to get file decoder");
|
let decoder = FilenameCipher::new(&master_key).expect("Failed to get file decoder");
|
||||||
let dir_decoder = decoder.get_decoder_for_dir(&dir_iv);
|
let dir_decoder = decoder.get_cipher_for_dir(&dir_iv);
|
||||||
|
|
||||||
let decrypted = dir_decoder
|
let decrypted = dir_decoder
|
||||||
.decode_filename("vTBajRt-yCpxB7Sly0E7lQ")
|
.decode_filename("vTBajRt-yCpxB7Sly0E7lQ")
|
||||||
|
|
|
@ -1,155 +0,0 @@
|
||||||
use std::{
|
|
||||||
error::Error,
|
|
||||||
fs::File,
|
|
||||||
io::Read,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::filename::{EncodedFilename, FilenameDecoder};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct DirCache {
|
|
||||||
filename: String,
|
|
||||||
dir_iv: [u8; 16],
|
|
||||||
dir_entries: Vec<DirEntry>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DirCache {
|
|
||||||
pub fn load_from_path<P>(path: P) -> Self
|
|
||||||
where
|
|
||||||
P: AsRef<Path>,
|
|
||||||
{
|
|
||||||
let path = path.as_ref();
|
|
||||||
|
|
||||||
let mut dir_iv = [0u8; 16];
|
|
||||||
{
|
|
||||||
let dir_iv_path = path.join("gocryptfs.diriv");
|
|
||||||
|
|
||||||
let mut file = File::open(dir_iv_path).unwrap();
|
|
||||||
|
|
||||||
file.read_exact(&mut dir_iv).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let dir_entries = path
|
|
||||||
.read_dir()
|
|
||||||
.unwrap()
|
|
||||||
.filter_map(|f| {
|
|
||||||
if let Ok(entry) = f {
|
|
||||||
if entry.file_name() != "gocryptfs.conf"
|
|
||||||
&& entry.file_name() != "gocryptfs.diriv"
|
|
||||||
{
|
|
||||||
Some(entry)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.map(|f| DirEntry::try_from(f.path().as_path()))
|
|
||||||
.filter_map(|f| f.ok())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
filename: path.to_string_lossy().to_string(),
|
|
||||||
dir_iv,
|
|
||||||
dir_entries,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lookup<P>(
|
|
||||||
&self,
|
|
||||||
filename_decoder: &FilenameDecoder,
|
|
||||||
decrypted_path: P,
|
|
||||||
) -> Option<PathBuf>
|
|
||||||
where
|
|
||||||
P: AsRef<Path>,
|
|
||||||
{
|
|
||||||
let decrypted_path = decrypted_path.as_ref();
|
|
||||||
|
|
||||||
let mut components = decrypted_path.components();
|
|
||||||
|
|
||||||
let component = components.next().expect("lol");
|
|
||||||
|
|
||||||
let decoder = filename_decoder.get_decoder_for_dir(&self.dir_iv);
|
|
||||||
|
|
||||||
let segment = decoder
|
|
||||||
.encrypt_filename(component.as_os_str().to_str().unwrap())
|
|
||||||
.expect("lol");
|
|
||||||
|
|
||||||
let segment_path = match segment {
|
|
||||||
EncodedFilename::ShortFilename(filename) => PathBuf::from(filename),
|
|
||||||
EncodedFilename::LongFilename(long_filename) => PathBuf::from(long_filename.filename),
|
|
||||||
};
|
|
||||||
|
|
||||||
if segment_path.is_dir() {
|
|
||||||
let (size, _) = components.size_hint();
|
|
||||||
|
|
||||||
if size > 0 {
|
|
||||||
unimplemented!()
|
|
||||||
} else {
|
|
||||||
unimplemented!()
|
|
||||||
//None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// component.as_path()
|
|
||||||
unimplemented!()
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lookup_internal<P>(
|
|
||||||
&self,
|
|
||||||
filename_decoder: &FilenameDecoder,
|
|
||||||
decrypted_path: P,
|
|
||||||
dir: &DirCache,
|
|
||||||
) -> Option<PathBuf>
|
|
||||||
where
|
|
||||||
P: AsRef<Path>,
|
|
||||||
{
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum DirEntry {
|
|
||||||
Dir(DirCache),
|
|
||||||
File(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DirEntry {
|
|
||||||
/// Returns `true` if the dir entry is [`Dir`].
|
|
||||||
///
|
|
||||||
/// [`Dir`]: DirEntry::Dir
|
|
||||||
#[must_use]
|
|
||||||
pub fn is_dir(&self) -> bool {
|
|
||||||
matches!(self, Self::Dir(..))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the dir entry is [`File`].
|
|
||||||
///
|
|
||||||
/// [`File`]: DirEntry::File
|
|
||||||
#[must_use]
|
|
||||||
pub fn is_file(&self) -> bool {
|
|
||||||
matches!(self, Self::File(..))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<&Path> for DirEntry {
|
|
||||||
type Error = Box<dyn Error>; // TODO
|
|
||||||
|
|
||||||
fn try_from(path: &Path) -> Result<Self, Self::Error> {
|
|
||||||
Ok(if path.is_dir() {
|
|
||||||
DirEntry::Dir(DirCache::load_from_path(path))
|
|
||||||
} else {
|
|
||||||
DirEntry::File(
|
|
||||||
path.components()
|
|
||||||
.last()
|
|
||||||
.unwrap()
|
|
||||||
.as_os_str()
|
|
||||||
.to_string_lossy()
|
|
||||||
.to_string(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +1,18 @@
|
||||||
//! A library to write gocryptfs compatible programs
|
//! A library to write gocryptfs compatible programs.
|
||||||
|
|
||||||
use std::{fs::File, path::Path};
|
use std::{fs::File, io::Read, path::Path};
|
||||||
|
|
||||||
use content_enc::ContentEnc;
|
use content::ContentEnc;
|
||||||
use filename::FilenameDecoder;
|
use filename::FilenameCipher;
|
||||||
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod content_enc;
|
pub mod content;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod filename;
|
pub mod filename;
|
||||||
|
|
||||||
/// A GocryptFs encrypted directory
|
/// A GocryptFs encrypted directory
|
||||||
pub struct GocryptFs {
|
pub struct GocryptFs {
|
||||||
filename_decoder: FilenameDecoder,
|
filename_decoder: FilenameCipher,
|
||||||
content_decoder: ContentEnc,
|
content_decoder: ContentEnc,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,18 +26,25 @@ impl GocryptFs {
|
||||||
{
|
{
|
||||||
let base_path = encrypted_dir_path.as_ref();
|
let base_path = encrypted_dir_path.as_ref();
|
||||||
|
|
||||||
let config = {
|
let mut config_file =
|
||||||
let mut config_file =
|
File::open(base_path.join("gocryptfs.conf"))?;
|
||||||
File::open(base_path.join("gocryptfs.conf")).expect("failed to get config");
|
|
||||||
|
|
||||||
serde_json::from_reader::<_, config::CryptConf>(&mut config_file)
|
Self::load_from_reader(&mut config_file, password.as_bytes())
|
||||||
.expect("failed to parse config")
|
}
|
||||||
};
|
|
||||||
|
|
||||||
let master_key = config.get_master_key(password.as_bytes())?;
|
/// Load a gocryptfs from the config.
|
||||||
|
///
|
||||||
|
/// reader_config must be a reader of a valid `gocryptfs.conf`.
|
||||||
|
pub fn load_from_reader<R>(reader_config: &mut R, password: &[u8]) -> error::Result<Self>
|
||||||
|
where
|
||||||
|
R: Read,
|
||||||
|
{
|
||||||
|
let config = serde_json::from_reader::<_, config::CryptConf>(reader_config)?;
|
||||||
|
|
||||||
let filename_decoder = FilenameDecoder::new(&master_key)?;
|
let master_key = config.get_master_key(password)?;
|
||||||
let content_decoder = ContentEnc::new(&master_key, 16); // TODO IV LEN
|
|
||||||
|
let filename_decoder = FilenameCipher::new(&master_key)?;
|
||||||
|
let content_decoder = ContentEnc::new(&master_key, 16)?; // TODO IV LEN
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
filename_decoder,
|
filename_decoder,
|
||||||
|
@ -45,10 +52,12 @@ impl GocryptFs {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn filename_decoder(&self) -> &FilenameDecoder {
|
/// Get the [`filename decoder`](struct@FilenameCipher) attached to this GocryptFs.
|
||||||
|
pub fn filename_decoder(&self) -> &FilenameCipher {
|
||||||
&self.filename_decoder
|
&self.filename_decoder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the [`content decoder`](struct@ContentEnc) attached to this GocryptFs.
|
||||||
pub fn content_decoder(&self) -> &ContentEnc {
|
pub fn content_decoder(&self) -> &ContentEnc {
|
||||||
&self.content_decoder
|
&self.content_decoder
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
[package]
|
||||||
|
name = "rustcryptfs-mount"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
|
rustcryptfs-fuse = { path = "../rustcryptfs-fuse" }
|
|
@ -0,0 +1,14 @@
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use rustcryptfs_fuse::EncryptedFs;
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub fn mount<P>(path: P, mount_point: P, password: &str) -> rustcryptfs_fuse::error::Result<()>
|
||||||
|
where
|
||||||
|
P: AsRef<Path>,
|
||||||
|
{
|
||||||
|
let fs = EncryptedFs::new(path, password)?;
|
||||||
|
|
||||||
|
fs.mount(mount_point)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -5,6 +5,10 @@ edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["mount"]
|
||||||
|
mount = ["rustcryptfs-mount"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.53"
|
anyhow = "1.0.53"
|
||||||
serde = { version = "1.0.136", features = ["derive"] }
|
serde = { version = "1.0.136", features = ["derive"] }
|
||||||
|
@ -12,4 +16,6 @@ serde_json = "1.0.78"
|
||||||
clap = { version = "3.1.18", features = ["derive"] }
|
clap = { version = "3.1.18", features = ["derive"] }
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
rustcryptfs-lib = { path = "../rustcryptfs-lib" }
|
rustcryptfs-lib = { path = "../rustcryptfs-lib" }
|
||||||
env_logger = "0.9.0"
|
env_logger = "0.9.0"
|
||||||
|
rpassword = "7.0.0"
|
||||||
|
rustcryptfs-mount = { path = "../rustcryptfs-mount", optional = true }
|
|
@ -1,3 +1,6 @@
|
||||||
|
#[cfg(feature = "mount")]
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
|
@ -12,8 +15,12 @@ pub(crate) enum Commands {
|
||||||
/// Decrypt a file
|
/// Decrypt a file
|
||||||
Decrypt(DecryptCommand),
|
Decrypt(DecryptCommand),
|
||||||
|
|
||||||
// List file contained in a directory
|
/// List file contained in a directory
|
||||||
Ls(LsCommand),
|
Ls(LsCommand),
|
||||||
|
|
||||||
|
#[cfg(feature = "mount")]
|
||||||
|
/// Mount an encrypted folder
|
||||||
|
Mount(MountCommand),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
|
@ -42,4 +49,18 @@ pub(crate) struct LsCommand {
|
||||||
/// The password
|
/// The password
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
pub(crate) password : Option<String>
|
pub(crate) password : Option<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "mount")]
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub(crate) struct MountCommand {
|
||||||
|
/// The directory
|
||||||
|
pub(crate) path: PathBuf,
|
||||||
|
|
||||||
|
/// The mount point
|
||||||
|
pub(crate) mountpoint: PathBuf,
|
||||||
|
|
||||||
|
/// The password
|
||||||
|
#[clap(short, long)]
|
||||||
|
pub(crate) password: Option<String>,
|
||||||
|
}
|
||||||
|
|
|
@ -9,39 +9,49 @@ use clap::Parser;
|
||||||
use args::{DecryptCommand, LsCommand};
|
use args::{DecryptCommand, LsCommand};
|
||||||
use rustcryptfs_lib::GocryptFs;
|
use rustcryptfs_lib::GocryptFs;
|
||||||
|
|
||||||
|
#[cfg(feature = "mount")]
|
||||||
|
use args::MountCommand;
|
||||||
|
|
||||||
mod args;
|
mod args;
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
let args = args::Args::parse();
|
let args = args::Args::parse();
|
||||||
log::debug!("{:?}", args);
|
|
||||||
|
|
||||||
match &args.command {
|
match &args.command {
|
||||||
args::Commands::Decrypt(c) => decrypt_file(c),
|
args::Commands::Decrypt(c) => decrypt_file(c),
|
||||||
args::Commands::Ls(c) => ls(c),
|
args::Commands::Ls(c) => ls(c),
|
||||||
|
#[cfg(feature = "mount")]
|
||||||
|
args::Commands::Mount(c) => mount(c),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ls(c: &LsCommand) -> anyhow::Result<()> {
|
fn ls(c: &LsCommand) -> anyhow::Result<()> {
|
||||||
let folder_path = Path::new(&c.folder_path);
|
let folder_path = Path::new(&c.folder_path);
|
||||||
|
|
||||||
|
let password = if let Some(password) = &c.password {
|
||||||
|
password.clone()
|
||||||
|
} else {
|
||||||
|
rpassword::prompt_password("Your password: ")?
|
||||||
|
};
|
||||||
|
|
||||||
let fs = GocryptFs::open(
|
let fs = GocryptFs::open(
|
||||||
c.gocryptfs_path
|
c.gocryptfs_path
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|p| Path::new(p))
|
.map(Path::new)
|
||||||
.unwrap_or(folder_path),
|
.unwrap_or(folder_path),
|
||||||
c.password.as_ref().expect("Please input a password"),
|
&password,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let filename_decoder = fs.filename_decoder();
|
let filename_decoder = fs.filename_decoder();
|
||||||
|
|
||||||
let iv = std::fs::read(folder_path.join("gocryptfs.diriv"))?;
|
let iv = std::fs::read(folder_path.join("gocryptfs.diriv"))?;
|
||||||
|
|
||||||
let dir_decoder = filename_decoder.get_decoder_for_dir(&iv);
|
let dir_decoder = filename_decoder.get_cipher_for_dir(&iv);
|
||||||
|
|
||||||
for dir in std::fs::read_dir(folder_path)?.flat_map(|e| e.ok()) {
|
for dir in std::fs::read_dir(folder_path)?.flat_map(|e| e.ok()) {
|
||||||
let filename = dir.file_name();
|
let filename = dir.file_name();
|
||||||
let filename = filename.to_str().unwrap();
|
let filename = filename.to_string_lossy();
|
||||||
|
|
||||||
if filename != "gocryptfs.conf" && filename != "gocryptfs.diriv" {
|
if filename != "gocryptfs.conf" && filename != "gocryptfs.diriv" {
|
||||||
if filename.starts_with("gocryptfs.longname.") {
|
if filename.starts_with("gocryptfs.longname.") {
|
||||||
|
@ -52,28 +62,33 @@ fn ls(c: &LsCommand) -> anyhow::Result<()> {
|
||||||
println!("{}", res);
|
println!("{}", res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if let Ok(res) = dir_decoder.decode_filename(&*filename) {
|
||||||
if let Ok(res) = dir_decoder.decode_filename(filename) {
|
println!("{}", res);
|
||||||
println!("{}", res);
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(());
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decrypt_file(c: &DecryptCommand) -> anyhow::Result<()> {
|
fn decrypt_file(c: &DecryptCommand) -> anyhow::Result<()> {
|
||||||
let file_path = Path::new(&c.file_path);
|
let file_path = Path::new(&c.file_path);
|
||||||
|
|
||||||
|
let password = if let Some(password) = &c.password {
|
||||||
|
password.clone()
|
||||||
|
} else {
|
||||||
|
rpassword::prompt_password("Your password: ")?
|
||||||
|
};
|
||||||
|
|
||||||
let fs = GocryptFs::open(
|
let fs = GocryptFs::open(
|
||||||
c.gocryptfs_path
|
c.gocryptfs_path
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|p| Path::new(p))
|
.map(Path::new)
|
||||||
.unwrap_or_else(|| file_path.parent().unwrap()),
|
.unwrap_or_else(|| file_path.parent().unwrap()),
|
||||||
c.password.as_ref().expect("Please input a password"),
|
&password,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut file = File::open(file_path).unwrap();
|
let mut file = File::open(file_path)?;
|
||||||
|
|
||||||
let enc = fs.content_decoder();
|
let enc = fs.content_decoder();
|
||||||
|
|
||||||
|
@ -92,7 +107,7 @@ fn decrypt_file(c: &DecryptCommand) -> anyhow::Result<()> {
|
||||||
|
|
||||||
stdout.write_all(&res)?;
|
stdout.write_all(&res)?;
|
||||||
|
|
||||||
if res.len() == 0 {
|
if res.is_empty() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,3 +117,26 @@ fn decrypt_file(c: &DecryptCommand) -> anyhow::Result<()> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
#[cfg(feature = "mount")]
|
||||||
|
fn mount(mount: &MountCommand) -> anyhow::Result<()> {
|
||||||
|
use anyhow::Context;
|
||||||
|
|
||||||
|
let password = if let Some(password) = &mount.password {
|
||||||
|
password.clone()
|
||||||
|
} else {
|
||||||
|
rpassword::prompt_password("Your password: ")?
|
||||||
|
};
|
||||||
|
|
||||||
|
rustcryptfs_mount::mount(&mount.path, &mount.mountpoint, &password)
|
||||||
|
.context("Failed to run fuse fs")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
#[cfg(feature = "mount")]
|
||||||
|
fn mount(mount: &MountCommand) -> anyhow::Result<()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue