Files
conquest/src/common/profile.nim

136 lines
5.2 KiB
Nim

import strutils, sequtils, random, base64, algorithm
import ./[types, utils]
import ./toml/toml
export parseFile, parseString, free, getTableKeys, getRandom
# Takes a specific "."-separated path as input and returns a default value if the key does not exits
# Example: cq.profile.getString("http-get.agent.heartbeat.prefix", "not found") returns the string value of the
# prefix key, or "not found" if the target key or any sub-tables don't exist
# '#' characters represent wildcard characters and are replaced with a random alphanumerical character (a-zA-Z0-9)
# '$' characters are replaced with a random number (0-9)
#[
Helper functions
]#
proc randomChar(): char =
let alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
return alphabet[rand(alphabet.len - 1)]
proc randomNumber(): char =
let numbers = "0123456789"
return numbers[rand(numbers.len - 1)]
proc getRandom*(values: seq[TomlValueRef]): TomlValueRef =
if values.len == 0:
return nil
return values[rand(values.len - 1)]
#[
Wrapper functions
]#
proc getStringValue*(key: TomlValueRef, default: string = ""): string =
if key.isNil or key.kind == None:
return default
var value: string = ""
if key.kind == String:
value = key.strVal
elif key.kind == Array:
let randomElem = getRandom(key.arrayVal)
if randomElem != nil and randomElem.kind == String:
value = randomElem.strVal
# Replace '#' with random alphanumerical character
return value.mapIt(if it == '#': randomChar() elif it == '$': randomNumber() else: it).join("")
proc getString*(profile: Profile, path: string, default: string = ""): string =
let key = profile.findKey(path)
return key.getStringValue(default)
proc getInt*(profile: Profile, path: string, default: int = 0): int =
let key = profile.findKey(path)
return key.getInt(default)
proc getBool*(profile: Profile, path: string, default: bool = false): bool =
let key = profile.findKey(path)
return key.getBool(default)
proc getTable*(profile: Profile, path: string): TomlTableRef =
let key = profile.findKey(path)
return key.getTable()
proc getArray*(profile: Profile, path: string): seq[TomlValueRef] =
let key = profile.findKey(path)
if key.kind != Array:
return @[]
return key.getElems()
proc isArray*(profile: Profile, path: string): bool =
let key = profile.findKey(path)
return key.kind == Array
#[
Data transformation
]#
proc applyDataTransformation*(profile: Profile, path: string, data: seq[byte]): string =
# 1. Encoding
var steps: seq[TomlTableRef] = @[]
# Apply all encoding techniques in the order specified in the profile
if profile.isArray(path & protect(".encoding")):
for encoding in profile.getArray(path & protect(".encoding")):
steps.add(encoding.getTable())
else:
steps = @[profile.getTable(path & protect(".encoding"))]
var dataString: string = Bytes.toString(data)
for step in steps:
case step.getTableValue(protect("type")).getStr(default = "none")
of protect("base64"):
dataString = encode(dataString, safe = step.getTableValue(protect("url-safe")).getBool()).replace("=", "")
of protect("hex"):
dataString = dataString.toHex().toLowerAscii()
of protect("rot"):
dataString = Bytes.toString(encodeRot(string.toBytes(dataString), step.getTableValue(protect("key")).getInt(default = 13)))
of protect("xor"):
dataString = Bytes.toString(xorBytes(string.toBytes(dataString), step.getTableValue(protect("key")).getInt(default = 1)))
of protect("none"):
discard
# 2. Add prefix & suffix
let prefix = profile.getString(path & protect(".prefix"))
let suffix = profile.getString(path & protect(".suffix"))
return prefix & dataString & suffix
proc reverseDataTransformation*(profile: Profile, path: string, data: string): seq[byte] =
# 1. Remove prefix & suffix
let
prefix = profile.getString(path & protect(".prefix"))
suffix = profile.getString(path & protect(".suffix"))
var dataString = data[len(prefix) ..^ len(suffix) + 1]
# 2. Decoding
var steps: seq[TomlTableRef] = @[]
# Apply all encoding techniques in reverse order
if profile.isArray(path & protect(".encoding")):
for encoding in profile.getArray(path & protect(".encoding")):
steps.add(encoding.getTable())
else:
steps = @[profile.getTable(path & protect(".encoding"))]
for step in steps.reversed():
case step.getTableValue(protect("type")).getStr(default = "none")
of protect("base64"):
dataString = decode(dataString)
of protect("hex"):
dataString = parseHexStr(dataString)
of protect("rot"):
dataString = Bytes.toString(decodeRot(string.toBytes(dataString), step.getTableValue(protect("key")).getInt(default = 13)))
of protect("xor"):
dataString = Bytes.toString(xorBytes(string.toBytes(dataString), step.getTableValue(protect("key")).getInt(default = 1)))
of protect("none"):
discard
return string.toBytes(dataString)