1
/*
2
*******************************************************************************
3
\file dstu.c
4
\brief DSTU 4145-2002 (Ukraine): digital signature algorithms
5
\project bee2 [cryptographic library]
6
\author (C) Sergey Agievich [agievich@{bsu.by|gmail.com}]
7
\created 2012.04.27
8
\version 2016.04.22
9
\license This program is released under the GNU General Public License 
10
version 3. See Copyright Notices in bee2/info.h.
11
*******************************************************************************
12
*/
13

14
#include "bee2/core/blob.h"
15
#include "bee2/core/err.h"
16
#include "bee2/core/mem.h"
17
#include "bee2/core/str.h"
18
#include "bee2/core/util.h"
19
#include "bee2/crypto/dstu.h"
20
#include "bee2/math/ec2.h"
21
#include "bee2/math/gf2.h"
22
#include "bee2/math/ww.h"
23
#include "bee2/math/zz.h"
24

25
/*
26
*******************************************************************************
27
Глубина стека
28

29
Высокоуровневые функции сообщают о потребностях в стековой памяти через 
30
функции интерфейса _dstu_deep_i. Потребности не должны учитывать память для 
31
размещения описаний базового поля и эллиптической кривой.
32
*******************************************************************************
33
*/
34

35
typedef size_t (*_dstu_deep_i)(
36
	size_t n,					/* размерность (в машинных словах) */
37
	size_t f_deep,				/* глубина стека базового поля */
38
	size_t ec_d,				/* число проективных координат */
39
	size_t ec_deep				/* глубина стека эллиптической кривой */
40
);
41

42
/*
43
*******************************************************************************
44
Стандартные параметры: dstu_163pb 
45
[базовая точка взята из приложения Б]
46
*******************************************************************************
47
*/
48

49
static const char _curve163pb_name[] = "1.2.804.2.1.1.1.1.3.1.1.1.2.0";
50

51
static u16 _curve163pb_p[4] = {163, 7, 6, 3};
52

53
static octet _curve163pb_A = 1;
54

55
static octet _curve163pb_B[] = {
56
	0x21, 0x5D, 0x45, 0xC1, 0x19, 0x8A, 0x63, 0x5E,
57
	0x92, 0x03, 0xB4, 0x0A, 0x21, 0xC8, 0x2D, 0x2A,
58
	0x46, 0x08, 0x61, 0xFF, 0x05,
59
};
60

61
static octet _curve163pb_n[] = {
62
	0x4D, 0xF1, 0xBC, 0x39, 0x2D, 0x26, 0xE2, 0x2B,
63
	0xC1, 0xBE, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
64
	0x00, 0x00, 0x00, 0x00, 0x04,
65
};
66

67
static octet _curve163pb_c = 2;
68

69
static octet _curve163pb_P[] = {
70
// x
71
	0x20, 0x04, 0x54, 0x8C, 0x5C, 0x88, 0x74, 0xFE,
72
	0xAF, 0x01, 0xFF, 0xF9, 0x7D, 0xC2, 0x3A, 0xA9,
73
	0x93, 0x7F, 0x86, 0x2D, 0x07,
74
// y
75
	0x9B, 0xFD, 0xC3, 0xAD, 0x22, 0x11, 0xB8, 0x4A,
76
	0x5F, 0x9D, 0x59, 0xC5, 0x97, 0x2B, 0x85, 0x47,
77
	0x39, 0x9C, 0x4A, 0x22, 0x00,
78
};
79

80
/*
81
*******************************************************************************
82
Стандартные параметры: dstu_167pb
83
*******************************************************************************
84
*/
85

86
static const char _curve167pb_name[] = "1.2.804.2.1.1.1.1.3.1.1.1.2.1";
87

88
static u16 _curve167pb_p[4] = {167, 6};
89

90
static octet _curve167pb_A = 1;
91

92
static octet _curve167pb_B[] = {
93
	0xAC, 0x7D, 0x82, 0x5A, 0x31, 0xA4, 0xF1, 0x30,
94
	0x09, 0x8A, 0x51, 0x20, 0x9F, 0x75, 0x11, 0x08,
95
	0x23, 0xEB, 0xCE, 0xE3, 0x6E,
96
};
97

98
static octet _curve167pb_n[] = {
99
	0x1F, 0x70, 0xF7, 0x9F, 0xF2, 0xD7, 0xC7, 0xBC,
100
	0x2E, 0xB1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
101
	0xFF, 0xFF, 0xFF, 0xFF, 0x3F,
102
};
103

104
static octet _curve167pb_c = 2;
105

106
/*
107
*******************************************************************************
108
Стандартные параметры: dstu_173pb
109
*******************************************************************************
110
*/
111

112
static const char _curve173pb_name[] = "1.2.804.2.1.1.1.1.3.1.1.1.2.2";
113

114
static u16 _curve173pb_p[4] = {173, 10, 2, 1};
115

116
static octet _curve173pb_A = 0;
117

118
static octet _curve173pb_B[] = {
119
	0xD9, 0x37, 0xB4, 0x6F, 0x6B, 0x8F, 0x27, 0xBB,
120
	0x3B, 0x85, 0xF6, 0xDD, 0x6E, 0xC1, 0x2F, 0xDB,
121
	0x99, 0x04, 0xC8, 0x76, 0x85, 0x10,
122
};
123

124
static octet _curve173pb_n[] = {
125
	0x31, 0x28, 0xBB, 0x25, 0x38, 0x6E, 0x60, 0x67,
126
	0x4E, 0x9B, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
127
	0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
128
};
129

130
static octet _curve173pb_c = 4;
131

132
/*
133
*******************************************************************************
134
Стандартные параметры: dstu_179pb
135
*******************************************************************************
136
*/
137

138
static const char _curve179pb_name[] = "1.2.804.2.1.1.1.1.3.1.1.1.2.3";
139

140
static u16 _curve179pb_p[4] = {179, 4, 2, 1};
141

142
static octet _curve179pb_A = 1;
143

144
static octet _curve179pb_B[] = {
145
	0x10, 0xB7, 0xBE, 0x72, 0x45, 0x18, 0x04, 0x2D,
146
	0xE3, 0x41, 0xA3, 0x07, 0xDD, 0x88, 0x2F, 0x6F,
147
	0x43, 0x26, 0x65, 0x85, 0xE0, 0xA6, 0x04,
148
};
149

150
static octet _curve179pb_n[] = {
151
	0xEF, 0x36, 0x42, 0xB6, 0x5A, 0xFE, 0x35, 0x04,
152
	0x96, 0x81, 0xB9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
153
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03,
154
};
155

156
static octet _curve179pb_c = 2;
157

158
/*
159
*******************************************************************************
160
Стандартные параметры: dstu_191pb
161
*******************************************************************************
162
*/
163

164
static const char _curve191pb_name[] = "1.2.804.2.1.1.1.1.3.1.1.1.2.4";
165

166
static u16 _curve191pb_p[4] = {191, 9};
167

168
static octet _curve191pb_A = 1;
169

170
static octet _curve191pb_B[] = {
171
	0x03, 0xFC, 0xFE, 0x50, 0x27, 0x48, 0xE0, 0x27,
172
	0xFF, 0x81, 0x49, 0x6B, 0x8B, 0x0E, 0x89, 0xD5,
173
	0xC4, 0x2E, 0x90, 0x02, 0x21, 0x6E, 0xC8, 0x7B
174
};
175

176
static octet _curve191pb_n[] = {
177
	0x4F, 0x47, 0xF7, 0x88, 0x67, 0xBC, 0xDA, 0xC1,
178
	0xCA, 0x79, 0xA7, 0x69, 0x00, 0x00, 0x00, 0x00,
179
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
180
};
181

182
static octet _curve191pb_c = 2;
183

184
/*
185
*******************************************************************************
186
Стандартные параметры: dstu_233pb
187
*******************************************************************************
188
*/
189

190
static const char _curve233pb_name[] = "1.2.804.2.1.1.1.1.3.1.1.1.2.5";
191

192
static u16 _curve233pb_p[4] = {233, 9, 4, 1};
193

194
static octet _curve233pb_A = 1;
195

196
static octet _curve233pb_B[] = {
197
	0x2C, 0x4D, 0x45, 0xCE, 0x6E, 0x93, 0xAA, 0x26,
198
	0x03, 0x8A, 0x3B, 0xDD, 0xF5, 0x4E, 0xD5, 0x1B,
199
	0xA2, 0x64, 0x7E, 0xCF, 0xC7, 0x34, 0x55, 0x67,
200
	0x95, 0x50, 0xB1, 0x73, 0x69, 0x00,
201
};
202

203
static octet _curve233pb_n[] = {
204
	0xD7, 0xE0, 0xCF, 0x03, 0x26, 0x1D, 0x03, 0x22,
205
	0x69, 0x8A, 0x2F, 0xE7, 0x74, 0xE9, 0x13, 0x00,
206
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
207
	0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
208
};
209

210
static octet _curve233pb_c = 2;
211

212
/*
213
*******************************************************************************
214
Стандартные параметры: dstu_257pb
215
*******************************************************************************
216
*/
217

218
static const char _curve257pb_name[] = "1.2.804.2.1.1.1.1.3.1.1.1.2.6";
219

220
static u16 _curve257pb_p[4] = {257, 12};
221

222
static octet _curve257pb_A = 0;
223

224
static octet _curve257pb_B[] = {
225
	0x10, 0xBE, 0xE3, 0xDB, 0x6A, 0xEA, 0x9E, 0x1F,
226
	0x86, 0x57, 0x8C, 0x45, 0xC1, 0x25, 0x94, 0xFF,
227
	0x94, 0x23, 0x94, 0xA7, 0xD7, 0x38, 0xF9, 0x18,
228
	0x7E, 0x65, 0x15, 0x01, 0x72, 0x94, 0xF4, 0xCE,
229
	0x01,
230
};
231

232
static octet _curve257pb_n[] = {
233
	0x0D, 0x47, 0x7D, 0x90, 0x14, 0x77, 0xE1, 0xD3,
234
	0x87, 0xE9, 0x82, 0xF1, 0x3A, 0x21, 0x59, 0x67,
235
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
236
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
237
};
238

239
static octet _curve257pb_c = 4;
240

241
/*
242
*******************************************************************************
243
Стандартные параметры: dstu_307pb
244
*******************************************************************************
245
*/
246

247
static const char _curve307pb_name[] = "1.2.804.2.1.1.1.1.3.1.1.1.2.7";
248

249
static u16 _curve307pb_p[4] = {307, 8, 4, 2};
250

251
static octet _curve307pb_A = 1;
252

253
static octet _curve307pb_B[] = {
254
	0xBB, 0x68, 0x49, 0x90, 0x86, 0x01, 0xC9, 0xBD,
255
	0x90, 0x60, 0x8B, 0xF1, 0x0D, 0x05, 0x41, 0xE2,
256
	0xE2, 0xE2, 0x99, 0xC5, 0xC0, 0x96, 0x42, 0x4F,
257
	0xE9, 0x3D, 0x6D, 0x6C, 0x5E, 0x4B, 0x05, 0xB5,
258
	0x66, 0x36, 0xD5, 0xF7, 0xC7, 0x93, 0x03,
259
};
260

261
static octet _curve307pb_n[] = {
262
	0xB7, 0xB7, 0x22, 0x40, 0x60, 0xD4, 0x88, 0xA5,
263
	0xBB, 0x0F, 0x39, 0x0D, 0xA7, 0x5D, 0x82, 0xF3,
264
	0xC2, 0x79, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
265
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
266
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03,
267
};
268

269
static octet _curve307pb_c = 2;
270

271
/*
272
*******************************************************************************
273
Стандартные параметры: dstu_367pb
274
*******************************************************************************
275
*/
276

277
static const char _curve367pb_name[] = "1.2.804.2.1.1.1.1.3.1.1.1.2.8";
278

279
static u16 _curve367pb_p[4] = {367, 21};
280

281
static octet _curve367pb_A = 1;
282

283
static octet _curve367pb_B[] = {
284
	0x36, 0x51, 0x99, 0x56, 0x7B, 0x43, 0x55, 0x97,
285
	0xA7, 0x79, 0x4C, 0x39, 0x92, 0x3D, 0xF9, 0xB8,
286
	0xDA, 0xCA, 0x42, 0xFE, 0x2A, 0x0C, 0x4B, 0xA6,
287
	0xA4, 0x6A, 0xBF, 0x47, 0x6B, 0x55, 0x47, 0x44,
288
	0x65, 0xD5, 0x7A, 0x62, 0xD1, 0xF3, 0xA6, 0xB7,
289
	0xB0, 0x42, 0xD2, 0x8A, 0xFC, 0x43,
290
};
291

292
static octet _curve367pb_n[] = {
293

294
	0x49, 0x2D, 0x9B, 0x04, 0x44, 0xEF, 0x45, 0x22,
295
	0x81, 0xE8, 0x8C, 0xD2, 0x8F, 0x42, 0x22, 0x4F,
296
	0x82, 0xFA, 0xA3, 0x75, 0x0B, 0x30, 0x9C, 0x00,
297
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
298
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
299
	0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
300
};
301

302
static octet _curve367pb_c = 2;
303

304
/*
305
*******************************************************************************
306
Стандартные параметры: dstu_431pb
307
*******************************************************************************
308
*/
309

310
static const char _curve431pb_name[] = "1.2.804.2.1.1.1.1.3.1.1.1.2.9";
311

312
static u16 _curve431pb_p[4] = {431, 5, 3, 1};
313

314
static octet _curve431pb_A = 1;
315

316
static octet _curve431pb_B[] = {
317
	0xF3, 0xCA, 0x40, 0xC6, 0x69, 0xA4, 0xDA, 0x17,
318
	0x31, 0x49, 0xCA, 0x12, 0xC3, 0x2D, 0xAE, 0x18,
319
	0x6B, 0x53, 0xAC, 0x6B, 0xC6, 0x36, 0x59, 0x97,
320
	0xDE, 0xAE, 0xAE, 0x8A, 0xD2, 0xD8, 0x88, 0xF9,
321
	0xBF, 0xD5, 0x34, 0x01, 0x69, 0x4E, 0xF9, 0xC4,
322
	0x27, 0x3D, 0x8C, 0xFE, 0x6D, 0xC2, 0x8F, 0x70,
323
	0x6A, 0x0F, 0x49, 0x10, 0xCE, 0x03,
324
};
325

326
static octet _curve431pb_n[] = {
327
	0xCF, 0x04, 0x05, 0x11, 0x95, 0x7A, 0x0C, 0xD9,
328
	0x80, 0xAF, 0xCB, 0x1F, 0x8A, 0xAA, 0x81, 0x2F,
329
	0xF0, 0x24, 0xA7, 0xC0, 0xA8, 0x09, 0x80, 0x45,
330
	0x75, 0x31, 0xBA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
331
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
332
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
333
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F,
334
};
335

336
static octet _curve431pb_c = 2;
337

338
/*
339
*******************************************************************************
340
Загрузка стандартных параметров
341
*******************************************************************************
342
*/
343

344
#define _LOAD_NAMED_PARAMS(params, name)\
345
	memCopy((params)->p, _##name##_p, sizeof(_##name##_p));\
346
	(params)->A = _##name##_A;\
347
	memCopy((params)->B, _##name##_B, sizeof(_##name##_B));\
348
	memCopy((params)->n, _##name##_n, sizeof(_##name##_n));\
349
	(params)->c = _##name##_c;\
350

351

352 1
err_t dstuStdParams(dstu_params* params, const char* name)
353
{
354 1
	if (!memIsValid(params, sizeof(dstu_params)))
355 0
		return ERR_BAD_INPUT;
356 1
	memSetZero(params, sizeof(dstu_params));
357 1
	if (strEq(name, _curve163pb_name))
358
	{
359 1
		_LOAD_NAMED_PARAMS(params, curve163pb);
360 1
		memCopy(params->P, _curve163pb_P, sizeof(_curve163pb_P));
361 1
		return ERR_OK;
362
	}
363 1
	if (strEq(name, _curve167pb_name))
364
	{
365 1
		_LOAD_NAMED_PARAMS(params, curve167pb);
366 1
		return ERR_OK;
367
	}
368 1
	if (strEq(name, _curve173pb_name))
369
	{
370 1
		_LOAD_NAMED_PARAMS(params, curve173pb);
371 1
		return ERR_OK;
372
	}
373 1
	if (strEq(name, _curve179pb_name))
374
	{
375 1
		_LOAD_NAMED_PARAMS(params, curve179pb);
376 1
		return ERR_OK;
377
	}
378 1
	if (strEq(name, _curve191pb_name))
379
	{
380 1
		_LOAD_NAMED_PARAMS(params, curve191pb);
381 1
		return ERR_OK;
382
	}
383 1
	if (strEq(name, _curve233pb_name))
384
	{
385 1
		_LOAD_NAMED_PARAMS(params, curve233pb);
386 1
		return ERR_OK;
387
	}
388 1
	if (strEq(name, _curve257pb_name))
389
	{
390 1
		_LOAD_NAMED_PARAMS(params, curve257pb);
391 1
		return ERR_OK;
392
	}
393 1
	if (strEq(name, _curve307pb_name))
394
	{
395 1
		_LOAD_NAMED_PARAMS(params, curve307pb);
396 1
		return ERR_OK;
397
	}
398 1
	if (strEq(name, _curve367pb_name))
399
	{
400 1
		_LOAD_NAMED_PARAMS(params, curve367pb);
401 1
		return ERR_OK;
402
	}
403 1
	if (strEq(name, _curve431pb_name))
404
	{
405 1
		_LOAD_NAMED_PARAMS(params, curve431pb);
406 1
		return ERR_OK;
407
	}
408 0
	return ERR_FILE_NOT_FOUND;
409
}
410

411
/*
412
*******************************************************************************
413
Создание описания эллиптической кривой
414

415
По долговременным параметрам params формируется описание pec эллиптической
416
кривой. Указатель *pec является одновременно началом фрагмента памяти, 
417
в котором размещается состояние и стек. Длина фрагмента определяется с учетом 
418
потребностей deep и размерностей dims.
419
\pre Указатель pec корректен.
420
\return ERR_OK, если описание успешно создано, и код ошибки в противном 
421
случае.
422
\remark Запрошенная память начинается по адресу objEnd(*pec, void).
423
\remark Проводится минимальная проверка параметров, обеспечивающая 
424
работоспособность высокоуровневых функций.
425
*******************************************************************************
426
*/
427

428 1
static err_t _dstuCreateEc(
429
	ec_o** pec,						/* [out] описание эллиптической кривой */
430
	const dstu_params* params,		/* [in] долговременные параметры */
431
	_dstu_deep_i deep				/* [in] потребности в стековой памяти */
432
)
433
{
434
	// размерности
435
	size_t m;
436
	size_t n;
437
	size_t f_keep;
438
	size_t f_deep;
439
	size_t ec_d;
440
	size_t ec_keep;
441
	size_t ec_deep;
442
	// состояние
443
	void* state;	
444
	size_t* p;			/* описание многочлена */
445
	qr_o* f;			/* поле */
446
	octet* A;			/* коэффициент A */
447
	ec_o* ec;			/* кривая */
448
	void* stack;
449
	// pre
450 1
	ASSERT(memIsValid(pec, sizeof(*pec)));
451
	// минимальная проверка входных данных
452 1
	if (!memIsValid(params, sizeof(dstu_params)) ||
453 1
		(m = params->p[0]) < 160 || m > 509 || 
454 1
		params->A > 1)
455 0
		return ERR_BAD_PARAMS;
456
	// определить размерности
457 1
	n = W_OF_B(m);
458 1
	f_keep = gf2Create_keep(m);
459 1
	f_deep = gf2Create_deep(m);
460 1
	ec_d = 3;
461 1
	ec_keep = ec2CreateLD_keep(n);
462 1
	ec_deep = ec2CreateLD_deep(n, f_deep);
463
	// создать состояние
464 1
	state = blobCreate(
465 1
		f_keep + ec_keep +
466 1
		utilMax(4,
467
			4 * sizeof(size_t) + f_deep,
468 1
			O_OF_B(m) + ec_deep,
469
			ecCreateGroup_deep(f_deep),
470
			deep(n, f_deep, ec_d, ec_deep)));
471 1
	if (state == 0)
472 0
		return ERR_OUTOFMEMORY;
473
	// создать поле
474 1
	f = (qr_o*)((octet*)state + ec_keep);
475 1
	p = (size_t*)((octet*)f + f_keep);
476 1
	p[0] = params->p[0];
477 1
	p[1] = params->p[1];
478 1
	p[2] = params->p[2];
479 1
	p[3] = params->p[3];
480 1
	stack = p + 4;
481 1
	if (!gf2Create(f, p, stack))
482
	{
483 0
		blobClose(state);
484 0
		return ERR_BAD_PARAMS;
485
	}
486
	// создать кривую и группу
487 1
	ec = (ec_o*)state;
488 1
	A = (octet*)p;
489 1
	A[0] = params->A;
490 1
	memSetZero(A + 1, f->no - 1);
491 1
	stack = A + f->no;
492 1
	if (!ec2CreateLD(ec, f, A, params->B, stack) ||
493 1
		!ecCreateGroup(ec, params->P, params->P + ec->f->no, params->n, 
494 1
			ec->f->no, params->c, stack))
495
	{
496 0
		blobClose(state);
497 0
		return ERR_BAD_PARAMS;
498
	}
499
	// присоединить f к ec
500 1
	objAppend(ec, f, 0);
501
	// все нормально
502 1
	*pec = ec;
503 1
	return ERR_OK;
504
}
505

506
/*
507
*******************************************************************************
508
Закрытие описания эллиптической кривой
509
*******************************************************************************
510
*/
511

512 1
void _dstuCloseEc(ec_o* ec)
513
{
514 1
	blobClose(ec);
515
}
516

517
/*
518
*******************************************************************************
519
Проверка параметров
520

521
В ДСТУ требуется, чтобы
522
1) A \in {0, 1},
523
2) B != 0,
524
3) order >= 2^160,
525
4) order >= 4(\floor{\sqrt{2^m}} + 1),
526
5) кривая является безопасной с MOV-порогом 32.
527

528
Условие 1) проверяется в функции _dstuCreateEc().
529
Условие 2) проверяется в функции ec2IsValid().
530
Условие 3) проверяется непосредственно.
531
Условие 4) следует из границы Хассе
532
	order * cofactor >= 2^m + 1 - 2^{m/2}
533
при малом cofactor и достаточно большом m.
534
Граница Хассе проверяется в функции ec2IsValid().
535

536
Дополнительно проверяется, что базовая точка лежит на кривой и имеет
537
порядок order.
538
*******************************************************************************
539
*/
540

541 1
static size_t _dstuValParams_deep(size_t n, size_t f_deep, size_t ec_d, 
542
	size_t ec_deep)
543
{
544 1
	return utilMax(4,
545
			ec2IsValid_deep(n),
546
			ec2SeemsValidGroup_deep(n, f_deep),
547
			ec2IsSafeGroup_deep(n),
548
			ecHasOrderA_deep(n, ec_d, ec_deep, n));
549
}
550

551 1
err_t dstuValParams(const dstu_params* params)
552
{
553
	err_t code;
554
	// состояние
555
	ec_o* ec;
556
	void* stack;
557
	// старт
558 1
	code = _dstuCreateEc(&ec, params, _dstuValParams_deep);
559 1
	ERR_CALL_CHECK(code);
560 1
	stack = objEnd(ec, void);
561
	// проверить кривую и базовую точку
562 1
	if (wwBitSize(ec->order, ec->f->n) <= 160 ||
563 1
		!ec2IsValid(ec, stack) ||
564 1
		!ec2SeemsValidGroup(ec, stack) ||
565 1
		!ec2IsSafeGroup(ec, 32, stack) ||
566 1
		!ecHasOrderA(ec->base, ec, ec->order, ec->f->n, stack))
567 0
		code = ERR_BAD_PARAMS;
568
	// завершение
569 1
	_dstuCloseEc(ec);
570 1
	return code;
571
}
572

573
/*
574
*******************************************************************************
575
Управление точками
576
*******************************************************************************
577
*/
578

579 1
static size_t _dstuGenPoint_deep(size_t n, size_t f_deep, size_t ec_d, 
580
	size_t ec_deep)
581
{
582 1
	return O_OF_W(3 * n) + 
583 1
		utilMax(2,
584
			gf2QSolve_deep(n, f_deep),
585
			ecHasOrderA_deep(n, ec_d, ec_deep, n));
586
}
587

588 1
err_t dstuGenPoint(octet point[], const dstu_params* params, gen_i rng, 
589
	void* rng_state)
590
{
591
	err_t code;
592
	// состояние
593
	ec_o* ec;
594
	word* x;
595
	word* y;
596
	word* t;
597
	void* stack;
598
	// проверить rng
599 1
	if (rng == 0)
600 0
		return ERR_BAD_RNG;
601
	// старт
602 1
	code = _dstuCreateEc(&ec, params, _dstuGenPoint_deep);
603 1
	ERR_CALL_CHECK(code);
604
	// проверить входные указатели
605 1
	if (!memIsValid(point, 2 * ec->f->no))
606
	{
607 0
		_dstuCloseEc(ec);
608 0
		return ERR_BAD_INPUT;
609
	}
610
	// раскладка состояния
611 1
	x = objEnd(ec, word);
612 1
	y = x + ec->f->n;
613 1
	t = y + ec->f->n;
614 1
	stack = t + ec->f->n;
615
	// пока точка не сгенерирована
616
	while (1)
617
	{
618
		// сгенерировать x-координату
619
		// [алгоритм из раздела 6.4 ДСТУ --- обрезка x]
620 1
		rng(x, ec->f->no, rng_state);
621 1
		wwFrom(x, x, ec->f->no);
622 1
		wwTrimHi(x, ec->f->n, gf2Deg(ec->f));
623
		// y <- x^2
624 1
		qrSqr(y, x, ec->f, stack);
625
		// t <- x^3
626 1
		qrMul(t, x, y, ec->f, stack);
627
		// t <- x^3 + a x^2 + b
628 1
		if (!qrIsZero(ec->A, ec->f))
629 1
			gf2Add2(t, y, ec->f);
630 1
		gf2Add2(t, ec->B, ec->f);
631
		// y <- Solve[y^2 + x y == t], ord(x, y) == order?
632 1
		if (gf2QSolve(y, x, t, ec->f, stack) &&
633 1
			ecHasOrderA(x, ec, ec->order, ec->f->n, stack))
634 1
			break;
635
	}
636
	// выгрузить точку
637 1
	qrTo(point, x, ec->f, stack);
638 1
	qrTo(point + ec->f->no, y, ec->f, stack);
639
	// завершение
640 1
	_dstuCloseEc(ec);
641 1
	return ERR_OK;
642
}
643

644 0
static size_t _dstuValPoint_deep(size_t n, size_t f_deep, size_t ec_d, 
645
	size_t ec_deep)
646
{
647 0
	return O_OF_W(2 * n) + 
648 0
		utilMax(2,
649
			ec2IsOnA_deep(n, f_deep),
650
			ecHasOrderA_deep(n, ec_d, ec_deep, n));
651
}
652

653 0
err_t dstuValPoint(const dstu_params* params, const octet point[])
654
{
655
	err_t code;
656
	// состояние
657
	ec_o* ec;
658
	word* x;
659
	word* y;
660
	void* stack;
661
	// старт
662 0
	code = _dstuCreateEc(&ec, params, _dstuValPoint_deep);
663 0
	ERR_CALL_CHECK(code);
664
	// проверить входные указатели
665 0
	if (!memIsValid(point, 2 * ec->f->no))
666
	{
667 0
		_dstuCloseEc(ec);
668 0
		return ERR_BAD_INPUT;
669
	}
670
	// раскладка состояния
671 0
	x = objEnd(ec, word);
672 0
	y = x + ec->f->n;
673 0
	stack = y + ec->f->n;
674
	// (x, y) лежит на ЭК? (x, y) имеет порядок order?
675 0
	if (!qrFrom(x, point, ec->f, stack) ||
676 0
		!qrFrom(y, point + ec->f->no, ec->f, stack) ||
677 0
		!ec2IsOnA(x, ec, stack) ||
678 0
		!ecHasOrderA(x, ec, ec->order, ec->f->n, stack))
679 0
		code = ERR_BAD_POINT;
680
	// завершение
681 0
	_dstuCloseEc(ec);
682 0
	return code;
683
}
684

685 1
static size_t _dstuCompressPoint_deep(size_t n, size_t f_deep, size_t ec_d, 
686
	size_t ec_deep)
687
{
688 1
	return O_OF_W(2 * n) + gf2Tr_deep(n, f_deep);
689
}
690

691 1
err_t dstuCompressPoint(octet xpoint[], const dstu_params* params, 
692
	const octet point[])
693
{
694
	err_t code;
695
	// состояние
696
	ec_o* ec;
697
	word* x;
698
	word* y;
699
	void* stack;
700
	// старт
701 1
	code = _dstuCreateEc(&ec, params, _dstuCompressPoint_deep);
702 1
	ERR_CALL_CHECK(code);
703
	// проверить входные указатели
704 1
	if (!memIsValid(point, 2 * ec->f->no) || 
705 1
		!memIsValid(xpoint, ec->f->no))
706
	{
707 0
		_dstuCloseEc(ec);
708 0
		return ERR_BAD_INPUT;
709
	}
710
	// раскладка состояния
711 1
	x = objEnd(ec, word);
712 1
	y = x + ec->f->n;
713 1
	stack = y + ec->f->n;
714
	// загрузить точку
715 1
	if (!qrFrom(x, point, ec->f, stack) ||
716 1
		!qrFrom(y, point + ec->f->no, ec->f, stack))
717
	{
718 0
		_dstuCloseEc(ec);
719 0
		return ERR_BAD_POINT;
720
	}
721
	// x == 0?
722 1
	if (wwIsZero(x, ec->f->n))
723
	{
724 0
		_dstuCloseEc(ec);
725 0
		return ERR_OK;
726
	}
727
	// y <- y / x
728 1
	qrDiv(y, y, x, ec->f, stack);
729
	// xpoint <- x(point), xpoint_0 <- tr(y)
730 1
	memMove(xpoint, point, ec->f->no);
731 1
	xpoint[0] &= 0xFE;
732 1
	xpoint[0] |= gf2Tr(y, ec->f, stack);
733
	// завершение
734 1
	_dstuCloseEc(ec);
735 1
	return ERR_OK;
736
}
737

738 1
static size_t _dstuRecoverPoint_deep(size_t n, size_t f_deep, size_t ec_d, 
739
	size_t ec_deep)
740
{
741 1
	return O_OF_W(2 * n) + 
742 1
		utilMax(2,
743
			gf2QSolve_deep(n, f_deep),
744
			gf2Tr_deep(n, f_deep));
745
}
746

747 1
err_t dstuRecoverPoint(octet point[], const dstu_params* params, 
748
	const octet xpoint[])
749
{
750
	err_t code;
751
	register bool_t trace;
752
	// состояние
753
	ec_o* ec;
754
	word* x;
755
	word* y;
756
	void* stack;
757
	// старт
758 1
	code = _dstuCreateEc(&ec, params, _dstuRecoverPoint_deep);
759 1
	ERR_CALL_CHECK(code);
760
	// проверить входные указатели
761 1
	if (!memIsValid(xpoint, ec->f->no) || 
762 1
		!memIsValid(point, 2 * ec->f->no))
763
	{
764 0
		_dstuCloseEc(ec);
765 0
		return ERR_BAD_INPUT;
766
	}
767
	// раскладка состояния
768 1
	x = objEnd(ec, word);
769 1
	y = x + ec->f->n;
770 1
	stack = y + ec->f->n;
771
	// загрузить сжатое представление точки
772 1
	if (!qrFrom(x, xpoint, ec->f, stack))
773
	{
774 0
		_dstuCloseEc(ec);
775 0
		return ERR_BAD_POINT;
776
	}
777
	// x == 0?
778 1
	if (qrIsZero(x, ec->f))
779
	{
780 0
		size_t m = gf2Deg(ec->f);
781
		// b <- b^{2^{m - 1}}
782 0
		while (--m)
783 0
			qrSqr(ec->B, ec->B, ec->f, stack);
784
		// выгрузить y-координату
785 0
		qrTo(point + ec->f->n, ec->B, ec->f, stack);
786
		// все нормально
787 0
		_dstuCloseEc(ec);
788 0
		return ERR_OK;
789
	}
790
	// восстановить первый разряд x
791 1
	trace = wwTestBit(x, 0);
792 1
	wwSetBit(x, 0, 0);
793 1
	if (gf2Tr(x, ec->f, stack) != (bool_t)params->A)
794 1
		wwSetBit(x, 0, 1);
795
	// y <- x + a + b / x^2
796 1
	qrSqr(y, x, ec->f, stack);
797 1
	qrDiv(y, ec->B, y, ec->f, stack);
798 1
	gf2Add2(y, x, ec->f);
799 1
	if (params->A)
800 1
		wwFlipBit(y, 0);
801
	// Solve[z^2 + z == y]
802 1
	if (!gf2QSolve(y, ec->f->unity, y, ec->f, stack))
803
	{
804 0
		trace = 0;
805 0
		_dstuCloseEc(ec);
806 0
		return ERR_BAD_PARAMS;
807
	}
808
	// tr(y) == trace?
809 1
	if (gf2Tr(y, ec->f, stack) == trace)
810
		// y <- y * x
811 1
		qrMul(y, x, y, ec->f, stack);
812
	else
813
		// y <- (y + 1) * x
814 0
		qrMul(y, x, y, ec->f, stack),
815 0
		gf2Add2(y, x, ec->f);
816
	// выгрузить точку
817 1
	qrTo(point, x, ec->f, stack);
818 1
	qrTo(point + ec->f->no, y, ec->f, stack);
819
	// все нормально
820 1
	trace = 0;
821 1
	_dstuCloseEc(ec);
822 1
	return code;
823
}
824

825
/*
826
*******************************************************************************
827
Управление ключами
828
*******************************************************************************
829
*/
830

831 1
static size_t _dstuGenKeypair_deep(size_t n, size_t f_deep, size_t ec_d, 
832
	size_t ec_deep)
833
{
834 1
	return O_OF_W(3 * n) + 
835 1
		ecMulA_deep(n, ec_d, ec_deep, n);
836
}
837

838 1
err_t dstuGenKeypair(octet privkey[], octet pubkey[], 
839
	const dstu_params* params, gen_i rng, void* rng_state)
840
{
841
	err_t code;
842
	size_t order_n, order_no, order_nb;
843
	// состояние
844
	ec_o* ec;
845
	word* d;
846
	word* x;
847
	word* y;
848
	void* stack;
849
	// проверить rng
850 1
	if (rng == 0)
851 0
		return ERR_BAD_RNG;
852
	// старт
853 1
	code = _dstuCreateEc(&ec, params, _dstuGenKeypair_deep);
854 1
	ERR_CALL_CHECK(code);
855
	// размерности order
856 1
	order_nb = wwBitSize(ec->order, ec->f->n);
857 1
	order_no = O_OF_B(order_nb);
858 1
	order_n = W_OF_B(order_nb);
859
	// проверить входные указатели
860 1
	if (!memIsValid(privkey, order_no) || 
861 1
		!memIsValid(pubkey, 2 * ec->f->no))
862
	{
863 0
		_dstuCloseEc(ec);
864 0
		return ERR_BAD_INPUT;
865
	}
866
	// раскладка состояния
867 1
	d = objEnd(ec, word);
868 1
	x = d + ec->f->n;
869 1
	y = x + ec->f->n;
870 1
	stack = y + ec->f->n;
871
	// d <-R {1,2,..., order - 1}
872
	// [алгоритм из раздела 6.3 ДСТУ --- обрезка d]
873 1
	wwSetZero(d, order_n);
874
	while (1)
875
	{
876 1
		rng(d, O_OF_B(order_nb), rng_state);
877 1
		wwFrom(d, d, O_OF_B(order_nb));
878 1
		wwTrimHi(d, order_n, order_nb - 1);
879 1
		ASSERT(wwCmp(d, ec->order, order_n) < 0);
880
		// 0 < d?
881 1
		if (!wwIsZero(d, order_n))
882 1
			break;
883
	}
884
	// Q <- d G
885 1
	if (!ecMulA(x, ec->base, ec, d, order_n, stack))
886
	{
887
		// если params корректны, то этого быть не должно
888 0
		_dstuCloseEc(ec);
889 0
		return ERR_BAD_PARAMS;
890
	}
891
	// Q <- -Q
892 1
	ec2NegA(x, x, ec);
893
	// выгрузить ключи
894 1
	wwTo(privkey, order_no, d);
895 1
	qrTo(pubkey, x, ec->f, stack);
896 1
	qrTo(pubkey + ec->f->no, y, ec->f, stack);
897
	// все нормально
898 1
	_dstuCloseEc(ec);
899 1
	return code;
900
}
901

902
/*
903
*******************************************************************************
904
ЭЦП
905
*******************************************************************************
906
*/
907

908 1
static size_t _dstuSign_deep(size_t n, size_t f_deep, size_t ec_d, 
909
	size_t ec_deep)
910
{
911 1
	return O_OF_W(6 * n) + 
912 1
		utilMax(2,
913
			ecMulA_deep(n, ec_d, ec_deep, n),
914
			zzMulMod_deep(n));
915
}
916

917 1
err_t dstuSign(octet sig[], const dstu_params* params, size_t ld, 
918
	const octet hash[], size_t hash_len, const octet privkey[], 
919
	gen_i rng, void* rng_state)
920
{
921
	err_t code;
922
	size_t order_n, order_no, order_nb;
923
	// состояние
924
	ec_o* ec;
925
	word* e;		/* эфемерный лк */
926
	word* h;		/* хэш-значение как элемент поля */
927
	word* x;		/* х-координата эфемерного ок */
928
	word* y;		/* y-координата эфемерного ок */
929
	word* r;		/* первая часть ЭЦП */
930
	word* s;		/* вторая часть ЭЦП */
931
	void* stack;
932
	// проверить rng
933 1
	if (rng == 0)
934 0
		return ERR_BAD_RNG;
935
	// старт
936 1
	code = _dstuCreateEc(&ec, params, _dstuSign_deep);
937 1
	ERR_CALL_CHECK(code);
938
	// размерности order
939 1
	order_nb = wwBitSize(ec->order, ec->f->n);
940 1
	order_no = O_OF_B(order_nb);
941 1
	order_n = W_OF_B(order_nb);
942
	// проверить входные указатели
943
	// шаги 1, 2: проверка params, privkey
944
	// шаг 3: проверить ld
945 1
	if (!memIsValid(privkey, order_no) || 
946 1
		ld % 16 != 0 || ld < 16 * order_no ||
947 1
		!memIsValid(hash, hash_len) ||
948 1
		!memIsValid(sig, O_OF_B(ld)))
949
	{
950 0
		_dstuCloseEc(ec);
951 0
		return ERR_BAD_INPUT;
952
	}
953
	// раскладка состояния
954 1
	e = objEnd(ec, word);
955 1
	h = e + ec->f->n;
956 1
	x = h + ec->f->n;
957 1
	y = x + ec->f->n;
958 1
	r = y + ec->f->n;
959 1
	s = r + ec->f->n;
960 1
	stack = s + ec->f->n;
961
	// шаги 4 -- 6: хэширование
962
	// шаг 7: перевести hash в элемент основного поля h
963
	// [алгоритм из раздела 5.9 ДСТУ]
964 1
	if (hash_len < ec->f->no)
965
	{
966 1
		memCopy(h, hash, hash_len);
967 1
		memSetZero((octet*)h + hash_len, ec->f->no - hash_len);
968
	}
969
	else
970
	{
971 1
		memCopy(h, hash, ec->f->no);
972
		// memTrimHi(h, ec->f->no, gf2Deg(ec->f));
973 1
		((octet*)h)[ec->f->no - 1] &= (1 << gf2Deg(ec->f) % 8) - 1;
974
	}
975 1
	qrFrom(h, (octet*)h, ec->f, stack);
976
	// шаг 7: если h == 0, то h <- 1
977 1
	if (qrIsZero(h, ec->f))
978 0
		qrSetUnity(h, ec->f);
979
	// шаг 8: e <-R {1,2,..., order - 1}
980
	// [алгоритм из раздела 6.3 ДСТУ --- обрезка e]
981
step8:
982
	while (1)
983
	{
984 1
		rng(e, O_OF_B(order_nb), rng_state);
985 1
		wwFrom(e, e, O_OF_B(order_nb));
986 1
		wwTrimHi(e, order_n, order_nb - 1);
987 1
		ASSERT(wwCmp(e, ec->order, order_n) < 0);
988 1
		if (!wwIsZero(e, order_n))
989 1
			break;
990
	}
991
	// шаг 8: (x, y) <- e G
992 1
	if (!ecMulA(x, ec->base, ec, e, order_n, stack))
993
	{
994
		// если params корректны, то этого быть не должно
995 0
		_dstuCloseEc(ec);
996 0
		return ERR_BAD_PARAMS;
997
	}
998
	// шаг 8: если x == 0, то повторить генерацию
999 1
	if (qrIsZero(x, ec->f))
1000 0
		goto step8;
1001
	// шаг 9: y <- x * h
1002 1
	qrMul(y, x, h, ec->f, stack);
1003
	// шаг 10: r <- \bar{y}
1004 1
	ASSERT(order_n <= ec->f->n);
1005 1
	qrTo((octet*)r, y, ec->f, stack);
1006 1
	wwFrom(r, r, order_no);
1007 1
	wwTrimHi(r, order_n, order_nb - 1);
1008
	// шаг 11: если r = 0, то повторить генерацию
1009 1
	if (wwIsZero(r, order_n))
1010 0
		goto step8;
1011
	// шаг 12: s <- (e + dr) mod order
1012 1
	wwFrom(s, privkey, order_no);
1013 1
	zzMulMod(s, s, r, ec->order, order_n, stack);
1014 1
	zzAddMod(s, s, e, ec->order, order_n);
1015
	// шаг 13: если s = 0, то повторить генерацию
1016 1
	if (wwIsZero(s, order_n))
1017 0
		goto step8;
1018
	// шаг 14: сформировать ЭЦП из r и s
1019
	// [алгоритм из раздела 5.10 ДСТУ]
1020 1
	memSetZero(sig, O_OF_B(ld));
1021 1
	wwTo(sig, order_no, r);
1022 1
	wwTo(sig + ld / 16, order_no, s);
1023
	// все нормально
1024 1
	_dstuCloseEc(ec);
1025 1
	return code;
1026
}
1027

1028 1
static size_t _dstuVerify_deep(size_t n, size_t f_deep, size_t ec_d, 
1029
	size_t ec_deep)
1030
{
1031 1
	return O_OF_W(5 * n) + 
1032 1
		ecAddMulA_deep(n, ec_d, ec_deep, 2, n, n);
1033
}
1034

1035 1
err_t dstuVerify(const dstu_params* params, size_t ld, const octet hash[], 
1036
	size_t hash_len, const octet sig[], const octet pubkey[])
1037
{
1038
	err_t code;
1039
	size_t order_n, order_no, order_nb, i;
1040
	// состояние
1041
	ec_o* ec;
1042
	word* h;		/* хэш-значение как элемент поля */
1043
	word* x;		/* х-координата эфемерного ок */
1044
	word* y;		/* y-координата эфемерного ок */
1045
	word* r;		/* первая часть ЭЦП */
1046
	word* s;		/* вторая часть ЭЦП */
1047
	void* stack;
1048
	// старт
1049 1
	code = _dstuCreateEc(&ec, params, _dstuVerify_deep);
1050 1
	ERR_CALL_CHECK(code);
1051
	// размерности order
1052 1
	order_nb = wwBitSize(ec->order, ec->f->n);
1053 1
	order_no = O_OF_B(order_nb);
1054 1
	order_n = W_OF_B(order_nb);
1055
	// проверить входные указатели
1056
	// шаги 1, 2: обработка идентификатора хэш-функции
1057
	// шаг 3: проверить ld
1058 1
	if (!memIsValid(pubkey, 2 * ec->f->no) || 
1059 1
		ld % 16 != 0 || ld < 16 * order_no ||
1060 1
		!memIsValid(hash, hash_len))
1061
	{
1062 0
		_dstuCloseEc(ec);
1063 0
		return ERR_BAD_INPUT;
1064
	}
1065
	// раскладка состояния
1066 1
	h = objEnd(ec, word);
1067 1
	x = h + ec->f->n;
1068 1
	y = x + ec->f->n;
1069 1
	r = y + ec->f->n;
1070 1
	s = r + ec->f->n;
1071 1
	stack = s + ec->f->n;
1072
	// шаг 4: проверить params
1073
	// шаг 5: проверить pubkey
1074
	// [минимальная проверка принадлежности координат базовому полю]
1075 1
	if (!qrFrom(x, pubkey, ec->f, stack) || 
1076 1
		!qrFrom(y, pubkey + ec->f->no, ec->f, stack))
1077
	{
1078 0
		_dstuCloseEc(ec);
1079 0
		return ERR_BAD_PUBKEY;
1080
	}
1081
	// шаги 6, 7: хэширование
1082
	// шаг 8: перевести hash в элемент основного поля h
1083
	// [алгоритм из раздела 5.9 ДСТУ]
1084 1
	if (hash_len < ec->f->no)
1085
	{
1086 1
		memCopy(h, hash, hash_len);
1087 1
		memSetZero((octet*)h + hash_len, ec->f->no - hash_len);
1088
	}
1089
	else
1090
	{
1091 1
		memCopy(h, hash, ec->f->no);
1092
		// memTrimHi(h, ec->f->no, gf2Deg(ec->f));
1093 1
		((octet*)h)[ec->f->no - 1] &= (1 << gf2Deg(ec->f) % 8) - 1;
1094
	}
1095 1
	qrFrom(h, (octet*)h, ec->f, stack);
1096
	// шаг 8: если h = 0, то h <- 1
1097 1
	if (qrIsZero(h, ec->f))
1098 0
		qrSetUnity(h, ec->f);
1099
	// шаг 9: выделить части подписи
1100 1
	wwFrom(r, sig, order_no);
1101 1
	wwFrom(s, sig + ld / 16, order_no);
1102 1
	for (i = order_no; i < ld / 16; ++i)
1103 1
		if (sig[i] || sig[i + ld / 16])
1104
		{
1105 0
			_dstuCloseEc(ec);
1106 0
			return ERR_BAD_SIG;
1107
		}
1108
	// шаги 10, 11: проверить r и s
1109 1
	if (wwIsZero(r, order_n) ||
1110 1
		wwIsZero(s, order_n) ||
1111 1
		wwCmp(r, ec->order, order_n) >= 0 ||
1112 1
		wwCmp(s, ec->order, order_n) >= 0)
1113
	{
1114 0
		_dstuCloseEc(ec);
1115 0
		return ERR_BAD_SIG;
1116
	}
1117
	// шаг 12: R <- sP + rQ
1118 1
	if (!ecAddMulA(x, ec, stack, 2, ec->base, s, order_n, x, r, order_n))
1119
	{
1120 0
		_dstuCloseEc(ec);
1121 0
		return ERR_BAD_SIG;
1122
	}
1123
	// шаг 13: y <- h * x
1124 1
	qrMul(y, x, h, ec->f, stack);
1125
	// шаг 14: r' <- \bar{y}
1126 1
	ASSERT(order_n <= ec->f->n);
1127 1
	qrTo((octet*)s, y, ec->f, stack);
1128 1
	wwFrom(s, s, order_no);
1129 1
	wwTrimHi(s, order_n, order_nb - 1);
1130
	// шаг 15:
1131 1
	if (!wwEq(r, s, order_n))
1132 1
		code = ERR_BAD_SIG;
1133
	// все нормально
1134 1
	_dstuCloseEc(ec);
1135 1
	return code;
1136
}

Read our documentation on viewing source code .

Loading