Содержание:
-
Введение.
-
Типы переменных
-
Строки
-
Числа
-
Массивы
-
-
Преобразование типов
-
Лексические элементы
-
Комментарии
-
Идентификаторы
-
Ключевые слова
-
Условия, циклы
-
break/continue
-
return
-
import
-
del
-
def
-
-
Массивы
-
Определение
-
Доступ к элементам
-
-
Переменные
-
Операторы
-
Доступные операторы
-
Приоритет операций
-
-
Функции
-
Определение
-
Вызов
-
builtin функции
-
-
Области видимости
-
Глобальная область видимости
-
Локальные области видимости
-
-
Модули
-
Подключение модуля
-
Стандартные пути
-
-
-
Стандартная библиотека
-
crypto
-
stdio
-
random
-
string
-
-
Примеры
-
Hello, World
-
RSAEP
-
Введение
Часто при реализации какого-либо криптографического протокола или алгоритма необходимо быстро получить вектора для тестирования. Для этого необходимо иметь набор инструментов, позволяющий быстро прототипировать выбранный протокол или алгоритм. Crypti - это интерпретируемый язык программирования с Си-подобным синтаксисом разработанный для этих целей, основные его особенности:
-
Нативная поддержка чисел произвольной длинны, что позволяет программисту отвлечься от низкоуровневых задач по контролю памяти и созданию или использованию библиотеки больших чисел;
-
Набор криптографических примитивов (хеш функции, симметричные и ассиметричные алгоритмы) позволяющих осуществлять быстрое прототипирование алгоритмов;
-
Использование библиотеки больших чисел, содержащей множество теоретико-числовых алгоритмов для операций с числами;
-
Слабая типизация, позволяющая преобразовывать содержание переменной в нужный программисту тип без вызова дополнительных функций в скрипте;
-
Интерпретатор и все встроенные криптоалгоритмы реализованы на языке C, что позволяет достичь высокой производительности.
Типы переменных
Crypti - язык со слабой динамической типизацией (например как Perl, PHP и т.д.), т.е. тип переменной выбирается в зависимости от места её использования. Это означает то что можно просто осуществлять операции с переменными разных типов, не заботясь о явном преобразовании. Например:
a = 42 b = a " universe"
сначала преобразует a в строку, а потом произведёт конкатенацию строки "42" и строки " universe", в итоге в b будет содержатья строка "42 universe"
Всего поддерживается 3 типа переменных:
1. Числа
В crypti используются только целые знаковые числа неограниченной размерности. Они могут быть записаны в нескольких системах счисления:
-
Десятичной Запись производится в соответствии с регулярным выражением: [+-]*[0-9]+
-
Шестнадцатиричной Число записывается с помощью регулярного выражения: [+-]*0x[0-9A-F]+
-
Восьмиричной. Число записывается с помощью регулярного выражения: [+-]*0[0-7]+
Если перед числом указано больше одного знака, то результирующий знак определяется по следующему правилу:
-) Если количество минусов перед знаком нечётное
+) В противном случае
Примеры определения чисел:
0xdeadbeef 0777 42 1000012 1234567890
2. Октетные строки
Представляют из себя последовательность однобайтовых символов. В отличие от C строк они не терменируются символом \0. Октетные строки в crypti выглядят так, потому что в процессе криптографических преобразований один или несколько символов в середине строки могут стать равными 0, что привело бы к уменьшению длины для C строк. В основном этот тип необходим для операций, производимых над последовательностями байтов, например конкатенации.
Примеры определения октетных строк:
`\x00\x44\x23\x11` `\x0d\x0a\x42`
3. Печатаемые строки
Аналог обычной С строки, терминированной нулём, и содержащей в себе печатную информацию о содержании переменной. В основном этот тип необходим для операций ввода вывода,
Примеры определения печатных строк:
"mystring" "m\x00\x01gg"
Преобразование типов
Преобразование типов происходит динамически, в зависимости от места использования переменной.
Следует помнить несколько правил преобразования типов:
-
Не все преобразования возвратны. Тип Октетная строка не хранит знака числа. Преобразование отрицательного числа в октетную строку выдаёт предупреждение на stderr о потере знака.
-
Преобразование из печатной строки в число не всегда успешно. Если печатная строка содержит символы, не являющиеся цифрами, она будет интерпретирована как 0 и на stderr будет выведено предупреждение.
Лексические элементы
Комментарии
Однострочные комментарии задаются с помощью последовательности //.
Пример:
Для написания многострочных комментариев используется последовательность /* */
Пример:
/* * this is commenary */
Идентификаторы
Идентификаторы можно задать в соответствии со следующим регулярным выражением. [a-zA-Z][a-zA-Z0-9]*
Ключевые слова
if/else
Выражение if используется для условного ветвления:
if (condition) block1 [else block2]
block1 выполняется только в том случае если condition не равно 0.
while
while используется для последовательного выполнения блока кода пока условие верно.
while (condition) block;
Если condition не указано то получившийся цикл аналогичен этому:
while (1) block;
do
Синтаксис:
do block while(condition)
Выражение эквивалентно выражению:
block; while(condition) block
for
Синтаксис:
for (expr1; condition; expr2) block;
Цикл for аналогичен данному циклу while:
expr1; while (condition) { block expr2 }
expr1, expr2 и condition могут быть пустыми
break
break может встречаться только внутри циклов for, while, do…while, его выполнение приводит к немедленному выходу из внутреннего охватывающего цикла.
continue
Как и break, continue может встречаться только внутри циклов for, while, do…while. Его выполнение приводит к немедленному переходу на следующую итерацию цикла.
return
return должен встречаться в теле функции. Он приводит немедленному завершению функции. При этом возвращаются текущие значения возвращаемых параметров функции.
import
Синтаксис:
import "modname" import <modname>
Ключевое слово, позволяющее импортировать модули в область главную область видимости. import должен присутствовать в глобальной области видимости (внутри условий, циклов, вложенных областях видимости import не обрабатывается).
del
Синтаксис:
del var
Ключевое слово, позволяющее удалить переменную из ближайшей области видимости и освободить занимаемую ей память.
def
def [ret1, ret2] funcname(param1, param2, ...) { block }
Ключевое слово, позволяющее определить новую или перепреоделить уже существующую функцию. Переопределение встроенных функций приводит к ошибке.
Массивы
Представляют из себя набор переменных, В crypti все массивы являются ассоциативными, т.е. индексом массива может быть как числа так и строки. Индекс состоит из перечисленных через раздельный символ выражений и указывается в квадратных скобках. За счёт этого достигается эмуляция многомерных массивов.
Примеры опеределения массивов
arr[1] = "my"; arr["name"] = 1; arr["job"] = "programming"; arr["example", "of", "multidimentional", "array"] = "there";
Инициализация
Массивы определяются с помощью перечисленных через запятую пар ключ ⇒ значение, заключённых в фигурные скобки, где ключ служет индексом в массиве для доступа к этому значению.
Например:
arr = {"one" => 1, "two" => 2}
Так же элементы массива можно определить по очереди Например написанное выше можно переписать как:
arr["one"] = 1; arr["two"] = 2
Если в определении ключи отсутствуют то по умолчанию значения размещаются в ячейках с индексом начиная с 0 и далее. Например:
arr = {"one", 2, 3, "some"}
значение "one" будет доступно при обращении arr[0], значение 2 при обращении arr[1] и так далее.
Доступ к элементам
Доступ к элементам массива происходит посредством передачи значения между квадратными скобками.
Например:
arr[1] arr["two"] arr["42"]
Попытка доступа к несуществующему элементу будет приводить к <Runtime error>
Переменные
Переменная - это идентификатор и связанная с ним область данных. Тип переменной динамически определяется во время использования.
Операторы
Доступные операторы
Ниже приведён список доступных операторов. Если не будет указано обратное то операторы бинарные.
Синтаксис использования бинарных операторов:
a OP b
Где:
a и b операнды - переменные или выражения стоящие слева и справа от оператора;
OP один из возможных операторов.
Арифметические, логические, побитовые операторы представляют операнды как числа а затем выполняют одну из следующих операций.
Арифметические операторы
Оператор |
Описание |
+ |
Складывает a и b. |
- |
Вычитает b из a. |
* |
Перемножает a и b. |
/ |
Делит a на b. |
** |
Возводит a в степень b. |
% |
Находит отстаток от деления a на b. |
+ |
Унарный оператор. Синтаксис использования: + a. Возвращает значение числа a. |
- |
Унарный оператор. Синтаксис использования: - a. Находит арифметически обратное число для a. |
Логические операторы
Оператор |
Описание |
&& |
Находит результат логического И a и b. |
|| |
Находит результат логического ИЛИ a и b. |
! |
Унарный оператор. Синтаксис использования: ! a. Находит логическое НЕ a. |
== |
Проверяет равны ли a и b. |
Побитовые операторы
Оператор |
Описание |
^ |
Находит результат исключающего ИЛИ a и _b-. Дополняет старшие разряды меньшего числа нулями. |
| |
Находит результат бинарного ИЛИ a и b. Дополняет старшие разряды меньшего числа нулями. |
& |
Находит результат бинарного ИЛИ a и b. Дополняет старшие разряды меньшего числа нулями. |
>> |
Сдвигает a на b разрядов вправо |
<< |
Сдвигает a на b разрядов влево |
~ |
Унарный оператор. Синтаксис использования: ~ a. Находит побитовое НЕ a. |
Операторы присваивания
Оператор |
Описание |
= |
Присваивает a значение b Доступно параллельное присваивание. Например: [a, b] = [b, a] [n, l, y] = func_with_3_outputs() |
op= |
Выполняет операцию op с a и b, затем присваивает a получившийся результат. |
Операторы с октетными строками
Представляют операнды как октетные строки и выполняют операцию.
Оператор |
Описание |
# |
Выполняет конкатенацию a и b |
Операторы с печатными строками
Представляют операнды как печатные строки и выполняют операцию.
Оператор |
Описание |
(пробел) |
Выполняет конкатенацию a и b |
Прочие операторы
Оператор |
Описание |
:? |
Тренарный оператор. Синтаксис: expr ? if_true : if_false Аналогичный Си тренарный оператор. Если expr истинно выполняется выражение if_true, в обратном случае выполняется if_false |
. |
Синтаксис: a.b. операция взятия атрибута b у переменной a. попытка взятия не существующего атрибута приводит к <Runtime error> |
Приоритетность
В таблице, приведённой ниже операторы перечисленны по возрастающей приоритетности.
ТАБЛИЦА
Операция |
Очерёдность |
a [] |
слева направо |
a.b |
слева направо |
** |
справа налево |
~ |
слева направо |
+ a - a |
слева направо |
* / % |
слева направо |
+ - |
слева направо |
<< >> |
слева направо |
< ⇐ >= |
слева направо |
== != |
слева направо |
& |
слева направо |
^ |
слева направо |
| |
слева направо |
&& |
слева направо |
! |
слева направо |
|| |
слева направо |
= op= |
справа налево |
Функции
Функции - набор логически выделенных инструкций, вызываемых по требованию. В crypti все определённые функции (включая встроеные) хрянятся в отдельной таблице, однако во избежание путанницы интерпретатор реализован так, что функции не могут иметь те же имена что переменные. По умолчанию параметры, переданные в функцию передаются как копии. То есть их изменение внутри функции не повлияет на значения после вызова. Возвращаемые значения записываются в квадратных скобках при определении функции. При достижении конца функции или ключевого слова return возвращаются их текущие значения Если на момент выхода из функции одно или несколько значений не определены - генерируется <Runtime error>
Пример:
a = 2 def [] func(SOME_WORD b) { b += 2; } func(a);
После вызова функции func a будет равно 4
Определение
Определение функции заносит новую функцию в таблицу, если функция уже присутствует в таблице и не является встроенной, то старое определение заменится новым. Переопределение встроенных функций не допускается.
Функция определяется таким образом:
def [ret1, ret2] func_name(parameter_list) { body }
Где:
[ret1, ret2] - список возвращаемых функцией аргументов (который может быть пустым) func_name - идентификатор, являющийся именем функции. parameter_list - перечисленные через запятую идентификаторы, являющиеся аргументами функции body - набор инструкций, выполняемых при вызове функции. Для блока инструкций внутри тела функции создаётся отдельная область видимости (см. Области видимости), локальные переменные определённые в ней пропадают в момент выхода из функции.
Вызов
Вызов функции осуществляется с помощью конструкции
func_name(parameter_list)
Где: func_name - идентификатор уже определённой функции. parameter_name - перечисленные через запятую выражения, результаты которых будут являться аргументами функции.
builtin функции
В Crypti содержится несколько встроеных функций, Эти функции не могут быть переопределены или удалены они всегда доступны для вызова. Ниже в алфавитном порядке перечисленны сами функции.
print(var, ...)
выводит на stdout переданные аргументы.
printf([format_string], ...)
Выводит на stdout переданные аргументы в соответствии с форматной строкой. При нехватке/переизбытке агрументов указанных в форматной строке печатает предупредительное соообщение
Области видимости
Область видимости содержит в себе набор имён переменных и ассоциированных с ними данных.
Для каждого блока инструкций обрамлённого символами { и } и для каждого вызова функции создаётся своя область видимости.
Пример:
{ a = 2 b = 4 }
Это значит что все определённые в этой области видимости переменные доступны только в этой области видимости, и доступ к ним при выходе из этой области невозможен. Поиск переменной происходит от текущей области видимости к глобальной. Это значит что программа может пользоваться переменными из области видимости меньшего уровня вложенности.
Глобальная область видимости
Внешняя область видимости. В ней доступны инструкции для подключения модулей, возможость определения новых функций.
Локальные области видимости.
Модули
Модуль это файл, написаный на языке crypti и импортированный в программу с помощью инструкции import. Все функции и переменные, определённые в модуле, импортируются в глобальную область видимости. для предотвращения бесконечного импортирования модулей (например когда модуль А импортирует модуль Б, а тот в свою очередь импортирует модуль А) информация об импортированном модуле заносится в таблицу импорта. При каждой новой попытке импорта проверяется таблица импорта, и если данный модуль уже импортирован, то запрос импорта пропускается. Это означает что если произошёл импорт, и затем произошли изменения в модуле то не существует никакого способа обновления данных модуля. Возможно в следующих версиях интерпретатора будет реализована специальная инструкция require, с помощью которой будет доступна перезагрузка содержания модуля.
Подключение
Модули подключаются с помощью ключевого слова import. Если подключается модуль из стандартной библиотеки то имя модуля обрамляется символами < и >. Пример:
import <crypto>
Если подключается файл, определённый пользователем, то указывается относительный от данного каталога путь, обрамлённый символами двойной кавычки. Пример:
import "crypto"
Стандартные пути расположения модулей
Стандартная библиотека
Описание функций (функции разбиты на несколько категорий)
1. crypto
[res] mod_inv(num, modulo)
Находит обратное num число по модулю modulo или -1 если такого числа не существует.
[res] mod_exp(n, exp, modulo)
Вычисляет (n ** exp) % modulo
[digest] md5(str) [ret] md5_ctx_init(id) [] md5_ctx_update(id, msg) [digest] md5_ctx_finalize(id)
Набор функций для получения md5 хеша.
[digest] whirpool(str) [ret] whirpool_ctx_init(id) [] whirpool_ctx_update(id, msg) [digest] whirpool_ctx_finalize(id)
Набор функций для получения whirpool хеша.
[digest] sha1(str) [ret] sha1_ctx_init(id) [] sha1_ctx_update(id, msg) [digest] sha1_ctx_finalize(id)
Набор функций для получения sha1 хеша.
[digest] sha256(str) [ret] sha256_ctx_init(id) [] sha256_ctx_update(id, msg) [digest] sha256_ctx_finalize(id)
Набор функций для получения sha256 хеша.
[ctx] aes_ctx_new() [] aes_set_key(ctx, key, keylen) [out] aes_encrypt(ctx, in) [out] aes_decrypt(ctx, in)
Набор функций для шифрования и дешифрования с помощью алгоритма aes (unimplemented now)
2. stdio
*( Functions unimplemented now)*
[fd] fopen(path, mode) [] fclose(fd)
fopen, fclose функции для открытия и закрытия файла. При успешном завершении fopen возвращает дескриптор файла, небольшое целое положительное число, которое должно быть использовано при вызове функций fread, fwrite, fseek, ftell.
аргумент mode - строка, которая может содержать одну из следующих последовательностей:
-
"r" - открывает файл для чтения. Начальное смещение находится на начале файла;
-
"r+" - открывает файл для чтения и записи. Начальное смещение находится на начале файла;
-
"w" - Изменяет длину файла до нуля и открывает его для записи. Начальное смещение находится на начале файла;
-
"w+" - открывает файл для чтения. Начальное смещение находится на начале файла;
-
"a" - открывает файл для записи в конец. Начальное смещение находится на конце файла;
-
"a+" - открывает файл для чтения и записи в конец. Начальное смещение находится на начале файла.
[oct_str] fread(fd, len) [nrbytes] fwrite(fd, octstr, len)
Функция fread возвращает len байт из файла с дескриптором fd функция fwrite записывает в файл ассоциированный с дискриптором fd len байт из октетной строки octstr
[cur_offset] fseek(fd, offset, whence) [cur_offset] ftell(fd)
fseek изменяет смещение для чтения/записи на offset байт в соответствии с диррективой whence, принимающей одно из нескольких значений * SEEK_CUR смещение относительно текущей позиции * SEEK_START смещение относительно начала файла * SEEK_END смещение относительно конца файла
ftell - возвращает текущее смещение в файле.
3.random
[res] randprime(nbytes)
Генерирует случайное число длиннной nbytes байт.
def [int] randint(start, stop)
Возвращает случайное число из промежутка [start; _stop)
def [octs] randocts(len)
Возвращает случайную октетную строку длинной nbytes байт.
4.string
[dst] lpad(src, width, filler) [dst] rpad(src, width, filler)
Дополняет src строку слева или справа до длинны width с помощью filler.
[sub] subs(string, start, len) [sub] subocts(string, start, len)
Возвращает часть строки/октетной строки начиная с start, len байт длинной.
[n] len(s) [nbytes] size(octs)
Возвращает длину строки s/ количество байтов в октетной строке octs
Примеры
1. Hello, World
Следующий фрагмент кода определяет функцию, печатающую "hello world" на стандартный вывод при вызове.
def [] hello() { print "Hello, World" } hello()
2. RSAEP
Функция ниже - пример реализации шифрования по алгоритму RSA
def [c, error] RSAEP (n, e, m) { if (m < 0 || m > n - 1) { error = "message representative out of range" c = 0 return } c = mod_exp(m, e, n) error = "" return; }