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:
oupson 2022-10-09 12:45:05 +02:00 committed by GitHub
commit 004a9b4b54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 1095 additions and 485 deletions

264
Cargo.lock generated
View File

@ -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",
]

View File

@ -1,5 +1,7 @@
[workspace] [workspace]
members = [ members = [
"rustcryptfs", "rustcryptfs",
"rustcryptfs-lib" "rustcryptfs-lib",
"rustcryptfs-fuse",
"rustcryptfs-mount"
] ]

View File

@ -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

View File

@ -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"

View File

@ -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)
}
}

View File

@ -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(),
}
}
}

View File

@ -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)
}
}

View File

@ -0,0 +1,6 @@
mod encrypted_filesystem;
mod inode_cache;
pub mod error;
pub use encrypted_filesystem::EncryptedFs;

View File

@ -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),
}

View File

@ -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)?, &params, &mut key)
password, .map_err(ScryptError::from)?;
&base64::decode(&self.salt).unwrap(),
&params,
&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)?;

View File

@ -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()
}
}

View File

@ -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));
}
}
}

View File

@ -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());
}
}

View File

@ -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),
} }

View File

@ -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())
}
}

View File

@ -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()
}

View File

@ -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
} }
} }

View File

@ -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")

View File

@ -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(),
)
})
}
}

View File

@ -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
} }

View File

@ -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" }

View File

@ -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(())
}

View File

@ -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"] }
@ -13,3 +17,5 @@ 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 }

View File

@ -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)]
@ -43,3 +50,17 @@ pub(crate) struct LsCommand {
#[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>,
}

View File

@ -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!()
}