diff --git a/src/unstable/Picocrypt.go b/src/unstable/Picocrypt.go index a27e52f..3137162 100644 --- a/src/unstable/Picocrypt.go +++ b/src/unstable/Picocrypt.go @@ -12,18 +12,23 @@ https://github.com/HACKERALERT/Picocrypt */ import ( - "fmt" "os" + "fmt" + "math" + "time" "strings" - "path/filepath" + "strconv" "image/color" - g "github.com/AllenDang/giu" - ig "github.com/AllenDang/imgui-go" - di "github.com/sqweek/dialog" "crypto/rand" + "path/filepath" + "golang.org/x/crypto/sha3" + "golang.org/x/crypto/argon2" + "golang.org/x/crypto/blake2b" + g "github.com/AllenDang/giu" + di "github.com/sqweek/dialog" + ig "github.com/AllenDang/imgui-go" "github.com/klauspost/reedsolomon" - _ "golang.org/x/crypto/argon2" - _ "github.com/HACKERALERT/Monocypher-Go/monocypher" + "golang.org/x/crypto/chacha20poly1305" ) // Global variables @@ -41,7 +46,9 @@ var inputLabel = "Drag and drop file(s) and folder(s) into this window." var outputEntry string var outputWidth float32 = 376 var orLabel = "or" +var progress float32 = 0 var progressInfo = "" +var status = "Ready." // User input variables var password string @@ -52,9 +59,12 @@ var erase bool var reedsolo bool // Reed-Solomon encoders +var rs5_128,_ = reedsolomon.New(5,128) var rs10_128,_ = reedsolomon.New(10,128) var rs16_128,_ = reedsolomon.New(16,128) var rs24_128,_ = reedsolomon.New(24,128) +var rs32_128,_ = reedsolomon.New(32,128) +var rs64_128,_ = reedsolomon.New(64,128) // Create the user interface func startUI(){ @@ -128,11 +138,11 @@ func startUI(){ ), // Progress bar - g.ProgressBar(0).Size(-1,0).Overlay(progressInfo), + g.ProgressBar(progress).Size(-1,0).Overlay(progressInfo), // Status label g.Dummy(10,0), - g.Label("Ready."), + g.Label(status), // Credits and version g.Line( @@ -162,6 +172,9 @@ func onDrop(names []string){ onlyFolders = nil allFiles = nil files,folders := 0,0 + + // Reset UI + resetUI() // Hide the ".pcv" label orLabel = "or" @@ -190,6 +203,22 @@ func onDrop(names []string){ mode = "decrypt" inputLabel = name+" (will decrypt)" outputEntry = names[0][:len(names[0])-4] + + // Open input file in read-only mode + fin,_ := os.Open(names[0]) + defer fin.Close() + + // Read metadata and insert into box + fin.Read(make([]byte,133)) + tmp := make([]byte,138) + fin.Read(tmp) + tmp = rsDecode(tmp,rs10_128,10) + metadataLength,_ := strconv.Atoi(string(tmp)) + //fmt.Println(metadataLength) + tmp = make([]byte,metadataLength) + fin.Read(tmp) + metadata = string(tmp) + }else{ mode = "encrypt" inputLabel = name+" (will encrypt)" @@ -205,6 +234,8 @@ func onDrop(names []string){ // Set the file as 'outputEntry' outputEntry = names[0] + + inputFile = names[0] } }else{ // There are multiple dropped items, check each one @@ -267,6 +298,16 @@ func work(){ //headerBroken := false //reedsoloFixed := 0 //reedsoloErrors := 0 + + // Declare salt and nonce + var salt []byte + var nonce []byte + var keyHash []byte + var crcHash []byte + var nonces []byte + + stat,_ := os.Stat(inputFile) + total := stat.Size() // Set the output file based on mode if mode=="encrypt"{ @@ -274,62 +315,262 @@ func work(){ }else{ outputFile = outputEntry } + + // Open input file in read-only mode + fin,_ := os.Open(inputFile) + defer fin.Close() + + var fout *os.File // If encrypting, generate values. If decrypting, read values from file if mode=="encrypt"{ - fout,_ := os.OpenFile( + status = "Generating values..." + g.Update() + fout,_ = os.OpenFile( outputFile, - os.O_WRONLY|os.O_TRUNC|os.O_CREATE, - 0666, + os.O_RDWR|os.O_CREATE|os.O_TRUNC, + 0755, ) + defer fout.Close() // Argon2 salt and XChaCha20 nonce - salt := make([]byte,16) - nonce := make([]byte,24) + salt = make([]byte,16) + nonce = make([]byte,24) + + // Write version to file + fout.Write(rsEncode([]byte("v1.13"),rs5_128,133)) // Encode the length of the metadata with Reed-Solomon metadataLength := []byte(fmt.Sprintf("%010d",len(metadata))) - /*shards,_ := rs10_128.Split(metadataLength) - rs10_128.Encode(shards) - tmp := make([]byte,138) - for i,shard := range(shards){ - tmp[i] = shard[0] - } - - fout.Write(tmp)*/ metadataLength = rsEncode(metadataLength,rs10_128,138) + // Write the length of the metadata to file fout.Write(metadataLength) + + // Write the actual metadata + fout.Write([]byte(metadata)) // Fill salt and nonce with Go's CSPRNG rand.Read(salt) rand.Read(nonce) + + //fmt.Println("Encrypting salt: ",salt) + //fmt.Println("Encrypting nonce: ",nonce) // Encode salt with Reed-Solomon and write to file - /*shards,_ = rs16_128.Split(salt) - rs16_128.Encode(shards) - tmp = make([]byte,144) - for i,shard := range(shards){ - tmp[i] = shard[0] - } - fout.Write(tmp)*/ salt = rsEncode(salt,rs16_128,144) fout.Write(salt) // Encode nonce with Reed-Solomon and write to file - /*shards,_ = rs24_128.Split(nonce) - rs24_128.Encode(shards) - tmp = make([]byte,152) - for i,shard := range(shards){ - tmp[i] = shard[0] - } - fout.Write(tmp)*/ - nonce = rsEncode(nonce,rs24_128,152) - fout.Write(nonce) + tmp := rsEncode(nonce,rs24_128,152) + fout.Write(tmp) + + // Write placeholder for hash of key + fout.Write(make([]byte,192)) + + // Write placeholder for Blake2 CRC + fout.Write(make([]byte,160)) + + pairs := int(math.Ceil(float64(total)/1048576)) + + offset := 152*pairs+144 + + // Write placeholder for nonce/Poly1305 pairs + fout.Write(make([]byte,offset)) }else{ + g.Update() + status = "Reading values..." + version := make([]byte,133) + fin.Read(version) + version = rsDecode(version,rs5_128,5) + tmp := make([]byte,138) + fin.Read(tmp) + tmp = rsDecode(tmp,rs10_128,10) + metadataLength,_ := strconv.Atoi(string(tmp)) + + fin.Read(make([]byte,metadataLength)) + + salt = make([]byte,144) + fin.Read(salt) + salt = rsDecode(salt,rs16_128,16) + + nonce = make([]byte,152) + fin.Read(nonce) + nonce = rsDecode(nonce,rs24_128,24) + + //fmt.Println("Decrypting salt: ",salt) + //fmt.Println("Decrypting nonce: ",nonce) + + keyHash = make([]byte,192) + fin.Read(keyHash) + keyHash = rsDecode(keyHash,rs64_128,64) + + crcHash = make([]byte,160) + fin.Read(crcHash) + crcHash = rsDecode(crcHash,rs32_128,32) + + _tmp := math.Ceil(float64(total-int64(metadataLength+919))/float64(1048744)) + nonces = make([]byte,int(_tmp*152)+144) + fin.Read(nonces) + + //fmt.Println("Nonces: ",nonces) + } + + g.Update() + status = "Deriving key..." + + // Derive encryption/decryption key + key := argon2.IDKey( + []byte(password), + salt, + 4, + 1048576/2, + 4, + 32, + )[:] + + key = make([]byte,32) + + sha3_512 := sha3.New512() + sha3_512.Write(key) + keyHash = sha3_512.Sum(nil) + //fmt.Println("keyHash: ",keyHash) + + // Check is password is correct + + if mode=="decrypt"{ + fout,_ = os.OpenFile( + outputFile, + os.O_RDWR|os.O_CREATE|os.O_TRUNC, + 0755, + ) + defer fout.Close() } + crc,_ := blake2b.New256(nil) + + done := 0 + counter := 0 + startTime := time.Now() + + cipher,_ := chacha20poly1305.NewX(key) + + if mode=="decrypt"{ + _mac := nonces[len(nonces)-144:] + _mac = rsDecode(_mac,rs16_128,16) + //fmt.Println("_mac len: ",len(_mac)) + nonces = nonces[:len(nonces)-144] + var tmp []byte + var chunk []byte + for i,j := range(nonces){ + chunk = append(chunk,j) + if (i+1)%152==0{ + chunk = rsDecode(chunk,rs24_128,24) + for _,k := range(chunk){ + tmp = append(tmp,k) + } + chunk = nil + } + } + for _,j := range(_mac){ + tmp = append(tmp,j) + } + //fmt.Println("ENCRYPTED NONCES: ",tmp) + // XXXXXXXXXXXXXXXXFSFSDFFFSFF + nonces,_ = cipher.Open(nil,nonce,tmp,nil) + //fmt.Println("UNENCRYPTED NONCES: ",nonces) + } + for{ + //fmt.Println("Encrypt/decrypt loop") + var _data []byte + var data []byte + var _nonce []byte + if mode=="encrypt"{ + _data = make([]byte,1048576) + }else{ + _data = make([]byte,1048592) + } + + size,err := fin.Read(_data) + if err!=nil{ + break + } + data = _data[:size] + + + if mode=="encrypt"{ + _nonce = make([]byte,24) + rand.Read(_nonce) + for _,i := range(_nonce){ + nonces = append(nonces,i) + } + }else{ + _nonce = nonces[counter*24:counter*24+24] + } + + //fmt.Println("Data nonce: ",_nonce) + //fmt.Println("Data: ",data) + if mode=="encrypt"{ + data = cipher.Seal(nil,_nonce,data,nil) + crc.Write(data) + fout.Write(data) + }else{ + //fmt.Println("DECODE LOOP") + crc.Write(data) + data,_ := cipher.Open(nil,_nonce,data,nil) + fout.Write(data) + //fmt.Println(authentic) + //fmt.Println("DECRYPTED DATA: ",data) + } + + done += 1048576 + counter++ + progress = float32(done)/float32(total) + + elapsed:= float64(int64(time.Now().Sub(startTime)))/float64(1000000000) + + speed := (float64(done)/elapsed)/1000000 + eta := float64(total-int64(done))/(speed*1000000) + + progressInfo = fmt.Sprintf("%.2f%%",progress*100) + + status = fmt.Sprintf("Working at %.2f MB/s (ETA: %.1fs)",speed,eta) + + g.Update() + data = nil + } + + if mode=="encrypt"{ + //fmt.Println("'nonces' before RS: ",nonces) + fout.Seek(int64(567+len(metadata)),0) + fout.Write(rsEncode(keyHash,rs64_128,192)) + fout.Write(rsEncode(crc.Sum(nil),rs32_128,160)) + //fmt.Println("UNENCRYPTED NONCES: ",nonces) + tmp := cipher.Seal(nil,nonce,nonces,nil) + //fmt.Println("ENCRYPTED NONCES: ",tmp) + _mac := tmp[len(tmp)-16:] + //fmt.Println("_mac len: ",len(_mac)) + tmp = tmp[:len(tmp)-16] + var chunk []byte + //fmt.Println("") + for i,j := range(tmp){ + chunk = append(chunk,j) + if (i+1)%24==0{ + fout.Write(rsEncode(chunk,rs24_128,152)) + //fmt.Println(rsEncode(chunk,rs24_128,152)) + chunk = nil + } + } + fout.Write(rsEncode(_mac,rs16_128,144)) + //fmt.Println("") + }else{ + fmt.Println("crcHash: ",crcHash) + fmt.Println("crc.Sum: ",crc.Sum(nil)) + } + fmt.Println("==============================") + resetUI() + status = "Completed." } // Reset the UI to a clean state with no nothing selected @@ -338,6 +579,14 @@ func resetUI(){ outputEntry = "" orLabel = "or" outputWidth = 376 + password = "" + cPassword = "" + metadata = "" + keep = false + erase = false + reedsolo = false + progress = 0 + progressInfo = "" g.Update() } @@ -351,6 +600,22 @@ func rsEncode(data []byte,encoder reedsolomon.Encoder,size int) []byte{ return tmp } +func rsDecode(data []byte,encoder reedsolomon.Encoder,size int) []byte{ + res := make([][]byte,len(data)) + for i,_ := range(data){ + tmp := make([]byte,1) + tmp[0] = data[i] + res[i] = tmp + } + _ = encoder.Reconstruct(res) + res = res[:size] + tmp := make([]byte,size) + for i,shard := range(res){ + tmp[i] = shard[0] + } + return tmp +} + // Create the master window, set callbacks, and start the UI func main(){ window := g.NewMasterWindow("Picocrypt",480,470,g.MasterWindowFlagsNotResizable,nil)