Restructure project

This commit is contained in:
2021-07-08 13:11:41 -07:00
parent 8099077c50
commit 74c6391dca
12 changed files with 43 additions and 369 deletions

193
internal/crypto/file.go Normal file
View File

@@ -0,0 +1,193 @@
/*
Copyright © 2021 Arsen Musayelyan
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package crypto
import (
"bytes"
"crypto/md5"
"crypto/rand"
"encoding/hex"
"io"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/klauspost/compress/zstd"
"github.com/rs/zerolog/log"
"golang.org/x/crypto/chacha20poly1305"
)
// Encrypt given file using the shared key
func CompressAndEncryptFile(filePath string, newFilePath string, sharedKey string) {
// Use ConsoleWriter logger
// Read data from file
file, err := os.Open(filePath)
if err != nil {
log.Fatal().Err(err).Msg("Error opening file")
}
// Create buffer for compressed data
compressedBuffer := &bytes.Buffer{}
// Create Zstd encoder
zstdEncoder, err := zstd.NewWriter(compressedBuffer)
if err != nil {
log.Fatal().Err(err).Msg("Error creating Zstd encoder")
}
// Copy file data to Zstd encoder
_, err = io.Copy(zstdEncoder, file)
if err != nil {
log.Fatal().Err(err).Msg("Error reading file")
}
// Close Zstd encoder
zstdEncoder.Close()
// Read compressed data into data variable
data, err := ioutil.ReadAll(compressedBuffer)
if err != nil {
log.Fatal().Err(err).Msg("Error reading compressed buffer")
}
// Create md5 hash of password in order to make it the required size
md5Hash := md5.New()
md5Hash.Write([]byte(sharedKey))
// Encode md5 hash bytes into hexadecimal
hashedKey := hex.EncodeToString(md5Hash.Sum(nil))
// Create new AES c20cipher
c20cipher, err := chacha20poly1305.NewX([]byte(hashedKey))
if err != nil {
log.Fatal().Err(err).Msg("Error creating ChaCha20-Poly1305 cipher")
}
// Make byte slice for nonce
nonce := make([]byte, c20cipher.NonceSize())
// Read random bytes into nonce slice
_, err = io.ReadFull(rand.Reader, nonce)
if err != nil {
log.Fatal().Err(err).Msg("Error creating nonce")
}
// Encrypt data
ciphertext := c20cipher.Seal(nonce, nonce, data, nil)
// Create new file
newFile, err := os.Create(newFilePath)
if err != nil {
log.Fatal().Err(err).Msg("Error creating file")
}
// Defer file close
defer newFile.Close()
// Write ciphertext to new file
bytesWritten, err := newFile.Write(ciphertext)
if err != nil {
log.Fatal().Err(err).Msg("Error writing to file")
}
// Log bytes written and to which file
log.Info().Str("file", filepath.Base(newFilePath)).Msg("Wrote " + strconv.Itoa(bytesWritten) + " bytes")
}
// Decrypt given file using the shared key
func DecryptAndDecompressFile(filePath string, newFilePath string, sharedKey string) {
// Use ConsoleWriter logger
// Read data from file
data, err := ioutil.ReadFile(filePath)
if err != nil {
log.Fatal().Err(err).Msg("Error reading file")
}
// Create md5 hash of password in order to make it the required size
md5Hash := md5.New()
md5Hash.Write([]byte(sharedKey))
hashedKey := hex.EncodeToString(md5Hash.Sum(nil))
// Create new AES cipher
c20cipher, err := chacha20poly1305.NewX([]byte(hashedKey))
if err != nil {
log.Fatal().Err(err).Msg("Error creating ChaCha20-Poly1305 cipher")
}
// Get standard GCM nonce size
nonceSize := c20cipher.NonceSize()
// Get nonce and ciphertext from data
nonce, ciphertext := data[:nonceSize], data[nonceSize:]
// Decrypt data
plaintext, err := c20cipher.Open(nil, nonce, ciphertext, nil)
if err != nil {
log.Fatal().Err(err).Msg("Error decrypting data")
}
// Create new Zstd decoder
zstdDecoder, err := zstd.NewReader(bytes.NewBuffer(plaintext))
if err != nil {
log.Fatal().Err(err).Msg("Error creating Zstd decoder")
}
// Create new file
newFile, err := os.Create(newFilePath)
if err != nil {
log.Fatal().Err(err).Msg("Error creating file")
}
// Close new file at the end of this function
defer newFile.Close()
// Write decompressed plaintext to new file
bytesWritten, err := io.Copy(newFile, zstdDecoder)
if err != nil {
log.Fatal().Err(err).Msg("Error writing to file")
}
zstdDecoder.Close()
// Log bytes written and to which file
log.Info().Str("file", filepath.Base(newFilePath)).Msg("Wrote " + strconv.Itoa(int(bytesWritten)) + " bytes")
}
// Encrypt files in given directory using shared key
func EncryptFiles(dir string, sharedKey string) {
// Use ConsoleWriter logger
// Walk given directory
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
// If error reading, return err
if err != nil {
return err
}
// If file is not a directory and is not the key
if !info.IsDir() && !strings.Contains(path, "key.aes") {
// Compress and Encrypt the file using shared key, appending .zst.enc
CompressAndEncryptFile(path, path+".zst.enc", sharedKey)
// Remove unencrypted file
err := os.Remove(path)
if err != nil {
return err
}
}
// Return nil if no error occurs
return nil
})
if err != nil {
log.Fatal().Err(err).Msg("Error encrypting files")
}
}
// Decrypt files in given directory using shared key
func DecryptFiles(dir string, sharedKey string) {
// Use ConsoleWriter logger
// Walk given directory
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
// If error reading, return err
if err != nil {
return err
}
// If file is not a directory and is encrypted
if !info.IsDir() && strings.Contains(path, ".enc") {
// Decrypt and decompress the file using the shared key, removing .zst.enc
DecryptAndDecompressFile(path, strings.TrimSuffix(path, ".zst.enc"), sharedKey)
}
// Return nil if no errors occurred
return nil
})
if err != nil {
log.Fatal().Err(err).Msg("Error decrypting files")
}
}

95
internal/crypto/key.go Normal file
View File

@@ -0,0 +1,95 @@
/*
Copyright © 2021 Arsen Musayelyan
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package crypto
import (
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"io/ioutil"
"net/http"
"github.com/rs/zerolog/log"
"go.arsenm.dev/opensend/internal/transfer"
)
// Generate RSA keypair
func GenerateRSAKeypair() (*rsa.PrivateKey, *rsa.PublicKey) {
// Use ConsoleWriter logger
// Generate private/public RSA keypair
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
log.Fatal().Err(err).Msg("Error generating RSA keypair")
}
// Get public key
publicKey := privateKey.PublicKey
// Return keypair
return privateKey, &publicKey
}
// Get public key from sender
func GetKey(sender *transfer.Sender) []byte {
// Use ConsoleWriter logger
// Send key request to connection
keyReader, code, err := sender.Get("/key")
if err != nil {
log.Fatal().Err(err).Msg("Error sending key request")
}
// If ok code returned
if code == http.StatusOK {
// Read received bytes into key
key, err := ioutil.ReadAll(keyReader)
if err != nil {
log.Fatal().Err(err).Msg("Error reading key")
}
// Return key
return key
// Otherwise
} else {
// Fatally log
if err != nil {
log.Fatal().Msg("Server reported error")
}
}
// Return nil if all else fails
return nil
}
// Encrypt shared key with received public key
func EncryptKey(sharedKey string, recvPubKey *rsa.PublicKey) []byte {
// Use ConsoleWriter logger
// Encrypt shared key using RSA
encryptedSharedKey, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, recvPubKey, []byte(sharedKey), nil)
if err != nil {
log.Fatal().Err(err).Msg("Error encrypting shared key")
}
// Return encrypted key
return encryptedSharedKey
}
// Decrypt shared key using private RSA key
func DecryptKey(encryptedKey []byte, privateKey *rsa.PrivateKey) string {
// Decrypt shared key using RSA
decryptedKey, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, privateKey, encryptedKey, nil)
if err != nil {
log.Fatal().Err(err).Msg("Error decrypting shared key")
}
// Get string of decrypted key
sharedKey := string(decryptedKey)
// Return shared key
return sharedKey
}

View File

@@ -0,0 +1,75 @@
/*
Copyright © 2021 Arsen Musayelyan
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package crypto
import (
"crypto/rsa"
"encoding/gob"
"net"
"github.com/rs/zerolog/log"
)
// Exchange keys with sender
func ReceiverKeyExchange(key *rsa.PublicKey) string {
// Use ConsoleWriter logger
// Create TCP listener on port 9797
listener, err := net.Listen("tcp", ":9797")
if err != nil {
log.Fatal().Err(err).Msg("Error starting listener")
}
// Create string for sender address
var senderAddr string
for {
// Accept connection on listener
connection, err := listener.Accept()
if err != nil {
log.Fatal().Err(err).Msg("Error accepting connections")
}
// Get sender address and store it in senderAddr
senderAddr = connection.RemoteAddr().String()
// Create gob encoder with connection as io.Writer
encoder := gob.NewEncoder(connection)
// Encode key into connection
err = encoder.Encode(key)
if err != nil {
log.Fatal().Err(err).Msg("Error encoding key")
}
return senderAddr
}
}
// Exchange keys with receiver
func SenderKeyExchange(receiverIP string) *rsa.PublicKey {
// Use ConsoleWriter logger
// Connect to TCP socket on receiver IP port 9797
connection, err := net.Dial("tcp", receiverIP+":9797")
if err != nil {
log.Fatal().Err(err).Msg("Error connecting to sender")
}
// Create gob decoder
decoder := gob.NewDecoder(connection)
// Instantiate rsa.PublicKey struct
recvPubKey := &rsa.PublicKey{}
// Decode key
err = decoder.Decode(recvPubKey)
if err != nil {
log.Fatal().Err(err).Msg("Error decoding key")
}
// Return received key
return recvPubKey
}