1 // Written in the D programming language. 2 3 /** 4 * Utility functions and unittests. 5 */ 6 7 module wrapper.sodium.utils; 8 9 import wrapper.sodium.core; // assure sodium got initialized 10 import std.exception : assertThrown, assertNotThrown; 11 import nogc.exception: enforce; 12 13 public 14 import deimos.sodium.utils : 15 // sodium_memzero, 16 sodium_stackzero, 17 /* sodium_memcmp, 18 sodium_compare, 19 sodium_is_zero, 20 sodium_increment, 21 sodium_add, 22 sodium_sub, 23 sodium_bin2hex, 24 sodium_hex2bin, */ 25 sodium_base64_VARIANT_ORIGINAL, 26 sodium_base64_VARIANT_ORIGINAL_NO_PADDING, 27 sodium_base64_VARIANT_URLSAFE, 28 sodium_base64_VARIANT_URLSAFE_NO_PADDING, 29 sodium_base64_ENCODED_LEN, 30 sodium_base64_encoded_len, 31 // sodium_bin2base64, 32 // sodium_base642bin, 33 sodium_mlock, 34 sodium_munlock, 35 sodium_malloc, 36 sodium_allocarray, 37 sodium_free, 38 sodium_mprotect_noaccess, 39 sodium_mprotect_readonly, 40 sodium_mprotect_readwrite; 41 // sodium_pad, 42 // sodium_unpad; 43 44 45 /* overloading some functions between module deimos.sodium.utils and this module, "overload-set" */ 46 47 48 /** Zeroing memory. 49 * 50 * After use, sensitive data should be overwritten, but memset() and hand-written code can be 51 * silently stripped out by an optimizing compiler or by the linker. 52 * The sodium_memzero() function tries to effectively zero `len` bytes starting at `pnt`, even if 53 * optimizations are being applied to the code. 54 * See_Also: https://download.libsodium.org/doc/memory_management#zeroing-memory 55 */ 56 alias sodium_memzero = deimos.sodium.utils.sodium_memzero; 57 58 /** Zeroing memory. 59 * 60 * After use, sensitive data should be overwritten, but memset() and hand-written code can be 61 * silently stripped out by an optimizing compiler or by the linker. 62 * The sodium_memzero() function tries to effectively zero the bytes of array `a`, even if 63 * optimizations are being applied to the code. 64 * See_Also: https://download.libsodium.org/doc/memory_management#zeroing-memory 65 */ 66 pragma(inline, true) 67 void sodium_memzero(scope ubyte[] a) @nogc nothrow pure @trusted 68 { 69 // enforce(a !is null, "a is null"); // not necessary (tested on Linux and Windows) 70 sodium_memzero(a.ptr, a.length); 71 } 72 73 /** Constant-time test for equality 74 * 75 * WARNING: sodium_memcmp() must be used to verify if two secret keys 76 * are equal, in constant time. 77 * This function is not designed for lexicographical comparisons. 78 * Returns: 0 if the keys are equal, and -1 if they differ. 79 * See_Also: https://download.libsodium.org/doc/helpers#constant-time-test-for-equality 80 */ 81 alias sodium_memcmp = deimos.sodium.utils.sodium_memcmp; 82 83 /** Constant-time test for equality 84 * 85 * WARNING: sodium_memcmp() must be used to verify if two secret keys 86 * are equal, in constant time (if array's length are equal). 87 * If array's length are NOT equal, the decision is solely based on that, 88 * i.e. no constant time guarantee and the function returns false. 89 * This function is not designed for lexicographical comparisons. 90 * Preferably use this function with equal length arrays. 91 * Returns: true if the keys are equal, and false if they differ. 92 * See_Also: https://download.libsodium.org/doc/helpers#constant-time-test-for-equality 93 */ 94 pragma(inline, true) 95 bool sodium_memcmp(scope const ubyte[] b1_, scope const ubyte[] b2_) @nogc nothrow pure @trusted 96 { 97 if (b1_.length != b2_.length) 98 return false; 99 return sodium_memcmp(b1_.ptr, b2_.ptr, b1_.length) == 0; // __attribute__ ((warn_unused_result)) 100 } 101 102 /** Comparing large numbers. 103 * 104 * It is suitable for lexicographical comparisons, or to compare nonces 105 * and counters stored in little-endian format. 106 * However, it is slower than sodium_memcmp(). 107 * The comparison is done in constant time for a given length. 108 * Returns: -1 if b1_ < b2_, 1 if b1_ > b2_ and 0 if b1_ == b2_ 109 * See_Also: https://download.libsodium.org/doc/helpers#comparing-large-numbers 110 */ 111 alias sodium_compare = deimos.sodium.utils.sodium_compare; 112 113 /** Comparing large numbers. 114 * 115 * It is suitable for lexicographical comparisons, or to compare nonces 116 * and counters stored in little-endian format. 117 * However, it is slower than sodium_memcmp(). 118 * The comparison is done in constant time for a given length. 119 * The two numbers don't need to have the same length in bytes. 120 * If array's length are NOT equal, the decision may be based on that, i.e. no constant time guarantee. 121 * Preferably use this function with equal length arrays. 122 * Returns: -1 if b1_ < b2_, 1 if b1_ > b2_ and 0 if b1_ == b2_ 123 * See_Also: https://download.libsodium.org/doc/helpers#comparing-large-numbers 124 */ 125 pragma(inline, true) 126 int sodium_compare(scope const ubyte[] b1_, scope const ubyte[] b2_) @nogc nothrow pure @trusted 127 { 128 import std.algorithm.comparison: min; 129 if (b1_.length < b2_.length && !sodium_is_zero(b2_[b1_.length..$])) 130 return -1; 131 else if (b1_.length > b2_.length && !sodium_is_zero(b1_[b2_.length..$])) 132 return 1; 133 return sodium_compare(b1_.ptr, b2_.ptr, min(b1_.length, b2_.length)); // __attribute__ ((warn_unused_result)); 134 } 135 136 /** Testing for all zeros. 137 * 138 * It's execution time is constant for a given length. 139 * Returns: 1 if the `nlen` bytes vector pointed by `n` contains only zeros. 140 * 0 if non-zero bits are found. 141 * See_Also: https://download.libsodium.org/doc/helpers#testing-for-all-zeros 142 */ 143 alias sodium_is_zero = deimos.sodium.utils.sodium_is_zero; 144 145 /** Testing for all zeros. 146 * 147 * It's execution time is constant for a given length. 148 * Returns: `ţrue` if array `a` contains only zeros. 149 * `false` if non-zero bits are found. 150 * See_Also: https://download.libsodium.org/doc/helpers#testing-for-all-zeros 151 */ 152 pragma(inline, true) 153 bool sodium_is_zero(const ubyte[] a) @nogc nothrow pure @trusted 154 { 155 // enforce(a !is null, "a is null"); // not necessary 156 return sodium_is_zero(a.ptr, a.length) == 1; 157 } 158 159 /// See_Also: https://download.libsodium.org/doc/helpers#incrementing-large-numbers 160 alias sodium_increment = deimos.sodium.utils.sodium_increment; 161 162 /** Incrementing large numbers. 163 * The sodium_increment() function takes an ubyte array representing an arbitrary-long unsigned number, 164 * and increments it. 165 * It runs in constant-time for a given length, and considers the number to be encoded in little- 166 * endian format. 167 * sodium_increment() can be used to increment nonces in constant time. 168 * This function was introduced in libsodium 1.0.4. 169 * Does nothing if the array is null 170 * See_Also: https://download.libsodium.org/doc/helpers#incrementing-large-numbers 171 */ 172 pragma(inline, true) 173 void sodium_increment(ubyte[] n) @nogc nothrow pure @trusted 174 { 175 // enforce(n !is null, "n is null"); // not necessary 176 sodium_increment(n.ptr, n.length); 177 } 178 179 /// See_Also: https://download.libsodium.org/doc/helpers#adding-large-numbers 180 alias sodium_add = deimos.sodium.utils.sodium_add; 181 182 /** Adding large numbers 183 * 184 * The sodium_add() function accepts two arrays of unsigned numbers encoded in little- 185 * endian format, a and b, both of size len bytes. 186 * It computes (a + b) mod 2^(8*len) in constant time for a given length, and overwrites a 187 * with the result. 188 * History: This function was introduced in libsodium 1.0.7. 189 * Throws: NoGcException, if a_.length != b.length 190 * See_Also: https://download.libsodium.org/doc/helpers#adding-large-numbers 191 */ 192 pragma(inline, true) 193 void sodium_add(scope ubyte[] a, scope const ubyte[] b) @nogc /*nothrow pure*/ @trusted 194 { 195 enforce(a.length == b.length, "a.length doesn't equal b.length"); 196 // enforce(a !is null, "a and b are null"); // not necessary 197 sodium_add(a.ptr, b.ptr, a.length); 198 } 199 200 version(bin_v1_0_16) {} 201 else { 202 /// See_Also: https://download.libsodium.org/doc/helpers#substracting-large-numbers 203 alias sodium_sub = deimos.sodium.utils.sodium_sub; 204 205 /** Substracting large numbers 206 * The sodium_sub() function accepts two arrays of unsigned numbers encoded in little- 207 * endian format, a and b, both of size len bytes. 208 * It computes (a - b) mod 2^(8*len) in constant time for a given length, and overwrites a 209 * with the result. 210 * History: This function was introduced in libsodium 1.0.17. 211 * Throws: NoGcException, if a_.length != b.length 212 * See_Also: https://download.libsodium.org/doc/helpers#substracting-large-numbers 213 */ 214 pragma(inline, true) 215 void sodium_sub(scope ubyte[] a, scope const ubyte[] b) @nogc /*nothrow pure*/ @trusted 216 { 217 enforce(a.length == b.length, "a.length doesn't equal b.length"); 218 // enforce(a !is null, "a and b are null"); // not necessary 219 sodium_sub(a.ptr, b.ptr, a.length); 220 } 221 } // version(> bin_v1_0_16) 222 223 /// Returns: char* hex 224 /// See_Also: https://download.libsodium.org/doc/helpers#hexadecimal-encoding-decoding 225 alias sodium_bin2hex = deimos.sodium.utils.sodium_bin2hex; 226 227 /** Hexadecimal encoding. 228 * The sodium_bin2hex() function converts the bytes stored at bin into a hexadecimal string. 229 * 230 * It evaluates in constant time for a given size. 231 * hex will receive a terminating null character 232 * Throws: NoGcException, if hex.length != 2*bin.length+1 or bin.length >= size_t.max/2 233 * See_Also: https://download.libsodium.org/doc/helpers#hexadecimal-encoding-decoding 234 */ 235 pragma(inline, true) 236 void sodium_bin2hex(scope char[] hex, scope const ubyte[] bin) @nogc /*nothrow pure*/ @trusted 237 { 238 enforce(bin.length < size_t.max/2); 239 // enforce(hex.length == 2*bin.length+1, "Expected hex.length: ", hex.length, " to be equal to 2*bin.length+1: ", 2*bin.length+1); 240 enforce(hex.length == 2*bin.length+1, "Expected hex.length is not equal to 2*bin.length+1"); 241 sodium_bin2hex(hex.ptr, hex.length, bin.ptr, bin.length); // __attribute__ ((nonnull(1))); 242 } 243 244 /// Returns: 0 on success, -1 otherwise 245 /// See_Also: https://download.libsodium.org/doc/helpers#hexadecimal-encoding-decoding 246 alias sodium_hex2bin = deimos.sodium.utils.sodium_hex2bin; 247 248 /** Hexadecimal decoding. 249 * The sodium_hex2bin() function parses a hexadecimal string hex and converts it to a byte sequence bin. 250 * ignore_nullterminated is a string of characters to skip. 251 * ignore_nullterminated's last character MUST be '\0' in order to save allocating a new C string and thus be @nogc 252 * For example, the string ": \0" allows colons and 253 * spaces to be present at any locations in the hexadecimal string. These characters will just 254 * be ignored. As a result, "69:FC", "69 FC", "69 : FC" and "69FC" will be valid inputs, 255 * and will produce the same output. 256 * ignore_nullterminated can be set to null in order to disallow any non-hexadecimal character. 257 * bin.length is the maximum number of bytes to put into bin, thus bin has to be sized 258 * appropriately in advance as >= hex.length/2, otherwise the function throws. 259 * The parser stops when a non-hexadecimal, non-ignored character is found or when 260 * bin.length bytes have been written. 261 * bin_len is the number of bytes that actually got written into bin. 262 * @returns always true (success) and sets `pos_hex_non_parsed` to the position 263 * within `hex` following the last parsed character. 264 * It evaluates in constant time for a given length and format. 265 * Returns: true on success, false otherwise 266 * Throws: NoGcException, if bin.length < hex.length/2 267 * See_Also: https://download.libsodium.org/doc/helpers#hexadecimal-encoding-decoding 268 */ 269 bool sodium_hex2bin(scope ubyte[] bin, const char[] hex, const string ignore_nullterminated, 270 out size_t bin_len, out size_t pos_hex_non_parsed) @nogc /*nothrow pure*/ @trusted 271 { 272 import std.algorithm.comparison : clamp; 273 enforce(bin.length >= hex.length/2); 274 const(char)* hex_non_parsed_ptr; 275 bool result; 276 result = sodium_hex2bin(bin.ptr, bin.length, hex.ptr, hex.length, ignore_nullterminated.ptr, &bin_len, &hex_non_parsed_ptr) == 0; 277 pos_hex_non_parsed = cast(size_t) clamp(hex_non_parsed_ptr - hex.ptr, ptrdiff_t(0), ptrdiff_t(hex.length)); // __attribute__ ((nonnull(1))); 278 return result; 279 } 280 281 /// Returns: char* b64 282 /// See_Also: https://download.libsodium.org/doc/helpers#base64-encoding-decoding 283 alias sodium_bin2base64 = deimos.sodium.utils.sodium_bin2base64; 284 285 /// Throws: NoGcException, if b64.length < sodium_base64_encoded_len(bin.length, variant) 286 /// See_Also: https://download.libsodium.org/doc/helpers#base64-encoding-decoding 287 pragma(inline, true) 288 void sodium_bin2base64(scope char[] b64, scope const ubyte[] bin, const int variant) @nogc /*nothrow pure*/ @trusted 289 { 290 size_t min_len = sodium_base64_encoded_len(bin.length, variant); 291 enforce(b64.length >= min_len); 292 sodium_bin2base64(b64.ptr, b64.length, bin.ptr, bin.length, variant); // __attribute__ ((nonnull(1))); 293 } 294 295 /// Returns: 0 on success, -1 otherwise 296 /// See_Also: https://download.libsodium.org/doc/helpers#base64-encoding-decoding 297 alias sodium_base642bin = deimos.sodium.utils.sodium_base642bin; 298 299 /// Returns: true on success, false otherwise 300 /// See_Also: https://download.libsodium.org/doc/helpers#base64-encoding-decoding 301 bool sodium_base642bin(scope ubyte[] bin, const char[] b64, const string ignore_nullterminated, 302 out size_t bin_len, out size_t pos_b64_non_parsed, const int variant) /*@nogc nothrow pure*/ @trusted 303 { 304 import std.algorithm.comparison : clamp; 305 import std.algorithm.searching: countUntil; // not @nogc 306 import std.range: retro; 307 const(char)* b64_non_parsed_ptr; 308 bool result; 309 enforce(bin.length >= (b64.length-1)/4*3 -countUntil!"a!=b"(b64[0..$-1].retro, '=')); 310 result = sodium_base642bin(&bin[0], bin.length, b64.ptr, b64.length, ignore_nullterminated.ptr, &bin_len, &b64_non_parsed_ptr, variant) == 0; 311 pos_b64_non_parsed = cast(size_t) clamp(b64_non_parsed_ptr - b64.ptr, ptrdiff_t(0), ptrdiff_t(b64.length)); // __attribute__ ((nonnull(1))); 312 return result; 313 } 314 315 /// Returns: 0 on success, -1 otherwise 316 /// See_Also: https://download.libsodium.org/doc/padding#usage 317 alias sodium_pad = deimos.sodium.utils.sodium_pad; 318 319 /// Returns: true on success, false otherwise 320 /// See_Also: https://download.libsodium.org/doc/padding#usage 321 pragma(inline, true) 322 bool sodium_pad(out size_t padded_buflen, scope ubyte[] buf, 323 size_t unpadded_buflen, size_t blocksize) @nogc nothrow pure @trusted 324 { 325 return sodium_pad(&padded_buflen, buf.ptr, unpadded_buflen, blocksize, buf.length) == 0; 326 } 327 328 329 /// Returns: 0 on success, -1 otherwise 330 /// See_Also: https://download.libsodium.org/doc/padding#usage 331 alias sodium_unpad = deimos.sodium.utils.sodium_unpad; 332 333 /// Returns: true on success, false otherwise 334 /// See_Also: https://download.libsodium.org/doc/padding#usage 335 pragma(inline, true) 336 bool sodium_unpad(out size_t unpadded_buflen, scope ubyte[] buf, 337 size_t padded_buflen, size_t blocksize) @nogc nothrow pure @trusted 338 { 339 return sodium_unpad(&unpadded_buflen, buf.ptr, padded_buflen, blocksize) == 0; 340 } 341 342 343 @safe 344 unittest 345 { 346 } 347 348 pure @system 349 unittest 350 { 351 debug { 352 import std.stdio : writeln; 353 writeln("unittest block 1 from sodium.utils.d"); 354 } 355 assert(sodium_base64_ENCODED_LEN(19, sodium_base64_VARIANT_ORIGINAL) == sodium_base64_encoded_len(19, sodium_base64_VARIANT_ORIGINAL)); 356 assert(sodium_base64_ENCODED_LEN(19, sodium_base64_VARIANT_ORIGINAL_NO_PADDING) == sodium_base64_encoded_len(19, sodium_base64_VARIANT_ORIGINAL_NO_PADDING)); 357 assert(sodium_base64_ENCODED_LEN(19, sodium_base64_VARIANT_URLSAFE) == sodium_base64_encoded_len(19, sodium_base64_VARIANT_URLSAFE)); 358 assert(sodium_base64_ENCODED_LEN(19, sodium_base64_VARIANT_URLSAFE_NO_PADDING) == sodium_base64_encoded_len(19, sodium_base64_VARIANT_URLSAFE_NO_PADDING)); 359 //sodium_memzero 360 //sodium_stackzero 361 //sodium_is_zero 362 import std.algorithm.searching : any; 363 import std.range : iota, array; 364 365 int[8] a = [1,2,3,4,5,6,7,8]; // allocate on the stack 366 sodium_memzero(a.ptr, a.length*int.sizeof); 367 368 assert(!any(a[])); // none of a[] evaluate to true 369 // assert( all!"a == 0"(a[])); 370 // assert(all!(x => x == 0)(a[])); 371 sodium_memzero(null, 0); 372 sodium_stackzero(8); // TODO check this later 373 374 int[] b = array(iota(99)); // allocate on the heap 375 assert(sodium_is_zero(cast(ubyte*)b.ptr, b.length*int.sizeof) == 0); 376 sodium_memzero(b.ptr, b.length*int.sizeof); 377 assert(!any(b)); 378 assert(sodium_is_zero(cast(ubyte*)b.ptr, b.length*int.sizeof) == 1); 379 assert(sodium_is_zero(null, 0) == 1); 380 381 //sodium_memcmp 382 a = [1,2,3,4,5,6,7,8]; 383 b = a.dup; 384 assert(sodium_memcmp(a.ptr, b.ptr, a.length*int.sizeof) == 0, "sodium_memcmp error"); 385 b[7] = 255; 386 assert(sodium_memcmp(a.ptr, b.ptr, a.length*int.sizeof) == -1, "sodium_memcmp error"); 387 cast(void) sodium_memcmp(null, null, 0); 388 389 //sodium_compare 390 //sodium_increment 391 //sodium_add 392 393 // LSB MSB 394 ubyte[] c = [254,2,3,4,254]; 395 ubyte[] d = c.dup; 396 d[0] = 253; 397 d[4] = 255; 398 assert(sodium_compare(c.ptr, d.ptr, c.length) == -1); 399 d[0] = d[4] = 254; 400 assert(sodium_compare(c.ptr, d.ptr, c.length) == 0); 401 c[0] = 253; 402 c[4] = 255; 403 assert(sodium_compare(c.ptr, d.ptr, c.length) == 1); 404 assert(sodium_compare(null, null, 0) == 0); 405 406 union anonymous { 407 ulong a = 0x40_00_00_00_00_00_00_00UL; 408 ubyte[8] b; 409 } 410 anonymous u, v, w; 411 sodium_increment(u.b.ptr, u.b.length); 412 assert(u.a == 0x40_00_00_00_00_00_00_01UL); 413 414 sodium_add(u.b.ptr, v.b.ptr, u.b.length); 415 assert(u.a == 0x80_00_00_00_00_00_00_01UL); 416 sodium_add(null, null, 0); 417 418 ubyte[100] buf; 419 size_t buf_unpadded_len = 10; 420 size_t buf_padded_len; 421 size_t block_size = 16; 422 /* round the length of the buffer to a multiple of `block_size` by appending 423 * padding data and put the new, total length into `buf_padded_len` */ 424 assert(sodium_pad(&buf_padded_len, buf.ptr, buf_unpadded_len, block_size, buf.sizeof) == 0); 425 assert(buf_padded_len == 16); // otherwise /* overflow! buf[] is not large enough */ 426 buf_unpadded_len = 0; 427 /* compute the original, unpadded length */ 428 assert(sodium_unpad(&buf_unpadded_len, buf.ptr, buf_padded_len, block_size) == 0); 429 assert(buf_unpadded_len == 10); // otherwise /* incorrect padding */ 430 } 431 432 /*pure*/ @safe 433 unittest // same as before except @safe and wrapping delegates + overloads 434 { 435 debug { 436 import std.stdio : writeln; 437 writeln("unittest block 2 from sodium.utils.d"); 438 } 439 //sodium_memzero 440 //sodium_is_zero 441 import std.algorithm.searching : any; 442 import std.range : iota, array; 443 444 int[8] a = [1,2,3,4,5,6,7,8]; // allocate on the stack 445 (() @trusted => sodium_memzero(a.ptr, a.length*int.sizeof))(); 446 assert(!any(a[])); 447 448 int[] b = array(iota(99)); // allocate on the heap 449 assert((() @trusted => sodium_is_zero(cast(ubyte*)b.ptr, b.length*int.sizeof))() == 0); 450 (() @trusted => sodium_memzero(b.ptr, b.length*int.sizeof))(); 451 assert(!any(b)); 452 assert((() @trusted => sodium_is_zero(cast(ubyte*)b.ptr, b.length*int.sizeof))() == 1); 453 454 //sodium_memcmp 455 a = [1,2,3,4,5,6,7,8]; 456 b = a.dup; 457 assert((() @trusted => sodium_memcmp(a.ptr, b.ptr, a.length*int.sizeof))() == 0, "sodium_memcmp error"); 458 b[7] = 255; 459 assert((() @trusted => sodium_memcmp(a.ptr, b.ptr, a.length*int.sizeof))() == -1, "sodium_memcmp error"); 460 461 //sodium_compare (both functions) 462 //sodium_increment (both functions) 463 //sodium_add (both functions) 464 //sodium_is_zero (overload) 465 466 { 467 ulong c, d; 468 c = ulong.max-1; 469 d = ulong.max; 470 /* 471 doing several "dangerous" actions here in a controlled way: 472 The following operations are not allowed in safe functions: 473 474 No casting from a pointer type to any type other than void*. 475 Calling any system functions. 476 No taking the address of a local variable or function parameter. 477 */ 478 assert( (() @trusted => sodium_compare(cast(ubyte*)&c, cast(ubyte*)&d, ulong.sizeof))() == -1); 479 --d; 480 assert( (() @trusted => sodium_compare(cast(ubyte*)&c, cast(ubyte*)&d, ulong.sizeof))() == 0); 481 ++c; 482 assert( (() @trusted => sodium_compare(cast(ubyte*)&c, cast(ubyte*)&d, ulong.sizeof))() == 1); 483 } 484 { // testing the overload 485 // LSB MSB 486 ubyte[] c = [254,2,3,4,254]; 487 ubyte[] d = c.dup; 488 assert(!sodium_is_zero(c)); 489 d[0] = 253; 490 d[4] = 255; 491 assert(sodium_compare(c, d) == -1); 492 assert(!sodium_memcmp(c, d)); 493 494 d[0] = d[4] = 254; 495 assert(sodium_compare(c, d) == 0); 496 assert( sodium_memcmp(c, d)); 497 c[0] = 253; 498 c[4] = 255; 499 assert(sodium_compare(c, d) == 1); 500 // checking unequal length 501 d ~= 0; 502 assert(sodium_compare(c, d) == 1); 503 c = [254,2,3,4,254]; 504 d = [254,2,3,4,254, 0]; 505 assert(sodium_compare(c, d) == 0); 506 c ~= 0; 507 d.length = 5; 508 d[0] = 253; 509 d[4] = 255; 510 assert(sodium_compare(c, d) == -1); 511 c[5] = 1; 512 assert(sodium_compare(c, d) == 1); // if (b1_.length > b2_.length && !sodium_is_zero(b1_[b2_.length..$])) return 1; 513 d = c.dup; 514 d ~= 1; 515 assert(sodium_compare(c, d) == -1); // if (b1_.length < b2_.length && !sodium_is_zero(b2_[b1_.length..$])) return -1; 516 assert(!sodium_memcmp(c, d)); 517 518 assert(sodium_compare(null, null) == 0); 519 // ubyte[] dummy = [1]; 520 // assertThrown(enforce( sodium_compare(null, dummy) == -1, new Exception("this should be thrown"))); 521 assert(sodium_is_zero(null)); 522 523 sodium_memzero(c); 524 assert(sodium_is_zero(c)); 525 sodium_memzero(null); 526 } 527 528 union anonymous { 529 ulong a = 0x40_00_00_00_00_00_00_00UL; 530 ubyte[8] b; 531 } 532 anonymous u, v; 533 (() @trusted => sodium_increment(u.b.ptr, u.b.length))(); 534 assert(u.a == 0x40_00_00_00_00_00_00_01UL); 535 536 (() @trusted => sodium_add(u.b.ptr, v.b.ptr, u.b.length))(); 537 assert(u.a == 0x80_00_00_00_00_00_00_01UL); 538 539 u.a = v.a = 0x40_00_00_00_00_00_00_00UL; 540 sodium_increment(u.b); 541 assert(u.a == 0x40_00_00_00_00_00_00_01UL); 542 sodium_increment(null); 543 544 sodium_add(u.b, v.b); 545 assert(u.a == 0x80_00_00_00_00_00_00_01UL); 546 ubyte[] dummy; // dummy is null 547 sodium_add(dummy, null); 548 assert(dummy is null); 549 550 version(bin_v1_0_16) {} 551 else { 552 sodium_sub(u.b, v.b); 553 assert(u.a == 0x40_00_00_00_00_00_00_01UL); 554 } 555 556 ubyte[100] buf; 557 size_t buf_unpadded_len = 10; 558 size_t buf_padded_len; 559 size_t block_size = 16; 560 /* round the length of the buffer to a multiple of `block_size` by appending 561 * padding data and put the new, total length into `buf_padded_len` */ 562 assert(sodium_pad(buf_padded_len, buf, buf_unpadded_len, block_size)); 563 assert(buf_padded_len == 16); // otherwise /* overflow! buf[] is not large enough */ 564 buf_unpadded_len = 0; 565 /* compute the original, unpadded length */ 566 assert(sodium_unpad(buf_unpadded_len, buf, buf_padded_len, block_size)); 567 assert(buf_unpadded_len == 10); // otherwise /* incorrect padding */ 568 } 569 570 pure @system 571 unittest 572 { 573 debug { 574 import std.stdio : writeln; 575 writeln("unittest block 3 from sodium.utils.d"); 576 } 577 import std.string : fromStringz; 578 import std.conv : hexString; 579 580 //sodium_bin2hex 581 ubyte[] a = [1,2,3,4,255]; 582 char[] result_out = new char[](11); 583 char* result = sodium_bin2hex(result_out.ptr, result_out.length, a.ptr, a.length); 584 assert(result.fromStringz==result_out[0..$-1]); // the "strings" result.fromStringz as well as "01020304ff".dup have no terminating \0 character, but result_out has it! 585 assert("01020304ff".dup ==result_out[0..$-1]); 586 587 //sodium_hex2bin 588 auto vbuf = cast(immutable(ubyte)[]) hexString!"ac 9f ff 4e ba"; // for comparison 589 string hex = "ac:9f:ff:4e:bay"; 590 const(char)* ignore_nullterminated_ptr = ":"; 591 ubyte[] bin = new ubyte[]((hex.length+1)/3); 592 size_t bin_len; 593 const(char)* hex_end; 594 assert(sodium_hex2bin(bin.ptr, bin.length, hex.ptr, hex.length, ignore_nullterminated_ptr, &bin_len, &hex_end) == 0); 595 assert(bin == vbuf); // [172, 159, 255, 78, 186]); 596 assert(bin_len == 5); 597 assert(hex.ptr+14 == hex_end); // addresses pointing to character y; i.e. hex_end points into hex 598 assert(*hex_end == 'y'); 599 // assert(*++hex_end == '\0'); // i.e. a D string is guaranteed to be followed by a '\0' in memory ? Is this always true ??? 600 601 --hex.length; 602 hex ~= ":fa"; 603 bin[] = ubyte.init; // bin.length unchanged; to small now to incorporate all input 604 assert(sodium_hex2bin(bin.ptr, bin.length, hex.ptr, hex.length, ignore_nullterminated_ptr, &bin_len, &hex_end) == -1); 605 assert(bin == vbuf); // [172, 159, 255, 78, 186]); 606 assert(bin_len == 0); 607 assert(*hex_end == 'f'); 608 assert(*++hex_end == 'a'); 609 } 610 611 @safe 612 unittest // same as before except @safe and wrapping delegates + overloads 613 { 614 import std.stdio : writeln; 615 import std.algorithm.comparison : equal; 616 import std.conv : hexString; 617 debug writeln("unittest block 4 from sodium.utils.d"); 618 619 //sodium_bin2hex 620 ubyte[] a = [1,2,3,4,255]; 621 char[] result_out = new char[](a.length*2+1); 622 char* result = (() @trusted => sodium_bin2hex(result_out.ptr, result_out.length, a.ptr, a.length))(); 623 // assert(result.fromStringz==result_out[0..$-1]); // the "strings" result.fromStringz as well as "01020304ff".dup have no terminating \0 character, but result_out has it! 624 assert(equal("01020304ff", result_out[0..$-1])); 625 result_out[] = char.init; 626 sodium_bin2hex(result_out, a); 627 assert(equal("01020304ff", result_out[0..$-1])); 628 char[] one = [char.init]; 629 sodium_bin2hex(one, null); 630 assertThrown(sodium_bin2hex(null, null)); 631 632 //sodium_hex2bin overload only 633 auto vbuf = cast(immutable(ubyte)[]) hexString!"ac 9f ff 4e ba"; // for comparison 634 string hex = "ac:9f:ff:4e:bay"; 635 string ignore_nullterminated = ":\0"; 636 ubyte[] bin = new ubyte[](hex.length/2); //((hex.length+1)/3); 637 size_t bin_len, pos_hex_non_parsed; 638 639 assert(sodium_hex2bin(bin, hex, ignore_nullterminated, bin_len, pos_hex_non_parsed)); 640 assert(bin_len == 5); 641 assert(bin[0..bin_len] == vbuf); // [172, 159, 255, 78, 186]); 642 assert(pos_hex_non_parsed == 14); 643 assertNotThrown(sodium_hex2bin(bin, hex, null, bin_len, pos_hex_non_parsed)); 644 /* 645 --hex.length; 646 hex ~= ":fa87"; 647 // bin.length = hex.length/2; 648 bin[] = ubyte.init; 649 assert(!sodium_hex2bin(bin, hex, ignore_nullterminated, bin_len, pos_hex_non_parsed)); 650 assert(bin[0..6] == vbuf~ubyte(0xfa)); 651 assert(bin_len == 0); 652 assert(pos_hex_non_parsed == 17); 653 */ 654 } 655 656 @system 657 unittest 658 { 659 debug { 660 import std.stdio : writeln; 661 writeln("unittest block 5 from sodium.utils.d"); 662 } 663 import std.algorithm.comparison : equal; 664 //sodium_mlock 665 //sodium_munlock 666 ubyte[] a = [1,2,3,4,5,6,7,8]; 667 assert(sodium_is_zero(a.ptr, a.length) == 0); 668 { 669 assert( sodium_mlock (a.ptr, a.length) != -1); // inhibits swapping and excludes from a core dump on Linux 670 assert(equal(a, [1,2,3,4,5,6,7,8])); 671 scope(exit) sodium_munlock(a.ptr, a.length); // sodium_munlock calls sodium_memzero internally 672 } 673 assert(sodium_is_zero(a.ptr, a.length) == 1); 674 } 675 676 @safe 677 unittest // same as before except @safe and wrapping delegates + overloads 678 { 679 debug { 680 import std.stdio : writeln; 681 writeln("unittest block 6 from sodium.utils.d"); 682 } 683 import std.algorithm.comparison : equal; 684 //sodium_mlock 685 //sodium_munlock 686 ubyte[] a = [1,2,3,4,5,6,7,8]; 687 assert(!sodium_is_zero(a)); 688 { 689 assert( (() @trusted => sodium_mlock(a.ptr, a.length))() != -1); 690 assert(equal(a, [1,2,3,4,5,6,7,8])); 691 scope(exit) (() @trusted => sodium_munlock(a.ptr, a.length))(); 692 } 693 assert(sodium_is_zero(a)); 694 695 a = [65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83]; 696 size_t min_len = sodium_base64_encoded_len(a.length, sodium_base64_VARIANT_ORIGINAL); 697 char[] b64 = new char[](min_len); // 29 698 sodium_bin2base64(b64, a, sodium_base64_VARIANT_ORIGINAL); 699 // writeln(b64); // QUJDREVGR0hJSktMTU5PUFFSUw== 700 a[] = ubyte.init; 701 size_t bin_len; 702 size_t pos_b64_non_parsed; 703 assert(sodium_base642bin(a, b64, null, bin_len, pos_b64_non_parsed, sodium_base64_VARIANT_ORIGINAL)); 704 assert(bin_len == a.length); 705 assert(equal(a, [65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83])); 706 assert(pos_b64_non_parsed == b64.length-1); 707 } 708 709 @system 710 unittest 711 { 712 debug { 713 import std.stdio : writeln; 714 writeln("unittest block 7 from sodium.utils.d"); 715 } 716 import std.conv : emplace; 717 import std.algorithm.searching : all; 718 import std.algorithm.comparison : equal; 719 720 struct S { 721 int[16] a; 722 } 723 //sodium_malloc 724 //sodium_free 725 S* p = cast(S*)sodium_malloc(64); 726 scope(exit) sodium_free(p); 727 emplace!S(p, S( [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16])); 728 assert(equal(p.a[], [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16])); 729 int[] arr_int = new (p) int[16]; 730 assert( all!"a == 0"(arr_int)); 731 732 enum s_len = 2; 733 alias s_type = S[s_len]; 734 //sodium_allocarray 735 s_type* p2 = cast(s_type*)sodium_allocarray(s_len, 64); 736 scope(exit) sodium_free(p2); 737 emplace!s_type(p2, S( [17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32])); 738 assert( (*p2)[0].a == [17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32]); 739 assert( (*p2)[1].a == [17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32]); 740 741 ubyte* p3 = cast(ubyte*)sodium_malloc(64); 742 scope(exit) sodium_free(p3); 743 ubyte[] arr_ubyte = p3[0..64]; // may be ubyte[] as well, but may not be increased, to still live in sodium_malloc'ed chunk! 744 // writefln("arr_ubyte = p3[0..64] : [%(%02X %)]", p3[0..64]); 745 //assert( all!"a == 0xD0"(arr_ubyte)); in version 1.0.11 746 assert( all!"a == 0xDB"(arr_ubyte)); //in version 1.0.12 747 assert( arr_ubyte.capacity == 0); 748 749 p.a = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]; 750 //sodium_mprotect_noaccess 751 sodium_mprotect_noaccess(p); 752 //assert(equal(p.a[], [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16])); //unrecoverable, just crashes ! 753 754 //sodium_mprotect_readonly 755 sodium_mprotect_readonly(p); 756 assert(equal(p.a[], [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16])); 757 //p.a[0] = 0; //unrecoverable, just crashes ! 758 759 //sodium_mprotect_readwrite 760 sodium_mprotect_readwrite(p); 761 p.a[0] = 0; 762 assert(p.a[0] == 0); 763 } 764 765 @nogc @safe 766 unittest 767 { 768 import std.string : fromStringz; // is @system 769 ubyte[8] a, b; 770 sodium_memzero(a); 771 assert(sodium_is_zero(a)); 772 assert(sodium_memcmp(a, b)); 773 assert(sodium_compare(a, b) == 0); 774 sodium_increment(a); 775 sodium_add(a, b); 776 char[17] hex; 777 sodium_bin2hex(hex, a); 778 size_t bin_len, pos_hex_non_parsed; 779 assert(sodium_hex2bin(b, (() @trusted => fromStringz(hex.ptr))(), null, bin_len, pos_hex_non_parsed)); 780 assert(bin_len == 8); 781 assert(pos_hex_non_parsed == 16); 782 }