From df2a6d45b7cc49e118f6cbbd682491afb3ec3e58 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Sun, 1 Sep 2024 17:44:33 +0800 Subject: [PATCH] feat: working in process --- .gitignore | 3 + encfs/file.go | 139 ++++++++++++++++++++++++++++++++++++++++++---- encfs/fs.go | 53 +++++++++--------- main.go | 149 +++++++++----------------------------------------- 4 files changed, 185 insertions(+), 159 deletions(-) diff --git a/.gitignore b/.gitignore index a6b6dc4..4a884d6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ main .idea/ +test +*.__encfile + # ---> Go # If you prefer the allow list template instead of the deny list, see community template: diff --git a/encfs/file.go b/encfs/file.go index abeac9b..643989e 100644 --- a/encfs/file.go +++ b/encfs/file.go @@ -1,11 +1,18 @@ package encfs import ( + "crypto/aes" "crypto/rand" + "encoding/binary" + "encoding/hex" "encoding/json" "errors" + "fmt" "io" + "io/fs" + "math" "os" + "strings" "syscall" "github.com/spf13/afero" @@ -52,7 +59,7 @@ func newEncFileMeta(name string) (*EncFileMeta, error) { } func openEncFileMeta(name string) (*EncFileMeta, error) { - encFileMetaName := name + "." + EncFileExt + encFileMetaName := name + EncFileExt encFileMetaFile, err := os.Open(encFileMetaName) if err != nil { if os.IsNotExist(err) { @@ -91,10 +98,12 @@ type EncFile struct { isDir bool closed bool encFileMeta *EncFileMeta + encFs *EncFs + filePos int64 file *os.File } -func NewEncFile(name string, file *os.File, isCreate bool) (*EncFile, error) { +func NewEncFile(name string, file *os.File, encFs *EncFs, isCreate bool) (*EncFile, error) { fileInfo, err := file.Stat() if err != nil { return nil, err @@ -115,6 +124,8 @@ func NewEncFile(name string, file *os.File, isCreate bool) (*EncFile, error) { isDir: isDir, closed: false, encFileMeta: encFileMeta, + encFs: encFs, + filePos: 0, file: file, }, nil } @@ -134,8 +145,21 @@ func (f *EncFile) Read(p []byte) (n int, err error) { return 0, checkIsFileErr } - // TODO decrypt - return f.file.Read(p) + beforeReadFilePos := f.filePos + readLen, err := f.file.Read(p) + if err == nil { + f.filePos += int64(readLen) + if f.encFs != nil && f.encFs.key != nil && f.encFileMeta != nil { + encryptedBytes, err := generateCtrEncryptBytes(f.encFs.key, f.encFileMeta.Iv, beforeReadFilePos, int64(readLen)) + if err != nil { + return 0, err + } + for i := 0; i < readLen; i++ { + p[i] = p[i] ^ encryptedBytes[i] + } + } + } + return readLen, err } func (f *EncFile) ReadAt(p []byte, off int64) (n int, err error) { @@ -153,8 +177,6 @@ func (f *EncFile) Seek(offset int64, whence int) (int64, error) { if checkIsFileErr != nil { return 0, checkIsFileErr } - - // TODO decrypt return f.file.Seek(offset, whence) } @@ -164,8 +186,24 @@ func (f *EncFile) Write(p []byte) (n int, err error) { return 0, checkIsFileErr } - // TODO encrypt - return f.file.Write(p) + writeBuff := p + if f.encFs != nil && f.encFs.key != nil && f.encFileMeta != nil { + buff := make([]byte, len(p)) + encryptedBytes, err := generateCtrEncryptBytes(f.encFs.key, f.encFileMeta.Iv, f.filePos, int64(len(p))) + if err != nil { + return 0, err + } + for i := 0; i < len(p); i++ { + buff[i] = p[i] ^ encryptedBytes[i] + } + writeBuff = buff + } + + writeLen, err := f.file.Write(writeBuff) + if err == nil { + f.filePos += int64(writeLen) + } + return writeLen, err } func (f *EncFile) WriteAt(p []byte, off int64) (n int, err error) { @@ -174,8 +212,24 @@ func (f *EncFile) WriteAt(p []byte, off int64) (n int, err error) { return 0, checkIsFileErr } - // TODO encrypt - return f.file.WriteAt(p, off) + writeBuff := p + if f.encFs != nil && f.encFs.key != nil && f.encFileMeta != nil { + buff := make([]byte, len(p)) + encryptedBytes, err := generateCtrEncryptBytes(f.encFs.key, f.encFileMeta.Iv, off, int64(len(p))) + if err != nil { + return 0, err + } + for i := 0; i < len(p); i++ { + buff[i] = p[i] ^ encryptedBytes[i] + } + writeBuff = buff + } + + writeLen, err := f.file.WriteAt(writeBuff, off) + // if err == nil { + // f.filePos += int64(writeLen) + // } + return writeLen, err } func (f *EncFile) Name() string { @@ -187,7 +241,27 @@ func (f *EncFile) Readdir(count int) ([]os.FileInfo, error) { return nil, afero.ErrFileClosed } - return f.file.Readdir(count) + // FIXME is count * 2 just ok? + fileInfos, err := f.file.Readdir(count * 2) + if err != nil { + return nil, err + } + if count == 0 { + return make([]fs.FileInfo, 0), nil + } + + filterFileInfos := make([]os.FileInfo, 0) + for _, fileInfo := range fileInfos { + isEncFileMetaFile := strings.HasSuffix(fileInfo.Name(), EncFileExt) + if !isEncFileMetaFile { + filterFileInfos = append(filterFileInfos, fileInfo) + } + if count >= 0 && len(filterFileInfos) >= count { + break + } + } + + return filterFileInfos, nil } func (f *EncFile) Readdirnames(n int) ([]string, error) { @@ -230,3 +304,46 @@ func (f *EncFile) checkIsFile() error { } return nil } + +func generateCtrEncryptBytes(key, iv []byte, offset, len int64) ([]byte, error) { + endOffset := offset + len + encryptStartOffset := (offset / 16) * 16 + encryptEndOffset := (endOffset / 16) * 16 + if endOffset%16 > 0 { + encryptEndOffset += 16 + } + blocksCount := int((encryptEndOffset - encryptStartOffset) / 16) + + cipher, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + encryptBytes := make([]byte, blocksCount*16) + for i := 0; i < blocksCount; i++ { + encNonce := nonceAdd(iv, uint64(i)) + cipher.Encrypt(encryptBytes[i*16:(i+1)*16], encNonce) + } + // fmt.Println("XX ", hex.EncodeToString(encryptBytes)) + encryptedBytes := encryptBytes[offset-encryptStartOffset : offset-encryptStartOffset+len] + fmt.Println("XX", hex.EncodeToString(key), hex.EncodeToString(iv), offset, len, hex.EncodeToString(encryptedBytes)) + return encryptedBytes, nil +} + +func nonceAdd(nonce []byte, incrementValue uint64) []byte { + n1 := binary.BigEndian.Uint64(nonce[:8]) + n2 := binary.BigEndian.Uint64(nonce[8:]) + + leftToMax := math.MaxUint64 - n2 + if leftToMax <= incrementValue { + incrementValue -= leftToMax + 1 + n2 = incrementValue + n1 += 1 + } else { + n2 += incrementValue + } + + newNonce := make([]byte, 16) + binary.BigEndian.PutUint64(newNonce, n1) + binary.BigEndian.PutUint64(newNonce[8:], n2) + return newNonce +} diff --git a/encfs/fs.go b/encfs/fs.go index 43c0b95..6246275 100644 --- a/encfs/fs.go +++ b/encfs/fs.go @@ -10,15 +10,19 @@ import ( // copied from afero/os.go -type EncFs struct{} - -func NewEncFs() afero.Fs { - return &EncFs{} +type EncFs struct { + key []byte } -func (EncFs) Name() string { return "EncFs" } +func NewEncFs(key []byte) afero.Fs { + return &EncFs{ + key: key, + } +} -func (EncFs) Create(name string) (afero.File, error) { +func (*EncFs) Name() string { return "EncFs" } + +func (encFs *EncFs) Create(name string) (afero.File, error) { if err := checkFileExt(name); err != nil { return nil, err } @@ -28,18 +32,18 @@ func (EncFs) Create(name string) (afero.File, error) { // a nil value of type *os.File or nil won't be nil return nil, e } - return convertOsFileToEncFile(name, f, e, true) + return convertOsFileToEncFile(name, f, e, encFs, true) } -func (EncFs) Mkdir(name string, perm os.FileMode) error { +func (*EncFs) Mkdir(name string, perm os.FileMode) error { return os.Mkdir(name, perm) } -func (EncFs) MkdirAll(path string, perm os.FileMode) error { +func (*EncFs) MkdirAll(path string, perm os.FileMode) error { return os.MkdirAll(path, perm) } -func (EncFs) Open(name string) (afero.File, error) { +func (encFs *EncFs) Open(name string) (afero.File, error) { if err := checkFileExt(name); err != nil { return nil, err } @@ -49,10 +53,10 @@ func (EncFs) Open(name string) (afero.File, error) { // a nil value of type *os.File or nil won't be nil return nil, e } - return convertOsFileToEncFile(name, f, e, false) + return convertOsFileToEncFile(name, f, e, encFs, false) } -func (EncFs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) { +func (*EncFs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) { f, e := os.OpenFile(name, flag, perm) if f == nil { // while this looks strange, we need to return a bare nil (of type nil) not @@ -62,47 +66,47 @@ func (EncFs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, erro return f, e } -func (EncFs) Remove(name string) error { +func (*EncFs) Remove(name string) error { // TODO remove enc file meta return os.Remove(name) } -func (EncFs) RemoveAll(path string) error { +func (*EncFs) RemoveAll(path string) error { // TODO remove enc file meta return os.RemoveAll(path) } -func (EncFs) Rename(oldname, newname string) error { +func (*EncFs) Rename(oldname, newname string) error { // TODO rename enc file meta return os.Rename(oldname, newname) } -func (EncFs) Stat(name string) (os.FileInfo, error) { +func (*EncFs) Stat(name string) (os.FileInfo, error) { return os.Stat(name) } -func (EncFs) Chmod(name string, mode os.FileMode) error { +func (*EncFs) Chmod(name string, mode os.FileMode) error { return os.Chmod(name, mode) } -func (EncFs) Chown(name string, uid, gid int) error { +func (*EncFs) Chown(name string, uid, gid int) error { return os.Chown(name, uid, gid) } -func (EncFs) Chtimes(name string, atime time.Time, mtime time.Time) error { +func (*EncFs) Chtimes(name string, atime time.Time, mtime time.Time) error { return os.Chtimes(name, atime, mtime) } -func (EncFs) LstatIfPossible(name string) (os.FileInfo, bool, error) { +func (*EncFs) LstatIfPossible(name string) (os.FileInfo, bool, error) { fi, err := os.Lstat(name) return fi, true, err } -func (EncFs) SymlinkIfPossible(oldname, newname string) error { +func (*EncFs) SymlinkIfPossible(oldname, newname string) error { return os.Symlink(oldname, newname) } -func (EncFs) ReadlinkIfPossible(name string) (string, error) { +func (*EncFs) ReadlinkIfPossible(name string) (string, error) { return os.Readlink(name) } @@ -113,12 +117,11 @@ func checkFileExt(name string) error { return nil } -func convertOsFileToEncFile(name string, file *os.File, e error, isCreate bool) (afero.File, error) { +func convertOsFileToEncFile(name string, file *os.File, e error, encFs *EncFs, isCreate bool) (afero.File, error) { if e != nil { return nil, e } - // TODO add password, and IV etc ... - encFile, err := NewEncFile(name, file, isCreate) + encFile, err := NewEncFile(name, file, encFs, isCreate) if err != nil { return nil, err } diff --git a/main.go b/main.go index 35785a5..36d907d 100644 --- a/main.go +++ b/main.go @@ -1,148 +1,51 @@ package main import ( - "crypto/aes" - "encoding/binary" "encoding/hex" "fmt" - "math" - "os" + "io" "git.hatter.ink/hatter/go-afero-encfs/encfs" - "github.com/spf13/afero" ) func main() { - fmt.Println("Hello Go.") - osFs := afero.NewOsFs() - fmt.Println(osFs) - key := []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } - cipher, err := aes.NewCipher(key) - if err != nil { - fmt.Println(err) - return - } - - dest := []byte{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - } - src := []byte{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - } - cipher.Encrypt(dest, src) - fmt.Println(hex.EncodeToString(key)) - fmt.Println(hex.EncodeToString(src)) - fmt.Println(hex.EncodeToString(dest)) - fmt.Println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") - nonce, err := hex.DecodeString("0011223344556677ffffffffffffffff") - if err != nil { - fmt.Println(err) - return - } - fmt.Println(hex.EncodeToString(nonce)) - newNonce := nonceAdd(nonce, 1) - fmt.Println(hex.EncodeToString(newNonce)) - newNonce = nonceAdd(newNonce, 1) - fmt.Println(hex.EncodeToString(newNonce)) - newNonce = nonceAdd(newNonce, 1) - fmt.Println(hex.EncodeToString(newNonce)) - newNonce = nonceAdd(newNonce, 1) - fmt.Println(hex.EncodeToString(newNonce)) - newNonce = nonceAdd(newNonce, 1) - fmt.Println(hex.EncodeToString(newNonce)) - newNonce = nonceAdd(newNonce, 1) - fmt.Println(hex.EncodeToString(newNonce)) - newNonce = nonceAdd(newNonce, 1) - fmt.Println(hex.EncodeToString(newNonce)) + encFs := encfs.NewEncFs(key) - fmt.Println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") - test_key := []byte{ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - } - test_iv := []byte{ - 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, - } - plaintext := "hello world.hello world.hello world.hello world.hello world.hello world." - plaintextBytes := []byte(plaintext) + write := false - e, err := generateCtrEncryptBytes(test_key, test_iv, 0, int64(len(plaintextBytes))) - if err != nil { - fmt.Println(err) - return - } - fmt.Println(hex.EncodeToString(e)) - for i := 0; i < len(plaintextBytes); i++ { - plaintextBytes[i] = plaintextBytes[i] ^ e[i] - } - // SHOULD BE: 84ad8d80732490c061177a58bd26d032d6fcff2e66f9afe3cf95717485d3a4485d4a2a7bd835df3d0756b8192e3bf5a287ad8dd81942c43bc812c82d666ebbb34df4e2a5069467d9 - fmt.Println(hex.EncodeToString(plaintextBytes)) + if write { + encFile, err := encFs.Create("test") + if err != nil { + fmt.Println(err) + return + } - encFs := encfs.NewEncFs() + encFile.Write([]byte("hello world")) - encFile, err := encFs.Create("test") - if err != nil { - fmt.Println(err) - return - } - len, err := encFile.Write([]byte("hello world")) - if err != nil { - fmt.Println(err) - return - } - fmt.Println("write len: ", len) + seekI, err := encFile.Seek(1, 1) + fmt.Println(seekI, ", ", err) - f, err := os.Open("test2") - fmt.Println(f, "-", err, ", ", os.IsNotExist(err)) + encFile.Write([]byte("XXXXXXXXXXXXXXXXXXXXXXXXXXXXX")) - a, err := encFs.Open(".") - fs, err := a.Readdir(1000) - for _, ff := range fs { - fmt.Println(ff.Name()) - } -} + // encFile.WriteAt([]byte("YYYYYYYYYYYYYYYYYYYYYYYY"), 1) -func generateCtrEncryptBytes(key, iv []byte, offset, len int64) ([]byte, error) { - endOffset := offset + len - encryptStartOffset := (offset / 16) * 16 - encryptEndOffset := (endOffset / 16) * 16 - if endOffset%16 > 0 { - encryptEndOffset += 16 - } - blocksCount := int((encryptEndOffset - encryptStartOffset) / 16) - - cipher, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - encryptBytes := make([]byte, blocksCount*16) - for i := 0; i < blocksCount; i++ { - encNonce := nonceAdd(iv, uint64(i)) - cipher.Encrypt(encryptBytes[i*16:(i+1)*16], encNonce) - } - // fmt.Println("XX ", hex.EncodeToString(encryptBytes)) - return encryptBytes[offset-encryptStartOffset : offset-encryptStartOffset+len], nil -} - -func nonceAdd(nonce []byte, incrementValue uint64) []byte { - n1 := binary.BigEndian.Uint64(nonce[:8]) - n2 := binary.BigEndian.Uint64(nonce[8:]) - - leftToMax := math.MaxUint64 - n2 - if leftToMax <= incrementValue { - incrementValue -= leftToMax + 1 - n2 = incrementValue - n1 += 1 + // encFile.Write([]byte("Z")) + // encFile.Write([]byte("---------------------------------------")) + // encFile.Write([]byte("Z")) } else { - n2 += incrementValue - } + encFile, err := encFs.Open("test") + if err != nil { + fmt.Println(err) + return + } - newNonce := make([]byte, 16) - binary.BigEndian.PutUint64(newNonce, n1) - binary.BigEndian.PutUint64(newNonce[8:], n2) - return newNonce + rr, err := io.ReadAll(encFile) + fmt.Println(rr, string(rr), err) + + } }