Restructure project
This commit is contained in:
		
							
								
								
									
										193
									
								
								internal/crypto/file.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								internal/crypto/file.go
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										95
									
								
								internal/crypto/key.go
									
									
									
									
									
										Normal 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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										75
									
								
								internal/crypto/keyExchange.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								internal/crypto/keyExchange.go
									
									
									
									
									
										Normal 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
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user