Содержание:

  • Введение.

  • Типы переменных

    • Строки

    • Числа

    • Массивы

  • Преобразование типов

  • Лексические элементы

    • Комментарии

    • Идентификаторы

    • Ключевые слова

      • Условия, циклы

      • 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 используются только целые знаковые числа неограниченной размерности. Они могут быть записаны в нескольких системах счисления:

  1. Десятичной Запись производится в соответствии с регулярным выражением: [+-]*[0-9]+

  2. Шестнадцатиричной Число записывается с помощью регулярного выражения: [+-]*0x[0-9A-F]+

  3. Восьмиричной. Число записывается с помощью регулярного выражения: [+-]*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"

Преобразование типов

Преобразование типов происходит динамически, в зависимости от места использования переменной.

Следует помнить несколько правил преобразования типов:

  1. Не все преобразования возвратны. Тип Октетная строка не хранит знака числа. Преобразование отрицательного числа в октетную строку выдаёт предупреждение на stderr о потере знака.

  2. Преобразование из печатной строки в число не всегда успешно. Если печатная строка содержит символы, не являющиеся цифрами, она будет интерпретирована как 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, dowhile, его выполнение приводит к немедленному выходу из внутреннего охватывающего цикла.

continue

Как и break, continue может встречаться только внутри циклов for, while, dowhile. Его выполнение приводит к немедленному переходу на следующую итерацию цикла.

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;
}