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 }