Skip to content

Commit

Permalink
add sm3 hash
Browse files Browse the repository at this point in the history
  • Loading branch information
nf404 committed Jul 8, 2019
1 parent 2905748 commit 01cb779
Show file tree
Hide file tree
Showing 7 changed files with 290 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
- Add toBase64
- Add fromArrayBuffer
- Add support of SHA512/t hash
- Optimize hash padding
- Add SM3 hash
* 0.8.3
- Fix Whirlpool bug
- Add compiled es5 version for nodejs
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
* [SHA512/t (SHA512/256 SHA512/224)](https://nf404.github.io/crypto-api/class/src/hasher/sha512.mjs~Sha512.html)
* [Snefru v2.0 (2 rounds 128, 4 rounds 256), Snefru v2.5 (8 rounds)](https://nf404.github.io/crypto-api/class/src/hasher/snefru.mjs~Snefru.html)
* [WHIRLPOOL (WHIRLPOOL-0, WHIRLPOOL-T)](https://nf404.github.io/crypto-api/class/src/hasher/whirlpool.mjs~Whirlpool.html)
* [SM3](https://nf404.github.io/crypto-api/class/src/hasher/sm3.mjs~Sm3.html)

### MAC
* [HMAC](https://nf404.github.io/crypto-api/class/src/mac/hmac.mjs~Hmac.html)
Expand All @@ -50,7 +51,7 @@

## Examples

### ES6 (recomended)
### ES6 (recommended)

Calculates SHA256 hash from UTF string "message"
```javascript
Expand Down
10 changes: 10 additions & 0 deletions example/benchmark.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Sha256 from "../src/hasher/sha256";
import Sha512 from "../src/hasher/sha512";
import Snefru from "../src/hasher/snefru";
import Whirlpool from "../src/hasher/whirlpool";
import Sm3 from "../src/hasher/sm3";
import {fromUtf} from "../src/encoder/utf";
import {toHex} from "../src/encoder/hex";

Expand Down Expand Up @@ -90,6 +91,11 @@ suite('Hash from simple string with HEX result', function (suite) {
suite.whirlpool.update(fromUtf('xxx'));
toHex(suite.whirlpool.finalize());
});
bench('sm3', function () {
suite.sm3 = new Sm3();
suite.sm3.update(fromUtf('xxx'));
toHex(suite.sm3.finalize());
});
});
suite('Update', function (suite) {
setup(function () {
Expand All @@ -108,6 +114,7 @@ suite('Update', function (suite) {
suite.sha512 = new Sha512();
suite.snefru = new Snefru();
suite.whirlpool = new Whirlpool();
suite.sm3 = new Sm3();
});
bench('md2', function () {
suite.md2.update(fromUtf('xxx'));
Expand Down Expand Up @@ -154,4 +161,7 @@ suite('Update', function (suite) {
bench('whirlpool', function () {
suite.whirlpool.update(fromUtf('xxx'));
});
bench('sm3', function () {
suite.sm3.update(fromUtf('xxx'));
});
});
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
"whirlpool",
"whirlpool-0",
"whirlpool-t",
"sm3",
"snefru",
"hmac"
]
Expand Down
200 changes: 200 additions & 0 deletions src/hasher/sm3.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
'use strict';

import Hasher32be from "./hasher32be";
import {rotateLeft} from "../tools/tools";


/**
* Calculates [SM3](https://tools.ietf.org/id/draft-oscca-cfrg-sm3-02.html) hash
*
* @example <caption>Calculates SM3 hash from string "message" - ES6 style</caption>
* import Sm3 from "crypto-api/src/hasher/sm3";
* import {toHex} from "crypto-api/src/encoder/hex";
*
* let hasher = new Sm3();
* hasher.update('message');
* console.log(toHex(hasher.finalize()));
*
* @example <caption>Calculates SM3 hash from UTF string "message" - ES6 style</caption>
* import Sm3 from "crypto-api/src/hasher/sm3";
* import {toHex} from "crypto-api/src/encoder/hex";
* import {fromUtf} from "crypto-api/src/encoder/utf";
*
* let hasher = new Sm3();
* hasher.update(fromUtf('message'));
* console.log(toHex(hasher.finalize()));
*
* @example <caption>Calculates SM3 hash from string "message" - ES5 style</caption>
* <script src="https://nf404.github.io/crypto-api/crypto-api.min.js"></script>
* <script>
* var hasher = CryptoApi.getHasher('sm3');
* hasher.update('message');
* console.log(CryptoApi.encoder.toHex(hasher.finalize()));
* </script>
*
* @example <caption>Calculates SM3 hash from UTF string "message" - ES5 style</caption>
* <script src="https://nf404.github.io/crypto-api/crypto-api.min.js"></script>
* <script>
* console.log(CryptoApi.hash('sm3', 'message'));
* </script>
*/
class Sm3 extends Hasher32be {
/**
* @param {Object} [options]
* @param {number} [options.rounds=64] - Number of rounds (Must be greater than 16)
* @param {number} [options.length=256] - Length of hash result
*/
constructor(options) {
options = options || {};
options.length = options.length || 256;
options.rounds = options.rounds || 64;
super(options);

/**
* Working variable (only for speed optimization)
* @private
* @ignore
* @type {number[]}
*/
this.W = new Array(132);
}

/**
* Reset hasher to initial state
*/
reset() {
super.reset();
this.state.hash = [
0x7380166f | 0, 0x4914b2b9 | 0, 0x172442d7 | 0, 0xda8a0600 | 0,
0xa96f30bc | 0, 0x163138aa | 0, 0xe38dee4d | 0, 0xb0fb0e4e | 0
];
}

/**
* @protected
* @ignore
* @param {number} x
* @returns {number}
*/
static p0 (x) {
return x ^ rotateLeft(x, 9) ^ rotateLeft(x, 17)
}

/**
* @protected
* @ignore
* @param {number} x
* @returns {number}
*/
static p1 (x) {
return x ^ rotateLeft(x, 15) ^ rotateLeft(x, 23)
}

/**
* @protected
* @ignore
* @param {number} i
* @returns {number}
*/
static tj (i) {
return i < 16 ? 0x79cc4519 : 0x7a879d8a;
}

/**
* @protected
* @ignore
* @param {number} i
* @param {number} a
* @param {number} b
* @param {number} c
* @returns {number}
*/
static ffj(i, a, b, c) {
return i < 16 ? a ^ b ^ c : (a & b) | (a & c) | (b & c)
}

/**
* @protected
* @ignore
* @param {number} i
* @param {number} e
* @param {number} f
* @param {number} g
* @returns {number}
*/
static ggj(i, e, f, g) {
return i < 16 ? e ^ f ^ g : (e & f) | (~e & g)
}

/**
* Process ready blocks
*
* @protected
* @ignore
* @param {number[]} block - Block
*/
processBlock(block) {
// Working variables
let a = this.state.hash[0] | 0;
let b = this.state.hash[1] | 0;
let c = this.state.hash[2] | 0;
let d = this.state.hash[3] | 0;
let e = this.state.hash[4] | 0;
let f = this.state.hash[5] | 0;
let g = this.state.hash[6] | 0;
let h = this.state.hash[7] | 0;
// Expand message
for (let i = 0; i < 132; i++) {
if (i < 16) {
this.W[i] = block[i] | 0;
} else if (i < 68) {
this.W[i] = Sm3.p1(this.W[i - 16] ^ this.W[i - 9] ^ rotateLeft(this.W[i - 3], 15)) ^
rotateLeft(this.W[i - 13], 7) ^ this.W[i - 6]
} else {
this.W[i] = this.W[i - 68] ^ this.W[i - 64]
}
}
// Calculate hash
for (let i = 0; i < this.options.rounds; i++) {
let ss1 = rotateLeft((rotateLeft(a, 12) + e + rotateLeft(Sm3.tj(i), i % 32)) | 0,7)
let ss2 = ss1 ^ rotateLeft(a, 12)
let tt1 = (Sm3.ffj(i, a, b, c) + d + ss2 + this.W[i + 68]) | 0
let tt2 = (Sm3.ggj(i, e, f, g) + h + ss1 + this.W[i]) | 0

d = c
c = rotateLeft(b, 9)
b = a
a = tt1
h = g
g = rotateLeft(f, 19)
f = e
e = Sm3.p0(tt2)
}

this.state.hash[0] = this.state.hash[0] ^ a;
this.state.hash[1] = this.state.hash[1] ^ b;
this.state.hash[2] = this.state.hash[2] ^ c;
this.state.hash[3] = this.state.hash[3] ^ d;
this.state.hash[4] = this.state.hash[4] ^ e;
this.state.hash[5] = this.state.hash[5] ^ f;
this.state.hash[6] = this.state.hash[6] ^ g;
this.state.hash[7] = this.state.hash[7] ^ h;
}

/**
* Finalize hash and return result
*
* @returns {string}
*/
finalize() {
this.addPaddingISO7816(
this.state.message.length < 56 ?
56 - this.state.message.length | 0 :
120 - this.state.message.length | 0);
this.addLengthBits();
this.process();
return this.getStateHash((this.options.length / 32) | 0);
}
}

export default Sm3;
73 changes: 73 additions & 0 deletions test/hasher/sm3Test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*global describe, it */
'use strict';
import Sm3 from "../../src/hasher/sm3";
import TestHasher from "../TestHasher";

// The SM3 test suite
// https://tools.ietf.org/id/draft-oscca-cfrg-sm3-02.html#rfc.appendix.A.1
// https://github.com/adamws/oscca-sm3/blob/master/example/example.c
// https://dev.gnupg.org/source/libgcrypt/browse/master/tests/basic.c;ab57613f10ad57d2fec648017c18d7abb189863b$10642

class Sm3Test extends TestHasher {
test() {
let t = this;
/** @test {Sm3} */
describe('Hash sm3 tests', function () {
it("sm3('')", function () {
t.testHash({
message: '',
hash: '1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b'
});
});

it("sm3('a')", function () {
t.testHash({
message: 'a',
hash: '623476ac18f65a2909e43c7fec61b49c7e764a91a18ccb82f1917a29c86c5e88'
});
});

it("sm3('abc')", function () {
t.testHash({
message: 'abc',
hash: '66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0'
});
});

it("sm3('abcd' x 16)", function () {
t.testHash({
message: new Array(17).join('abcd'),
hash: 'debe9ff92275b8a138604889c18e5a4d6fdb70e5387e5765293dcba39c0c5732'
});
});

it("sm3('a..z')", function () {
t.testHash({
message: 'abcdefghijklmnopqrstuvwxyz',
hash: 'b80fe97a4da24afc277564f66a359ef440462ad28dcc6d63adb24d5c20a61595'
});
});

it("sm3('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')", function () {
t.testHash({
message: 'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq',
hash: '639b6cc5e64d9e37a390b192df4fa1ea0720ab747ff692b9f38c4e66ad7b8c05'
});
});

/**
* @test {Sm3#setState}
* @test {Sm3#getState}
*/
it('hash setState() getState()', function () {
t.testSetGetState();
});
});
}

getInstance(options) {
return new Sm3();
}
}

export default Sm3Test;
3 changes: 2 additions & 1 deletion test/test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import Snefru256_8Test from "./hasher/snefru256_8Test";
import Whirlpool0Test from "./hasher/whirlpool-0Test";
import WhirlpoolTTest from "./hasher/whirlpool-tTest";
import WhirlpoolTest from "./hasher/whirlpoolTest";
import Sm3Test from './hasher/sm3Test'

import HmacHas160Test from "./mac/hmac-has160Test";
import HmacMd5Test from "./mac/hmac-md5Test";
Expand All @@ -36,7 +37,6 @@ import UtfTest from "./encoder/UtfTest";
import Base64Test from "./encoder/Base64Test";
import ArrayBufferTest from "./encoder/ArrayBufferTest";


// Hash tests
(new Has160Test()).test();
(new Md2Test()).test();
Expand All @@ -61,6 +61,7 @@ import ArrayBufferTest from "./encoder/ArrayBufferTest";
(new Whirlpool0Test()).test();
(new WhirlpoolTTest()).test();
(new WhirlpoolTest()).test();
(new Sm3Test()).test();

// HMAC tests
(new HmacHas160Test()).test();
Expand Down

0 comments on commit 01cb779

Please sign in to comment.