OpenClonk
C4ScriptLibraries.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2018, The OpenClonk Team and contributors
5  *
6  * Distributed under the terms of the ISC license; see accompanying file
7  * "COPYING" for details.
8  *
9  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
10  * See accompanying file "TRADEMARK" for details.
11  *
12  * To redistribute this file separately, substitute the full license texts
13  * for the above references.
14  */
15 
16 #include "C4Include.h"
18 
19 #ifdef _MSC_VER
20 # pragma warning(push)
21 # pragma warning(disable: 4804) // 'operation' : unsafe use of type 'bool' in operation
22 #endif
23 #include "blake2.h"
24 #ifdef _MSC_VER
25 # pragma warning(pop)
26 #endif
27 
28 #include "script/C4Aul.h"
29 #include "script/C4AulDefFunc.h"
30 
31 C4ScriptLibrary::C4ScriptLibrary(const char *name) : C4PropListStatic(nullptr, nullptr, ::Strings.RegString(name)) {}
32 
33 void C4ScriptLibrary::RegisterWithEngine(C4AulScriptEngine *engine)
34 {
37  Freeze();
38 }
39 
40 // Define USE_Z85 to 0 if you want to use Adobe's Ascii-85 encoding, which has
41 // easier to understand encode/decode tables but uses characters that require
42 // escaping in string constants, or to 1 to use ZeroMQ's variant which has a
43 // more complicated decode table but avoids those characters
44 #define USE_Z85 1
45 namespace {
46  constexpr const char encoder_table[] =
47 #if USE_Z85
48  "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#"
49 #else
50  "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstu"
51 #endif
52  ;
53  static_assert(sizeof(encoder_table) == 85 + 1, "encoder table has more than 85 entries");
54 
55  constexpr const char decoder_table[] = {
56  // Omits codes 0..31 because they're nonprintable and not part of the Base85 alphabets
57 #if USE_Z85
58  '\xff', '\x44', '\xff', '\x54', '\x53', '\x52', '\x48', '\xff',
59  '\x4b', '\x4c', '\x46', '\x41', '\xff', '\x3f', '\x3e', '\x45',
60  '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
61  '\x08', '\x09', '\x40', '\xff', '\x49', '\x42', '\x4a', '\x47',
62  '\x51', '\x24', '\x25', '\x26', '\x27', '\x28', '\x29', '\x2a',
63  '\x2b', '\x2c', '\x2d', '\x2e', '\x2f', '\x30', '\x31', '\x32',
64  '\x33', '\x34', '\x35', '\x36', '\x37', '\x38', '\x39', '\x3a',
65  '\x3b', '\x3c', '\x3d', '\x4d', '\xff', '\x4e', '\x43', '\xff',
66  '\xff', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', '\x10',
67  '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x18',
68  '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f', '\x20',
69  '\x21', '\x22', '\x23', '\x4f', '\xff', '\x50', '\xff', '\xff'
70 #else
71  '\xff', '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06',
72  '\x07', '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e',
73  '\x0f', '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16',
74  '\x17', '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e',
75  '\x1f', '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26',
76  '\x27', '\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e',
77  '\x2f', '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36',
78  '\x37', '\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e',
79  '\x3f', '\x40', '\x41', '\x42', '\x43', '\x44', '\x45', '\x46',
80  '\x47', '\x48', '\x49', '\x4a', '\x4b', '\x4c', '\x4d', '\x4e',
81  '\x4f', '\x50', '\x51', '\x52', '\x53', '\x54', '\xff', '\xff',
82  '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
83 #endif
84  };
85 
86  std::string b85_encode(const unsigned char *input, size_t length)
87  {
88  // Input strings need to be padded to be a multiple of 4 bytes in
89  // length
90  size_t padding = (4 - length % 4) % 4;
91 
92  // The encoding process adds an overhead of 20%
93  size_t output_length = (length + padding) * 5 / 4;
94  std::string encoded(output_length, '\0');
95 
96  int encode_cursor = 0;
97  for (size_t i = 0; i < length; i += 4)
98  {
99  uint32_t value = 0;
100  for (int j = 0; j < 4; ++j)
101  {
102  // Merge one byte
103  value *= 256;
104  if (i + j < length)
105  value += input[i + j];
106  }
107  // Write encoded data
108  for (int j = 4; j >= 0; --j)
109  {
110  encoded[encode_cursor + j] = encoder_table[value % 85];
111  value /= 85;
112  }
113  encode_cursor += 5;
114  }
115  // Remove the padding
116  encoded.resize(output_length - padding);
117  return encoded;
118  }
119 
120  std::string b85_decode(const std::string &input)
121  {
122  // Input strings need to be padded to a multiple of 5 bytes
123  size_t length = input.size();
124  size_t padding = (5 - length % 5) % 5;
125 
126  size_t output_length = (length + padding) * 4 / 5;
127  std::string decoded(output_length, '\0');
128 
129  int decode_cursor = 0;
130  for (size_t i = 0; i < length; i += 5)
131  {
132  uint32_t value = 0;
133  for (int j = 0; j < 5; ++j)
134  {
135  // Merge one byte
136  value *= 85;
137  if (i + j < length)
138  {
139  unsigned char byte = input[i + j];
140  if (byte >= 32 && byte <= 127)
141  value += decoder_table[byte - 32];
142  }
143  else
144  {
145  value += 84;
146  }
147  }
148  // Write decoded data
149  for (int j = 3; j >= 0; --j)
150  {
151  decoded[decode_cursor + j] = static_cast<char>(value & 0xFF);
152  value /= 256;
153  }
154  decode_cursor += 4;
155  }
156  decoded.resize(output_length - padding);
157  return decoded;
158  }
159 }
160 
162 {
163  static constexpr const char *LIBRARY_NAME = "_Crypto";
164  static constexpr const char *FUNC_COMPUTE_HASH = "_ComputeHash";
165 
166  // Calculates a hash value over a given input string.
167  static C4Value Hash_Dispatch(C4PropList *_this, C4String *input, int32_t output_length)
168  {
169  constexpr auto MAX_EXPANDED_OUTPUT = BLAKE2B_OUTBYTES * 5 / 4;
170  if (output_length < 1 || output_length > MAX_EXPANDED_OUTPUT)
171  {
172  throw C4AulExecError(strprintf("outputLength must be between 1 and %d", MAX_EXPANDED_OUTPUT).c_str());
173  }
174  if (!input)
175  {
176  throw C4AulExecError(strprintf(R"(call to "%s" parameter %d: passed %s, but expected %s)", FUNC_COMPUTE_HASH, 1, GetC4VName(C4V_Nil), GetC4VName(C4V_String)).c_str());
177  }
178 
179  // Reduce target hash length to account for Base85 encoding
180  int32_t raw_output_length = output_length * 4 / 5 + (output_length % 5 != 0);
181 
182  const unsigned char *data = (const unsigned char*) input->GetCStr();
183  const int data_length = input->GetData().getLength();
184 
185  auto hash_output = std::make_unique<unsigned char[]>(raw_output_length);
186  if (blake2b(hash_output.get(), raw_output_length, data, data_length, nullptr, 0) != 0)
187  {
188  throw C4AulExecError("internal error: blake2b call failed");
189  }
190 
191  std::string encoded = b85_encode(hash_output.get(), raw_output_length);
192  // Need to null-terminate here because StdStrBuf doesn't, so then
193  // C4String's GetCStr() doesn't actually return a C string.
194  encoded.resize(output_length);
195  return C4VString(StdStrBuf(encoded.c_str(), output_length, false));
196  }
197 
198 protected:
199  void CreateFunctions() override
200  {
201  AddFunc(this, FUNC_COMPUTE_HASH, &Hash_Dispatch);
202  }
203 
204 public:
206 };
207 
209 {
210  C4ScriptLibrary *libraries[] = {
212  };
213 
214  for (auto library : libraries)
215  {
216  library->RegisterWithEngine(engine);
217  }
218 }
void AddFunc(C4PropListStatic *Parent, const char *Name, RType(*pFunc)(ThisType *, ParTypes...), bool Public=true)
Definition: C4AulDefFunc.h:261
C4StringTable Strings
Definition: C4Globals.cpp:42
const char * GetC4VName(const C4V_Type Type)
Definition: C4Value.cpp:32
@ C4V_Nil
Definition: C4Value.h:25
@ C4V_String
Definition: C4Value.h:29
C4Value C4VPropList(C4PropList *p)
Definition: C4Value.h:242
C4Value C4VString(C4String *pStr)
Definition: C4Value.h:243
std::string strprintf(const char *format,...)
Definition: Standard.cpp:838
void RegisterGlobalConstant(const char *szName, const C4Value &rValue)
Definition: C4Aul.cpp:123
void Freeze()
Definition: C4PropList.h:132
C4RefCntPointer< C4String > ParentKeyName
Definition: C4PropList.h:279
void CreateFunctions() override
static void InstantiateAllLibraries(C4AulScriptEngine *engine)
virtual void CreateFunctions()=0
C4ScriptLibrary(const char *name)
StdStrBuf GetData() const
Definition: C4StringTable.h:50
const char * GetCStr() const
Definition: C4StringTable.h:49
size_t getLength() const
Definition: StdBuf.h:445