23 | /// Permission is hereby granted, free of charge, to any person obtaining a copy of this software 24 | /// and associated documentation files (the "Software"), to deal in the Software without restriction, 25 | /// including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 26 | /// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 27 | /// subject to the following conditions: 28 | ///
29 | /// The above copyright notice and this permission notice shall be included in all copies or substantial 30 | /// portions of the Software. 31 | ///
32 | /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 33 | /// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 34 | /// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 35 | /// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 36 | /// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 37 | /// DEALINGS IN THE SOFTWARE. 38 | /// ``` 39 | /// 40 | class Argon2BytesGenerator { 41 | static const int ARGON2_BLOCK_SIZE = 1024; 42 | static const int ARGON2_QWORDS_IN_BLOCK = ARGON2_BLOCK_SIZE ~/ 8; 43 | 44 | static const int ARGON2_ADDRESSES_IN_BLOCK = 128; 45 | 46 | static const int ARGON2_PREHASH_DIGEST_LENGTH = 64; 47 | static const int ARGON2_PREHASH_SEED_LENGTH = 72; 48 | 49 | static const int ARGON2_SYNC_POINTS = 4; 50 | 51 | /// Minimum and maximum number of lanes (degree of parallelism). 52 | static const int MIN_PARALLELISM = 1; 53 | static const int MAX_PARALLELISM = 16777216; 54 | 55 | /// Minimum and maximum digest size in bytes. 56 | static const int MIN_OUTLEN = 4; 57 | 58 | /// Minimum and maximum number of passes. 59 | static const int MIN_ITERATIONS = 1; 60 | 61 | static const int M32L = 0xFFFFFFFFFFFFFFFF; 62 | 63 | static final Uint8List _ZERO_BYTES = Uint8List(4); 64 | 65 | late Argon2Parameters _parameters; 66 | late List<_Block> _memory; 67 | late int _segmentLength; 68 | late int _laneLength; 69 | 70 | Argon2BytesGenerator(); 71 | 72 | Argon2Parameters get parameters => _parameters; 73 | 74 | /// Initialise the Argon2BytesGenerator from the parameters. 75 | /// - [param] parameters Argon2 configuration. 76 | void init(Argon2Parameters parameters) { 77 | _parameters = parameters; 78 | 79 | if (parameters.lanes < Argon2BytesGenerator.MIN_PARALLELISM) { 80 | throw ArgumentError.value(parameters.lanes, 'parameters.lanes', 81 | 'lanes must be greater than ${Argon2BytesGenerator.MIN_PARALLELISM}'); 82 | } else if (parameters.lanes > Argon2BytesGenerator.MAX_PARALLELISM) { 83 | throw ArgumentError.value(parameters.lanes, 'parameters.lanes', 84 | 'lanes must be less than ${Argon2BytesGenerator.MAX_PARALLELISM}'); 85 | } else if (parameters.memory < 2 * parameters.lanes) { 86 | throw ArgumentError.value(parameters.memory, 'parameters.memory', 87 | 'memory is less than: ${(2 * parameters.lanes)} expected ${(2 * parameters.lanes)}'); 88 | } else if (parameters.iterations < Argon2BytesGenerator.MIN_ITERATIONS) { 89 | throw ArgumentError.value(parameters.iterations, 'parameters.iterations', 90 | 'iterations is less than: ${Argon2BytesGenerator.MIN_ITERATIONS}'); 91 | } 92 | 93 | _doInit(parameters); 94 | } 95 | 96 | int generateBytesFromString(String password, Uint8List out, 97 | [int outOff = 0, int? outLen]) => 98 | generateBytes( 99 | _parameters.converter.convert(password), out, outOff, outLen); 100 | 101 | int generateBytes(Uint8List password, Uint8List out, 102 | [int outOff = 0, int? outLen]) { 103 | outLen ??= out.length; 104 | 105 | if (outLen < Argon2BytesGenerator.MIN_OUTLEN) { 106 | throw ArgumentError.value(outLen, 'outLen', 107 | 'output length less than ${Argon2BytesGenerator.MIN_OUTLEN}'); 108 | } 109 | 110 | var tmpBlockBytes = Uint8List(ARGON2_BLOCK_SIZE); 111 | 112 | _initialize(tmpBlockBytes, password, outLen); 113 | _fillMemoryBlocks(); 114 | _digest(tmpBlockBytes, out, outOff, outLen); 115 | 116 | _reset(); 117 | 118 | return outLen; 119 | } 120 | 121 | /// Clear memory. 122 | void _reset() { 123 | for (var i = _memory.length - 1; i >= 0; --i) { 124 | var b = _memory[i]; 125 | b.clear(); 126 | } 127 | } 128 | 129 | void _doInit(Argon2Parameters parameters) { 130 | /* 2. Align memory size */ 131 | /* Minimum memoryBlocks = 8L blocks, where L is the number of lanes */ 132 | var memoryBlocks = parameters.memory; 133 | 134 | if (memoryBlocks < 135 | 2 * Argon2BytesGenerator.ARGON2_SYNC_POINTS * parameters.lanes) { 136 | memoryBlocks = 137 | 2 * Argon2BytesGenerator.ARGON2_SYNC_POINTS * parameters.lanes; 138 | } 139 | 140 | _segmentLength = memoryBlocks ~/ 141 | (parameters.lanes * Argon2BytesGenerator.ARGON2_SYNC_POINTS); 142 | _laneLength = _segmentLength * Argon2BytesGenerator.ARGON2_SYNC_POINTS; 143 | 144 | /* Ensure that all segments have equal length */ 145 | memoryBlocks = _segmentLength * 146 | (parameters.lanes * Argon2BytesGenerator.ARGON2_SYNC_POINTS); 147 | 148 | _initMemory(memoryBlocks); 149 | } 150 | 151 | void _initMemory(int memoryBlocks) { 152 | _memory = List<_Block>.generate(memoryBlocks, (i) => _Block()); 153 | } 154 | 155 | void _fillMemoryBlocks() { 156 | var filler = _FillBlock(); 157 | var position = _Position(); 158 | for (var pass = 0; pass < _parameters.iterations; ++pass) { 159 | position.pass = pass; 160 | 161 | for (var slice = 0; slice < ARGON2_SYNC_POINTS; ++slice) { 162 | position.slice = slice; 163 | 164 | for (var lane = 0; lane < _parameters.lanes; ++lane) { 165 | position.lane = lane; 166 | 167 | _fillSegment(filler, position); 168 | } 169 | } 170 | } 171 | } 172 | 173 | void _fillSegment(_FillBlock filler, _Position position) { 174 | _Block? addressBlock; 175 | _Block? inputBlock; 176 | 177 | var dataIndependentAddressing = _isDataIndependentAddressing(position); 178 | var startingIndex = _getStartingIndex(position); 179 | var currentOffset = position.lane * _laneLength + 180 | position.slice * _segmentLength + 181 | startingIndex; 182 | var prevOffset = _getPrevOffset(currentOffset); 183 | 184 | if (dataIndependentAddressing) { 185 | addressBlock = filler.addressBlock.clear(); 186 | inputBlock = filler.inputBlock.clear(); 187 | 188 | _initAddressBlocks(filler, position, inputBlock, addressBlock); 189 | } 190 | 191 | final withXor = _isWithXor(position); 192 | 193 | for (var index = startingIndex; index < _segmentLength; ++index) { 194 | var pseudoRandom = _getPseudoRandom(filler, index, addressBlock, 195 | inputBlock, prevOffset, dataIndependentAddressing); 196 | var refLane = _getRefLane(position, pseudoRandom); 197 | var refColumn = _getRefColumn( 198 | position, index, pseudoRandom, refLane == position.lane); 199 | 200 | /* 2 Creating a new block */ 201 | var prevBlock = _memory[prevOffset]; 202 | var refBlock = _memory[((_laneLength) * refLane + refColumn)]; 203 | var currentBlock = _memory[currentOffset]; 204 | 205 | if (withXor) { 206 | filler.fillBlockWithXor(prevBlock, refBlock, currentBlock); 207 | } else { 208 | filler.fillBlock2(prevBlock, refBlock, currentBlock); 209 | } 210 | 211 | prevOffset = currentOffset; 212 | currentOffset++; 213 | } 214 | } 215 | 216 | bool _isDataIndependentAddressing(_Position position) { 217 | return (_parameters.type == Argon2Parameters.ARGON2_i) || 218 | (_parameters.type == Argon2Parameters.ARGON2_id && 219 | (position.pass == 0) && 220 | (position.slice < ARGON2_SYNC_POINTS / 2)); 221 | } 222 | 223 | void _initAddressBlocks(_FillBlock filler, _Position position, 224 | _Block inputBlock, _Block addressBlock) { 225 | inputBlock._v[0] = _intToLong(position.pass); 226 | inputBlock._v[1] = _intToLong(position.lane); 227 | inputBlock._v[2] = _intToLong(position.slice); 228 | inputBlock._v[3] = _intToLong(_memory.length); 229 | inputBlock._v[4] = _intToLong(_parameters.iterations); 230 | inputBlock._v[5] = _intToLong(_parameters.type); 231 | 232 | if ((position.pass == 0) && (position.slice == 0)) { 233 | /* Don't forget to generate the first block of addresses: */ 234 | _nextAddresses(filler, inputBlock, addressBlock); 235 | } 236 | } 237 | 238 | bool _isWithXor(_Position position) { 239 | return !(position.pass == 0 || 240 | _parameters.version == Argon2Parameters.ARGON2_VERSION_10); 241 | } 242 | 243 | int _getPrevOffset(int currentOffset) { 244 | if (currentOffset % _laneLength == 0) { 245 | /* Last block in this lane */ 246 | return currentOffset + _laneLength - 1; 247 | } else { 248 | /* Previous block */ 249 | return currentOffset - 1; 250 | } 251 | } 252 | 253 | static int _getStartingIndex(_Position position) { 254 | if ((position.pass == 0) && (position.slice == 0)) { 255 | return 2; /* we have already generated the first two blocks */ 256 | } else { 257 | return 0; 258 | } 259 | } 260 | 261 | void _nextAddresses( 262 | _FillBlock filler, _Block inputBlock, _Block addressBlock) { 263 | inputBlock._v[6]++; 264 | filler.fillBlock(inputBlock, addressBlock); 265 | filler.fillBlock(addressBlock, addressBlock); 266 | } 267 | 268 | /* 1.2 Computing the index of the reference block */ 269 | /* 1.2.1 Taking pseudo-random value from the previous block */ 270 | int _getPseudoRandom(_FillBlock filler, int index, _Block? addressBlock, 271 | _Block? inputBlock, int prevOffset, bool dataIndependentAddressing) { 272 | if (dataIndependentAddressing) { 273 | var addressIndex = index % ARGON2_ADDRESSES_IN_BLOCK; 274 | if (addressIndex == 0) { 275 | _nextAddresses(filler, inputBlock!, addressBlock!); 276 | } 277 | return addressBlock!._v[addressIndex]; 278 | } else { 279 | return _memory[prevOffset]._v[0]; 280 | } 281 | } 282 | 283 | int _getRefLane(_Position position, int pseudoRandom) { 284 | var refLane = (pseudoRandom.tripleShift64(32) % _parameters.lanes); 285 | 286 | if ((position.pass == 0) && (position.slice == 0)) { 287 | /* Can not reference other lanes yet */ 288 | refLane = position.lane; 289 | } 290 | return refLane; 291 | } 292 | 293 | int _getRefColumn( 294 | _Position position, int index, int pseudoRandom, bool sameLane) { 295 | int referenceAreaSize; 296 | int startPosition; 297 | 298 | if (position.pass == 0) { 299 | startPosition = 0; 300 | 301 | if (sameLane) { 302 | /* The same lane => add current segment */ 303 | referenceAreaSize = position.slice * _segmentLength + index - 1; 304 | } else { 305 | /* pass == 0 && !sameLane => position.slice > 0*/ 306 | referenceAreaSize = 307 | position.slice * _segmentLength + ((index == 0) ? (-1) : 0); 308 | } 309 | } else { 310 | startPosition = ((position.slice + 1) * _segmentLength) % _laneLength; 311 | 312 | if (sameLane) { 313 | referenceAreaSize = _laneLength - _segmentLength + index - 1; 314 | } else { 315 | referenceAreaSize = 316 | _laneLength - _segmentLength + ((index == 0) ? (-1) : 0); 317 | } 318 | } 319 | 320 | var relativePosition = pseudoRandom & 0xFFFFFFFF; 321 | relativePosition = (relativePosition * relativePosition).tripleShift64(32); 322 | relativePosition = referenceAreaSize - 323 | 1 - 324 | (referenceAreaSize * relativePosition).tripleShift64(32); 325 | 326 | return (startPosition + relativePosition) % _laneLength; 327 | } 328 | 329 | void _digest(Uint8List tmpBlockBytes, Uint8List out, int outOff, int outLen) { 330 | var finalBlock = _memory[_laneLength - 1]; 331 | 332 | /* XOR the last blocks */ 333 | for (var i = 1; i < _parameters.lanes; i++) { 334 | var lastBlockInLane = i * _laneLength + (_laneLength - 1); 335 | finalBlock.xorWith(_memory[lastBlockInLane]); 336 | } 337 | 338 | finalBlock.toBytes(tmpBlockBytes); 339 | 340 | _hash(tmpBlockBytes, out, outOff, outLen); 341 | } 342 | 343 | /// H' - hash - variable length hash function 344 | void _hash(Uint8List input, Uint8List out, int outOff, int outLen) { 345 | var outLenBytes = Uint8List(4); 346 | Pack.intToLittleEndianAtList(outLen, outLenBytes, 0); 347 | 348 | var blake2bLength = 64; 349 | 350 | if (outLen <= blake2bLength) { 351 | var blake = Blake2bDigest(digestSize: outLen); 352 | 353 | blake.update(outLenBytes, 0, outLenBytes.length); 354 | blake.update(input, 0, input.length); 355 | blake.doFinal(out, outOff); 356 | } else { 357 | var digest = Blake2bDigest(digestSize: blake2bLength); 358 | 359 | var outBuffer = Uint8List(blake2bLength); 360 | 361 | /* V1 */ 362 | digest.update(outLenBytes, 0, outLenBytes.length); 363 | digest.update(input, 0, input.length); 364 | digest.doFinal(outBuffer, 0); 365 | 366 | var halfLen = blake2bLength ~/ 2, outPos = outOff; 367 | out.setFrom(outPos, outBuffer, 0, halfLen); 368 | 369 | outPos += halfLen; 370 | 371 | var r = ((outLen + 31) ~/ 32) - 2; 372 | 373 | for (var i = 2; i <= r; i++, outPos += halfLen) { 374 | digest.reset(); 375 | /* V2 to Vr */ 376 | digest.update(outBuffer, 0, outBuffer.length); 377 | digest.doFinal(outBuffer, 0); 378 | 379 | out.setFrom(outPos, outBuffer, 0, halfLen); 380 | } 381 | 382 | var lastLength = outLen - 32 * r; 383 | 384 | /* Vr+1 */ 385 | digest = Blake2bDigest(digestSize: lastLength); 386 | digest.update(outBuffer, 0, outBuffer.length); 387 | digest.doFinal(out, outPos); 388 | } 389 | } 390 | 391 | void _initialize( 392 | Uint8List tmpBlockBytes, Uint8List password, int outputLength) { 393 | /** 394 | * H0 = H64(p, τ, m, t, v, y, |P|, P, |S|, S, |L|, K, |X|, X) 395 | * -> 64 byte (ARGON2_PREHASH_DIGEST_LENGTH) 396 | */ 397 | 398 | var blake = Blake2bDigest(digestSize: ARGON2_PREHASH_DIGEST_LENGTH); 399 | 400 | var values = Uint32List.fromList([ 401 | _parameters.lanes, 402 | outputLength, 403 | _parameters.memory, 404 | _parameters.iterations, 405 | _parameters.version, 406 | _parameters.type 407 | ]); 408 | 409 | Pack.intListToLittleEndianAtList(values, tmpBlockBytes, 0); 410 | blake.update(tmpBlockBytes, 0, values.length * 4); 411 | 412 | _addByteString(tmpBlockBytes, blake, password); 413 | _addByteString(tmpBlockBytes, blake, _parameters.salt); 414 | _addByteString(tmpBlockBytes, blake, _parameters.secret); 415 | _addByteString(tmpBlockBytes, blake, _parameters.additional); 416 | 417 | var initialHashWithZeros = Uint8List(ARGON2_PREHASH_SEED_LENGTH); 418 | blake.doFinal(initialHashWithZeros, 0); 419 | 420 | _fillFirstBlocks(tmpBlockBytes, initialHashWithZeros); 421 | } 422 | 423 | static void _addByteString(Uint8List tmpBlockBytes, Digest digest, 424 | [Uint8List? octets]) { 425 | if (octets == null) { 426 | digest.update(_ZERO_BYTES, 0, 4); 427 | return; 428 | } 429 | 430 | Pack.intToLittleEndianAtList(octets.length, tmpBlockBytes, 0); 431 | digest.update(tmpBlockBytes, 0, 4); 432 | digest.update(octets, 0, octets.length); 433 | } 434 | 435 | /// (H0 || 0 || i) 72 byte -> 1024 byte 436 | /// (H0 || 1 || i) 72 byte -> 1024 byte 437 | void _fillFirstBlocks( 438 | Uint8List tmpBlockBytes, Uint8List initialHashWithZeros) { 439 | var initialHashWithOnes = Uint8List(ARGON2_PREHASH_SEED_LENGTH); 440 | initialHashWithOnes.setFrom( 441 | 0, initialHashWithZeros, 0, ARGON2_PREHASH_DIGEST_LENGTH); 442 | 443 | initialHashWithOnes[ARGON2_PREHASH_DIGEST_LENGTH] = 1; 444 | 445 | for (var i = 0; i < _parameters.lanes; i++) { 446 | Pack.intToLittleEndianAtList( 447 | i, initialHashWithZeros, ARGON2_PREHASH_DIGEST_LENGTH + 4); 448 | Pack.intToLittleEndianAtList( 449 | i, initialHashWithOnes, ARGON2_PREHASH_DIGEST_LENGTH + 4); 450 | 451 | _hash(initialHashWithZeros, tmpBlockBytes, 0, ARGON2_BLOCK_SIZE); 452 | _memory[i * _laneLength + 0].fromBytes(tmpBlockBytes); 453 | 454 | _hash(initialHashWithOnes, tmpBlockBytes, 0, ARGON2_BLOCK_SIZE); 455 | _memory[i * _laneLength + 1].fromBytes(tmpBlockBytes); 456 | } 457 | } 458 | 459 | static int _intToLong(int x) => (x & M32L); 460 | } 461 | 462 | class _FillBlock { 463 | final _Block _r = _Block(); 464 | final _Block _z = _Block(); 465 | 466 | _Block addressBlock = _Block(); 467 | _Block inputBlock = _Block(); 468 | 469 | void _applyBlake() { 470 | /* Apply Blake2 on columns of 64-bit words: (0,1,...,15) , then 471 | (16,17,..31)... finally (112,113,...127) */ 472 | for (var i = 0; i < 8; i++) { 473 | var i16 = 16 * i; 474 | _roundFunction( 475 | _z, 476 | i16, 477 | i16 + 1, 478 | i16 + 2, 479 | i16 + 3, 480 | i16 + 4, 481 | i16 + 5, 482 | i16 + 6, 483 | i16 + 7, 484 | i16 + 8, 485 | i16 + 9, 486 | i16 + 10, 487 | i16 + 11, 488 | i16 + 12, 489 | i16 + 13, 490 | i16 + 14, 491 | i16 + 15); 492 | } 493 | 494 | /* Apply Blake2 on rows of 64-bit words: (0,1,16,17,...112,113), then 495 | (2,3,18,19,...,114,115).. finally (14,15,30,31,...,126,127) */ 496 | for (var i = 0; i < 8; i++) { 497 | var i2 = 2 * i; 498 | _roundFunction( 499 | _z, 500 | i2, 501 | i2 + 1, 502 | i2 + 16, 503 | i2 + 17, 504 | i2 + 32, 505 | i2 + 33, 506 | i2 + 48, 507 | i2 + 49, 508 | i2 + 64, 509 | i2 + 65, 510 | i2 + 80, 511 | i2 + 81, 512 | i2 + 96, 513 | i2 + 97, 514 | i2 + 112, 515 | i2 + 113); 516 | } 517 | } 518 | 519 | void fillBlock(_Block Y, _Block currentBlock) { 520 | _z.copyBlock(Y); 521 | _applyBlake(); 522 | currentBlock.xor(Y, _z); 523 | } 524 | 525 | void fillBlock2(_Block X, _Block Y, _Block currentBlock) { 526 | _r.xor(X, Y); 527 | _z.copyBlock(_r); 528 | _applyBlake(); 529 | currentBlock.xor(_r, _z); 530 | } 531 | 532 | void fillBlockWithXor(_Block X, _Block Y, _Block currentBlock) { 533 | _r.xor(X, Y); 534 | _z.copyBlock(_r); 535 | _applyBlake(); 536 | currentBlock.xorWith2(_r, _z); 537 | } 538 | 539 | static void _roundFunction( 540 | _Block block, 541 | int v0, 542 | int v1, 543 | int v2, 544 | int v3, 545 | int v4, 546 | int v5, 547 | int v6, 548 | int v7, 549 | int v8, 550 | int v9, 551 | int v10, 552 | int v11, 553 | int v12, 554 | int v13, 555 | int v14, 556 | int v15) { 557 | final v = block._v; 558 | 559 | _F(v, v0, v4, v8, v12); 560 | _F(v, v1, v5, v9, v13); 561 | _F(v, v2, v6, v10, v14); 562 | _F(v, v3, v7, v11, v15); 563 | 564 | _F(v, v0, v5, v10, v15); 565 | _F(v, v1, v6, v11, v12); 566 | _F(v, v2, v7, v8, v13); 567 | _F(v, v3, v4, v9, v14); 568 | } 569 | 570 | static void _F(Uint64List v, int a, int b, int c, int d) { 571 | _quarterRound(v, a, b, d, 32); 572 | _quarterRound(v, c, d, b, 24); 573 | _quarterRound(v, a, b, d, 16); 574 | _quarterRound(v, c, d, b, 63); 575 | } 576 | 577 | static void _quarterRound(Uint64List v, int x, int y, int z, int s) { 578 | var a = v[x]; 579 | var b = v[y]; 580 | var c = v[z]; 581 | 582 | a += b + 2 * Longs.toInt32(a) * Longs.toInt32(b); 583 | c = Longs.rotateRight(c ^ a, s); 584 | 585 | v[x] = a; 586 | v[z] = c; 587 | } 588 | } 589 | 590 | class _Block { 591 | static const int SIZE = Argon2BytesGenerator.ARGON2_QWORDS_IN_BLOCK; 592 | 593 | /// 128 * 8 Byte QWords. 594 | final Uint64List _v = Uint64List(SIZE); 595 | 596 | _Block(); 597 | 598 | void fromBytes(Uint8List input) { 599 | if (input.length < Argon2BytesGenerator.ARGON2_BLOCK_SIZE) { 600 | throw ArgumentError.value( 601 | input.length, 'input.length', 'input shorter than blocksize'); 602 | } 603 | 604 | Pack.littleEndianToLongAtList(input, 0, _v); 605 | } 606 | 607 | void toBytes(Uint8List output) { 608 | if (output.length < Argon2BytesGenerator.ARGON2_BLOCK_SIZE) { 609 | throw ArgumentError.value( 610 | output.length, 'output.length', 'output shorter than blocksize'); 611 | } 612 | 613 | Pack.longListToLittleEndianAtList(_v, output, 0); 614 | } 615 | 616 | void copyBlock(_Block other) { 617 | _v.setAll(0, other._v); 618 | } 619 | 620 | void xor(_Block b1, _Block b2) { 621 | var v0 = _v; 622 | var v1 = b1._v; 623 | var v2 = b2._v; 624 | 625 | for (var i = SIZE - 1; i >= 0; --i) { 626 | v0[i] = v1[i] ^ v2[i]; 627 | } 628 | } 629 | 630 | void xorWith(_Block b1) { 631 | var v0 = _v; 632 | var v1 = b1._v; 633 | for (var i = SIZE - 1; i >= 0; --i) { 634 | v0[i] ^= v1[i]; 635 | } 636 | } 637 | 638 | void xorWith2(_Block b1, _Block b2) { 639 | var v0 = _v; 640 | var v1 = b1._v; 641 | var v2 = b2._v; 642 | for (var i = SIZE - 1; i >= 0; --i) { 643 | v0[i] ^= v1[i] ^ v2[i]; 644 | } 645 | } 646 | 647 | _Block clear() { 648 | _v.setAllElementsTo(0); 649 | 650 | return this; 651 | } 652 | } 653 | 654 | class _Position { 655 | int pass; 656 | int lane; 657 | int slice; 658 | 659 | _Position([this.pass = 0, this.lane = 0, this.slice = 0]); 660 | } 661 | --------------------------------------------------------------------------------