Use golang.org/x/crypto/chacha20

This commit is contained in:
Evan Su 2021-08-01 23:13:48 -04:00 committed by GitHub
parent 3313fbab58
commit 81351f86fb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -36,24 +36,26 @@ import (
"encoding/hex" "encoding/hex"
"path/filepath" "path/filepath"
// Reed-Solomon
"github.com/HACKERALERT/infectious" // v0.0.0-20210730231340-8af02cb9ed0a
// Cryptography // Cryptography
"crypto/rand" "crypto/rand"
"crypto/hmac"
"crypto/subtle"
"crypto/md5" "crypto/md5"
"crypto/sha1" "crypto/sha1"
"crypto/sha256" "crypto/sha256"
"golang.org/x/crypto/argon2" "golang.org/x/crypto/argon2"
"golang.org/x/crypto/hkdf"
"golang.org/x/crypto/sha3" "golang.org/x/crypto/sha3"
"golang.org/x/crypto/blake2b" "golang.org/x/crypto/blake2b"
"golang.org/x/crypto/blake2s" "golang.org/x/crypto/blake2s"
"golang.org/x/crypto/chacha20poly1305" "golang.org/x/crypto/chacha20"
"github.com/HACKERALERT/Picocrypt/src/monocypher"
// GUI // GUI
"github.com/AllenDang/giu" "github.com/AllenDang/giu"
// Reed-Solomon
"github.com/HACKERALERT/infectious" // v0.0.0-20210730231340-8af02cb9ed0a
// Helpers // Helpers
"github.com/HACKERALERT/clipboard" // v0.1.5-0.20210716140604-61d96bf4fc94 "github.com/HACKERALERT/clipboard" // v0.1.5-0.20210716140604-61d96bf4fc94
"github.com/HACKERALERT/dialog" // v0.0.0-20210716143851-223edea1d840 "github.com/HACKERALERT/dialog" // v0.0.0-20210716143851-223edea1d840
@ -119,7 +121,7 @@ var shredding = "Ready."
var password string var password string
var cPassword string // Confirm password text entry string variable var cPassword string // Confirm password text entry string variable
var keyfilePath string var keyfilePath string
var keyfileLabel = "Use a keyfile" var keyfileLabel = "Use a keyfile (experimental)"
var metadata string var metadata string
var shredTemp bool var shredTemp bool
var serpent bool var serpent bool
@ -193,11 +195,13 @@ func startUI(){
}), }),
giu.Button("Yes").Size(100,0).OnClick(func(){ giu.Button("Yes").Size(100,0).OnClick(func(){
giu.CloseCurrentPopup() giu.CloseCurrentPopup()
giu.Update()
giu.OpenPopup(" ") giu.OpenPopup(" ")
go func (){ go func (){
work() work()
working = false working = false
debug.FreeOSMemory() debug.FreeOSMemory()
giu.Update()
}() }()
}), }),
), ),
@ -356,7 +360,7 @@ func startUI(){
giu.Row( giu.Row(
giu.Checkbox("Encode with Reed-Solomon to prevent corruption",&reedsolo), giu.Checkbox("Encode with Reed-Solomon to prevent corruption",&reedsolo),
giu.Button("?").Size(24,25).OnClick(func(){ giu.Button("?").Size(24,25).OnClick(func(){
browser.OpenURL("https://bit.ly/reedsolomonwikipedia") browser.OpenURL("https://bit.ly/3A2V7LR")
}), }),
).Build() ).Build()
giu.Row( giu.Row(
@ -400,6 +404,7 @@ func startUI(){
work() work()
working = false working = false
debug.FreeOSMemory() debug.FreeOSMemory()
giu.Update()
}() }()
} }
}), }),
@ -626,24 +631,29 @@ func onDrop(names []string){
fin,_ := os.Open(names[0]) fin,_ := os.Open(names[0])
// Read metadata and insert into box // Read metadata and insert into box
var err error
fin.Read(make([]byte,15)) fin.Read(make([]byte,15))
tmp := make([]byte,30) tmp := make([]byte,30)
fin.Read(tmp) fin.Read(tmp)
tmp,_ = rsDecode(rs10,tmp) tmp,err = rsDecode(rs10,tmp)
metadataLength,_ := strconv.Atoi(string(tmp))
tmp = make([]byte,metadataLength*3) if err==nil{
fin.Read(tmp) metadataLength,_ := strconv.Atoi(string(tmp))
metadata = "" tmp = make([]byte,metadataLength*3)
fin.Read(tmp)
metadata = ""
for i:=0;i<metadataLength*3;i+=3{ for i:=0;i<metadataLength*3;i+=3{
fmt.Println(tmp[i:i+3]) fmt.Println(tmp[i:i+3])
t,err := rsDecode(rs1,tmp[i:i+3]) t,err := rsDecode(rs1,tmp[i:i+3])
if err!=nil{ if err!=nil{
metadata = "Metadata is corrupted." metadata = "Metadata is corrupted."
break break
}
metadata += string(t)
} }
metadata += string(t) }else{
metadata = "Metadata is corrupted."
} }
flags := make([]byte,15) flags := make([]byte,15)
@ -752,13 +762,14 @@ func work(){
//reedsoloFixed := 0 //reedsoloFixed := 0
//reedsoloErrors := 0 //reedsoloErrors := 0
var salt []byte var salt []byte
var hkdfSalt []byte
var nonce []byte var nonce []byte
var keyHash []byte var keyHash []byte
var _keyHash []byte var _keyHash []byte
var khash []byte var khash []byte
var khash_hash []byte = make([]byte,32) var khash_hash []byte = make([]byte,32)
var _khash_hash []byte var _khash_hash []byte
var nonces []byte var fileMac []byte
// Set the output file based on mode // Set the output file based on mode
if mode=="encrypt"{ if mode=="encrypt"{
@ -854,17 +865,12 @@ func work(){
progressInfo = "" progressInfo = ""
} }
//fmt.Println(inputFile)
stat,_ := os.Stat(inputFile) stat,_ := os.Stat(inputFile)
total := stat.Size() total := stat.Size()
//fmt.Println(total)
// Open input file in read-only mode // Open input file in read-only mode
fin,_ := os.Open(inputFile) fin,_ := os.Open(inputFile)
var fout *os.File var fout *os.File
//fmt.Println(mode)
// If encrypting, generate values; If decrypting, read values from file // If encrypting, generate values; If decrypting, read values from file
if mode=="encrypt"{ if mode=="encrypt"{
@ -878,6 +884,7 @@ func work(){
// Argon2 salt and XChaCha20 nonce // Argon2 salt and XChaCha20 nonce
salt = make([]byte,16) salt = make([]byte,16)
hkdfSalt = make([]byte,32)
nonce = make([]byte,24) nonce = make([]byte,24)
// Write version to file // Write version to file
@ -890,9 +897,6 @@ func work(){
// Write the length of the metadata to file // Write the length of the metadata to file
fout.Write(metadataLength) fout.Write(metadataLength)
// Write the actual metadata
//fout.Write([]byte(metadata))
// Reed-Solomon-encode the metadata and write to file // Reed-Solomon-encode the metadata and write to file
for _,i := range []byte(metadata){ for _,i := range []byte(metadata){
@ -910,17 +914,19 @@ func work(){
flags = rsEncode(rs5,flags) flags = rsEncode(rs5,flags)
fout.Write(flags) fout.Write(flags)
// Fill salt and nonce with Go's CSPRNG // Fill salts and nonce with Go's CSPRNG
rand.Read(salt) rand.Read(salt)
rand.Read(hkdfSalt)
rand.Read(nonce) rand.Read(nonce)
//fmt.Println("salt: ",salt)
//fmt.Println("nonce: ",nonce)
// Encode salt with Reed-Solomon and write to file // Encode salt with Reed-Solomon and write to file
_salt := rsEncode(rs16,salt) _salt := rsEncode(rs16,salt)
fout.Write(_salt) fout.Write(_salt)
// Encode HKDF salt with Reed-Solomon and write to file
_hkdfSalt := rsEncode(rs32,hkdfSalt)
fout.Write(_hkdfSalt)
// Encode nonce with Reed-Solomon and write to file // Encode nonce with Reed-Solomon and write to file
tmp := rsEncode(rs24,nonce) tmp := rsEncode(rs24,nonce)
fout.Write(tmp) fout.Write(tmp)
@ -931,21 +937,24 @@ func work(){
// Write placeholder for hash of hash of keyfile // Write placeholder for hash of hash of keyfile
fout.Write(make([]byte,96)) fout.Write(make([]byte,96))
// Write placeholder for HMAC-BLAKE2b/HMAC-SHA3 of file
pairs := int(math.Ceil(float64(total)/1048576)) fout.Write(make([]byte,192))
offset := 72*pairs+48
// Write placeholder for nonce/Poly1305 pairs
fout.Write(make([]byte,offset))
}else{ }else{
var err error var err1 error
var err2 error
var err3 error
var err4 error
var err5 error
var err6 error
var err7 error
var err8 error
var err9 error
status = "Reading values..." status = "Reading values..."
giu.Update() giu.Update()
version := make([]byte,15) version := make([]byte,15)
fin.Read(version) fin.Read(version)
version,err = rsDecode(rs5,version) version,err1 = rsDecode(rs5,version)
_ = err
if string(version)=="v1.13"{ if string(version)=="v1.13"{
_status = "Please use Picocrypt v1.13 to decrypt this file." _status = "Please use Picocrypt v1.13 to decrypt this file."
_status_color = color.RGBA{0xff,0x00,0x00,255} _status_color = color.RGBA{0xff,0x00,0x00,255}
@ -955,64 +964,54 @@ func work(){
tmp := make([]byte,30) tmp := make([]byte,30)
fin.Read(tmp) fin.Read(tmp)
tmp,err = rsDecode(rs10,tmp) tmp,err2 = rsDecode(rs10,tmp)
metadataLength,_ := strconv.Atoi(string(tmp)) metadataLength,_ := strconv.Atoi(string(tmp))
//fmt.Println("metadataLength",metadataLength)
//fmt.Println("metadataLength",err,metadataLength)
fin.Read(make([]byte,metadataLength*3)) fin.Read(make([]byte,metadataLength*3))
flags := make([]byte,15) flags := make([]byte,15)
fin.Read(flags) fin.Read(flags)
flags,err = rsDecode(rs5,flags) flags,err3 = rsDecode(rs5,flags)
//fmt.Println("flags",flags)
//fmt.Println("flags",err,flags)
fast = flags[0]==1 fast = flags[0]==1
keyfile = flags[1]==1 keyfile = flags[1]==1
salt = make([]byte,48) salt = make([]byte,48)
fin.Read(salt) fin.Read(salt)
salt,err = rsDecode(rs16,salt) salt,err4 = rsDecode(rs16,salt)
//fmt.Println("salt",err,salt)
hkdfSalt = make([]byte,96)
fin.Read(hkdfSalt)
hkdfSalt,err5 = rsDecode(rs32,hkdfSalt)
nonce = make([]byte,72) nonce = make([]byte,72)
fin.Read(nonce) fin.Read(nonce)
nonce,err = rsDecode(rs24,nonce) nonce,err6 = rsDecode(rs24,nonce)
//fmt.Println("nonce",err,nonce)
//fmt.Println("salt: ",salt)
//fmt.Println("nonce: ",nonce)
_keyHash = make([]byte,192) _keyHash = make([]byte,192)
fin.Read(_keyHash) fin.Read(_keyHash)
//fmt.Println("ud",_keyHash) _keyHash,err7 = rsDecode(rs64,_keyHash)
_keyHash,err = rsDecode(rs64,_keyHash)
//fmt.Println("_keyHash",keyHash)
//fmt.Println("_keyHash",err)
_khash_hash = make([]byte,96) _khash_hash = make([]byte,96)
fin.Read(_khash_hash) fin.Read(_khash_hash)
_khash_hash,_ = rsDecode(rs32,_khash_hash) _khash_hash,err8 = rsDecode(rs32,_khash_hash)
//fmt.Println("crcHash",crcHash)
//fmt.Println("_khash_hash",err) fileMac = make([]byte,192)
fin.Read(fileMac)
var _tmp float64 fileMac,err9 = rsDecode(rs64,fileMac)
if fast{
_tmp = math.Ceil(float64(total-int64(metadataLength*3+468))/float64(1048664)) // Is there a better way?
}else{ if err1!=nil||err2!=nil||err3!=nil||err4!=nil||err5!=nil||err6!=nil||err7!=nil||err8!=nil||err9!=nil{
_tmp = math.Ceil(float64(total-int64(metadataLength*3+468))/float64(1048696)) fmt.Println("Header error")
} }
nonces = make([]byte,int(_tmp*72)+48)
fin.Read(nonces)
//fmt.Println("Nonces: ",nonces)
} }
giu.Update()
status = "Deriving key..." status = "Deriving key..."
progress = 0 progress = 0
progressInfo = "" progressInfo = ""
giu.Update()
// Derive encryption/decryption key // Derive encryption/decryption key and subkeys
var key []byte var key []byte
if fast{ if fast{
key = argon2.IDKey( key = argon2.IDKey(
@ -1053,7 +1052,6 @@ func work(){
if keyfile{ if keyfile{
kin,_ := os.Open(keyfilePath) kin,_ := os.Open(keyfilePath)
kstat,_ := os.Stat(keyfilePath) kstat,_ := os.Stat(keyfilePath)
//fmt.Println(kstat.Size())
kbytes := make([]byte,kstat.Size()) kbytes := make([]byte,kstat.Size())
kin.Read(kbytes) kin.Read(kbytes)
kin.Close() kin.Close()
@ -1064,34 +1062,25 @@ func work(){
khash_sha3 := sha3.New256() khash_sha3 := sha3.New256()
khash_sha3.Write(khash) khash_sha3.Write(khash)
khash_hash = khash_sha3.Sum(nil) khash_hash = khash_sha3.Sum(nil)
//fmt.Println("khash",khash)
//fmt.Println("khash_hash",khash_hash)
} }
//key = make([]byte,32)
//fmt.Println("output",outputFile)
sha3_512 := sha3.New512() sha3_512 := sha3.New512()
sha3_512.Write(key) sha3_512.Write(key)
keyHash = sha3_512.Sum(nil) keyHash = sha3_512.Sum(nil)
//fmt.Println("keyHash: ",keyHash)
//fmt.Println(keyHash,_keyHash)
// Check is password is correct // Check is password is correct
if mode=="decrypt"{ if mode=="decrypt"{
keyCorrect := true keyCorrect := true
keyfileCorrect := true keyfileCorrect := true
var tmp bool var tmp bool
for i,j := range _keyHash{ if subtle.ConstantTimeCompare(keyHash,_keyHash)==0{
if keyHash[i]!=j{ keyCorrect = false
keyCorrect = false
break
}
} }
if keyfile{ if keyfile{
for i,j := range _khash_hash{ if subtle.ConstantTimeCompare(khash_hash,_khash_hash)==0{
if khash_hash[i]!=j{ keyfileCorrect = false
keyfileCorrect = false
break
}
} }
tmp = !keyCorrect||!keyfileCorrect tmp = !keyCorrect||!keyfileCorrect
}else{ }else{
@ -1135,47 +1124,12 @@ func work(){
counter := 0 counter := 0
startTime := time.Now() startTime := time.Now()
cipher,_ := chacha20poly1305.NewX(key) cipher,_ := chacha20.NewUnauthenticatedCipher(key,nonce)
if mode=="decrypt"{
_mac := nonces[len(nonces)-48:]
_mac,_ = rsDecode(rs16,_mac)
//fmt.Println("_mac ",_mac)
nonces = nonces[:len(nonces)-48]
var tmp []byte
var chunk []byte
for i,j := range nonces{
chunk = append(chunk,j)
if (i+1)%72==0{
chunk,_ = rsDecode(rs24,chunk)
for _,k := range chunk{
tmp = append(tmp,k)
}
chunk = nil
}
}
var authentic bool subkey := make([]byte,32)
nonces,authentic = monocypher.Unlock(tmp,nonce,key,_mac) hkdf := hkdf.New(sha3.New256,key,hkdfSalt,nil)
if !authentic{ hkdf.Read(subkey)
if keep{ mac := hmac.New(sha3.New512,subkey)
kept = true
}else{
fin.Close()
fout.Close()
_status = "The file is either corrupted or intentionally modified."
_status_color = color.RGBA{0xff,0x00,0x00,255}
if recombine{
os.Remove(inputFile)
}
os.Remove(outputFile)
return
}
}
//fmt.Println("UNENCRYPTED NONCES: ",nonces)
}
crc_blake2b := sha3.New256()
for{ for{
if !working{ if !working{
_status = "Operation cancelled by user." _status = "Operation cancelled by user."
@ -1191,100 +1145,30 @@ func work(){
os.Remove(outputFile) os.Remove(outputFile)
return return
} }
//fmt.Println("Encrypt/decrypt loop")
var _data []byte
var data []byte
var _nonce []byte
if mode=="encrypt"{
_data = make([]byte,1048576)
}else{
if fast{
_data = make([]byte,1048592)
}else{
_data = make([]byte,1048624)
}
}
size,err := fin.Read(_data) //var _data []byte
var data []byte
data = make([]byte,1048576)
size,err := fin.Read(data)
if err!=nil{ if err!=nil{
break break
} }
data = _data[:size] data = data[:size]
crc_blake2b.Write(data)
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"{
if fast{
data = cipher.Seal(nil,_nonce,data,nil)
fout.Write(data)
//crc.Write(data)
}else{
mac,data := monocypher.Lock(data,_nonce,key)
fout.Write(data)
fout.Write(rsEncode(rs16,mac))
//crc.Write(data)
//crc.Write(mac)
}
//fout.Write(data) _data := make([]byte,len(data))
if mode=="encrypt"{
cipher.XORKeyStream(_data,data)
mac.Write(_data)
}else{ }else{
//crc.Write(data) mac.Write(data)
if fast{ cipher.XORKeyStream(_data,data)
data,err = cipher.Open(nil,_nonce,data,nil)
if err!=nil{
if keep{
kept = true
mac := data[len(data)-16:]
data = data[:len(data)-16]
data,_ = monocypher.Unlock(data,_nonce,key,mac)
}else{
fin.Close()
fout.Close()
broken()
return
}
}
}else{
//crc.Write(data)
mac,_ := rsDecode(rs16,data[len(data)-48:])
data = data[:len(data)-48]
var authentic bool
data,authentic = monocypher.Unlock(data,_nonce,key,mac)
if !authentic{
if keep{
kept = true
}else{
fin.Close()
fout.Close()
broken()
return
}
}
}
fout.Write(data)
} }
fout.Write(_data)
// Update statistics // Update statistics
if mode=="encrypt"{ done += 1048576
done += 1048576
}else{
if fast{
done += 1048592
}else{
done += 1048624
}
}
counter++ counter++
progress = float32(done)/float32(total) progress = float32(done)/float32(total)
elapsed:= float64(int64(time.Now().Sub(startTime)))/float64(1000000000) elapsed:= float64(int64(time.Now().Sub(startTime)))/float64(1000000000)
@ -1301,22 +1185,21 @@ func work(){
} }
if mode=="encrypt"{ if mode=="encrypt"{
fout.Seek(int64(180+len(metadata)*3),0) fout.Seek(int64(276+len(metadata)*3),0)
fout.Write(rsEncode(rs64,keyHash)) fout.Write(rsEncode(rs64,keyHash))
fout.Write(rsEncode(rs32,khash_hash)) fout.Write(rsEncode(rs32,khash_hash))
fout.Write(rsEncode(rs64,mac.Sum(nil)))
_mac,tmp := monocypher.Lock(nonces,nonce,key) }else{
var chunk []byte if subtle.ConstantTimeCompare(mac.Sum(nil),fileMac)==0{
if keep{
for i,j := range tmp{ kept = true
chunk = append(chunk,j) }else{
if (i+1)%24==0{ fin.Close()
fout.Write(rsEncode(rs24,chunk)) fout.Close()
chunk = nil broken()
return
} }
} }
fout.Write(rsEncode(rs16,_mac))
} }
fin.Close() fin.Close()
@ -1428,6 +1311,17 @@ func work(){
status = "Ready." status = "Ready."
} }
// This function is run if an issue occurs during decryption
func broken(){
_status = "The file is either corrupted or intentionally modified."
_status_color = color.RGBA{0xff,0x00,0x00,255}
if recombine{
os.Remove(inputFile)
}
os.Remove(outputFile)
giu.Update()
}
// Generate file checksums (pretty straightforward) // Generate file checksums (pretty straightforward)
func generateChecksums(file string){ func generateChecksums(file string){
fin,_ := os.Open(file) fin,_ := os.Open(file)
@ -1701,16 +1595,6 @@ func resetUI(){
giu.Update() giu.Update()
} }
// This function is run if an issue occurs during decryption
func broken(){
_status = "The file is either corrupted or intentionally modified."
_status_color = color.RGBA{0xff,0x00,0x00,255}
if recombine{
os.Remove(inputFile)
}
os.Remove(outputFile)
}
// Reed-Solomon encoder // Reed-Solomon encoder
func rsEncode(rs *infectious.FEC,data []byte) []byte{ func rsEncode(rs *infectious.FEC,data []byte) []byte{
var res []byte var res []byte
@ -1730,7 +1614,10 @@ func rsDecode(rs *infectious.FEC,data []byte) ([]byte,error){
} }
} }
res,err := rs.Decode(nil,tmp) res,err := rs.Decode(nil,tmp)
return res,err if err!=nil{
return data[:rs.Total()/3],err
}
return res,nil
} }
func main(){ func main(){
@ -1766,8 +1653,15 @@ func main(){
icon,_ := png.Decode(r) icon,_ := png.Decode(r)
window.SetIcon([]image.Image{icon}) window.SetIcon([]image.Image{icon})
// Add drag and drop callback, set the screen DPI, start the UI // Add callbacks, set the screen DPI, start the UI
window.SetDropCallback(onDrop) window.SetDropCallback(onDrop)
window.SetCloseCallback(func() bool{
// Disable closing window if a Picocrypt is working to prevent temporary files
if working||shredding!="Ready."{
return false
}
return true
})
dpi = giu.Context.GetPlatform().GetContentScale() dpi = giu.Context.GetPlatform().GetContentScale()
window.Run(startUI) window.Run(startUI)