package docs import ( "crypto/aes" "crypto/cipher" "crypto/sha256" "encoding/base64" "errors" "math" "unicode/utf8" ) // // Decompress uri encoded lz-string // http://pieroxy.net/blog/pages/lz-string/index.html // https://github.com/pieroxy/lz-string/ // // map of "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$" var keyStrUriSafe map[byte]int = map[byte]int{74: 9, 78: 13, 83: 18, 36: 64, 109: 38, 114: 43, 116: 45, 101: 30, 45: 63, 73: 8, 81: 16, 113: 42, 49: 53, 50: 54, 54: 58, 76: 11, 100: 29, 107: 36, 121: 50, 77: 12, 89: 24, 105: 34, 66: 1, 69: 4, 85: 20, 48: 52, 119: 48, 117: 46, 120: 49, 52: 56, 56: 60, 110: 39, 112: 41, 70: 5, 71: 6, 79: 14, 88: 23, 97: 26, 102: 31, 103: 32, 67: 2, 118: 47, 65: 0, 68: 3, 72: 7, 108: 37, 51: 55, 57: 61, 82: 17, 90: 25, 98: 27, 115: 44, 122: 51, 53: 57, 86: 21, 106: 35, 111: 40, 55: 59, 43: 62, 75: 10, 80: 15, 84: 19, 87: 22, 99: 28, 104: 33} type dataStruct struct { input string val int position int index int dictionary []string enlargeIn float64 numBits int } func getBaseValue(char byte) int { return keyStrUriSafe[char] } // Input is composed of ASCII characters, so accessing it by array has no UTF-8 pb. func readBits(nb int, data *dataStruct) int { result := 0 power := 1 for i := 0; i < nb; i++ { respB := data.val & data.position data.position = data.position / 2 if data.position == 0 { data.position = 32 data.val = getBaseValue(data.input[data.index]) data.index += 1 } if respB > 0 { result |= power } power *= 2 } return result } func appendValue(data *dataStruct, str string) { data.dictionary = append(data.dictionary, str) data.enlargeIn -= 1 if data.enlargeIn == 0 { data.enlargeIn = math.Pow(2, float64(data.numBits)) data.numBits += 1 } } func getString(last string, data *dataStruct) (string, bool, error) { c := readBits(data.numBits, data) switch c { case 0: str := string(readBits(8, data)) appendValue(data, str) return str, false, nil case 1: str := string(readBits(16, data)) appendValue(data, str) return str, false, nil case 2: return "", true, nil } if c < len(data.dictionary) { return data.dictionary[c], false, nil } if c == len(data.dictionary) { return concatWithFirstRune(last, last), false, nil } return "", false, errors.New("Bad character encoding.") } // Need to handle UTF-8, so we need to use rune to concatenate func concatWithFirstRune(str string, getFirstRune string) string { r, _ := utf8.DecodeRuneInString(getFirstRune) return str + string(r) } func DecompressFromEncodedUriComponent(input string) (string, error) { data := dataStruct{input, getBaseValue(input[0]), 32, 1, []string{"0", "1", "2"}, 5, 2} result, isEnd, err := getString("", &data) if err != nil || isEnd { return result, err } last := result data.numBits += 1 for { str, isEnd, err := getString(last, &data) if err != nil || isEnd { return result, err } result = result + str appendValue(&data, concatWithFirstRune(last, str)) last = str } return "", errors.New("Unexpected end of buffer reached.") } func StringDecrypt(key string, str string) string { // hash keyHash := sha256.Sum256([]byte(key)) // iv - encrypt method AES-256-CBC expects 16 bytes - else you will get a warning iv := keyHash[:16] // create a new aes cipher using the key and iv block, err := aes.NewCipher(keyHash[:32]) if err != nil { panic(err) } // decode the base64 string to a []byte ciphertext, _ := base64.StdEncoding.DecodeString(str) // create the decrypter decrypter := cipher.NewCBCDecrypter(block, iv) // decrypt decrypted := make([]byte, len(ciphertext)) decrypter.CryptBlocks(decrypted, ciphertext) // remove padding padLen := int(decrypted[len(decrypted)-1]) decrypted = decrypted[:len(decrypted)-padLen] return string(decrypted) } func ResponseVclaim(encrypted string, key string) (string, error) { cipherText, err := base64.StdEncoding.DecodeString(encrypted) if err != nil { return "", err } hash := sha256.Sum256([]byte(key)) block, err := aes.NewCipher(hash[:]) if err != nil { return "", err } if len(cipherText) < aes.BlockSize { return "", errors.New("cipherText too short") } iv := hash[:aes.BlockSize] if len(cipherText)%aes.BlockSize != 0 { return "", errors.New("cipherText is not a multiple of the block size") } mode := cipher.NewCBCDecrypter(block, iv) mode.CryptBlocks(cipherText, cipherText) // cipherText, _ = pkcs7.Unpad(cipherText, aes.BlockSize) cipherText, _ = Unpad(cipherText, aes.BlockSize) // data, err := lzstring.DecompressFromEncodedURIComponent(string(cipherText)) data, err := DecompressFromEncodedUriComponent(string(cipherText)) if err != nil { return "", err } return data, nil }