experiments

All kinds of coding experiments
Log | Files | Refs | Submodules

main.go (3349B)


      1 package main
      2 
      3 import (
      4 	"crypto/aes"
      5 	"crypto/cipher"
      6 	"crypto/rand"
      7 	"crypto/sha256"
      8 	"encoding/base64"
      9 	"io"
     10 	"syscall/js"
     11 )
     12 
     13 func main() {
     14 	js.Global().Set("encryptToBase64", encryptJS())
     15 	js.Global().Set("decryptBase64", decryptJS())
     16 	select {}
     17 }
     18 
     19 const (
     20 	ErrMissingInput              int = iota + 1
     21 	ErrBadInput                      = iota
     22 	ErrUnexpectedEncryptionError     = iota
     23 	ErrWrongEncryptionKey            = iota
     24 )
     25 
     26 func encryptJS() js.Func {
     27 	return js.FuncOf(func(_ js.Value, args []js.Value) interface{} {
     28 		result := make(map[string]interface{})
     29 		if len(args) < 2 {
     30 			result["error"] = "missing function input - expected (plaintext, encryption_key) "
     31 			return result
     32 		}
     33 		if args[0].Type() != js.TypeString {
     34 			result["error"] = "plaintext input is not a string"
     35 			return result
     36 		}
     37 		if args[1].Type() != js.TypeString {
     38 			result["error"] = "encryption key not a string"
     39 			return result
     40 		}
     41 		plaintext := []byte(args[0].String())
     42 		encryptionKey := []byte(args[1].String())
     43 		encrypted, err := encrypt(plaintext, encryptionKey)
     44 		if err != nil {
     45 			result["error"] = "encryption failed: " + err.Error()
     46 			return result
     47 		}
     48 		result["output"] = base64.RawStdEncoding.EncodeToString(encrypted)
     49 		return result
     50 	})
     51 }
     52 
     53 func decryptJS() js.Func {
     54 	return js.FuncOf(func(this js.Value, args []js.Value) interface{} {
     55 		result := make(map[string]interface{})
     56 		if len(args) < 2 {
     57 			result["error"] = "missing function input - expected (ciphertext, decryption_key) "
     58 			return result
     59 		}
     60 		if args[0].Type() != js.TypeString {
     61 			result["error"] = "ciphertext input is not a string"
     62 			return result
     63 		}
     64 		if args[1].Type() != js.TypeString {
     65 			result["error"] = "decryption key not a string"
     66 			return result
     67 		}
     68 		cipherbytes, err := base64.RawStdEncoding.DecodeString(args[0].String())
     69 		if err != nil {
     70 			result["error"] = "ciphertext is not base64 formatted"
     71 			return result
     72 		}
     73 		encryptionKey := []byte(args[1].String())
     74 		decrypted, err := decrypt(cipherbytes, encryptionKey)
     75 		if err != nil {
     76 			result["error"] = "decryption failed: " + err.Error()
     77 			return result
     78 		}
     79 		result["output"] = string(decrypted)
     80 		return result
     81 	})
     82 }
     83 
     84 const gcmStandardNonceSize = 12 // taken from crypto/cipher/gcm.go
     85 
     86 // sha256Sum returns the SHA256 sum of the given byte slice as a new byte slice
     87 func sha256Sum(input []byte) []byte {
     88 	hash := sha256.Sum256(input)
     89 	return hash[:]
     90 }
     91 
     92 // Symmetric encryption for extra private key protection
     93 func encrypt(plaintext, encryptionKey []byte) ([]byte, error) {
     94 	keyHash := sha256Sum(encryptionKey)
     95 	block, err := aes.NewCipher(keyHash)
     96 	if err != nil {
     97 		return nil, err
     98 	}
     99 	nonce := make([]byte, gcmStandardNonceSize)
    100 	if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
    101 		return nil, err
    102 	}
    103 	aesgcm, err := cipher.NewGCM(block)
    104 	if err != nil {
    105 		return nil, err
    106 	}
    107 	ciphertext := aesgcm.Seal(nil, nonce, []byte(plaintext), nil)
    108 	return append(nonce, ciphertext...), nil
    109 }
    110 
    111 func decrypt(cipherbundle, encryptionKey []byte) ([]byte, error) {
    112 	keyHash := sha256Sum(encryptionKey)
    113 	block, err := aes.NewCipher(keyHash)
    114 	if err != nil {
    115 		return nil, err
    116 	}
    117 	aesgcm, err := cipher.NewGCM(block)
    118 	if err != nil {
    119 		return nil, err
    120 	}
    121 	nonce := cipherbundle[:gcmStandardNonceSize]
    122 	ciphertext := cipherbundle[gcmStandardNonceSize:]
    123 	return aesgcm.Open(nil, nonce, ciphertext, nil)
    124 }