1+ 'use strict' ;
2+
3+ import Hasher32be from "./hasher32be" ;
4+ import { rotateLeft } from "../tools/tools" ;
5+
6+
7+ /**
8+ * Calculates [SM3](https://tools.ietf.org/id/draft-oscca-cfrg-sm3-02.html) hash
9+ *
10+ * @example <caption>Calculates SM3 hash from string "message" - ES6 style</caption>
11+ * import Sm3 from "crypto-api/src/hasher/sm3";
12+ * import {toHex} from "crypto-api/src/encoder/hex";
13+ *
14+ * let hasher = new Sm3();
15+ * hasher.update('message');
16+ * console.log(toHex(hasher.finalize()));
17+ *
18+ * @example <caption>Calculates SM3 hash from UTF string "message" - ES6 style</caption>
19+ * import Sm3 from "crypto-api/src/hasher/sm3";
20+ * import {toHex} from "crypto-api/src/encoder/hex";
21+ * import {fromUtf} from "crypto-api/src/encoder/utf";
22+ *
23+ * let hasher = new Sm3();
24+ * hasher.update(fromUtf('message'));
25+ * console.log(toHex(hasher.finalize()));
26+ *
27+ * @example <caption>Calculates SM3 hash from string "message" - ES5 style</caption>
28+ * <script src="https://nf404.github.io/crypto-api/crypto-api.min.js"></script>
29+ * <script>
30+ * var hasher = CryptoApi.getHasher('sm3');
31+ * hasher.update('message');
32+ * console.log(CryptoApi.encoder.toHex(hasher.finalize()));
33+ * </script>
34+ *
35+ * @example <caption>Calculates SM3 hash from UTF string "message" - ES5 style</caption>
36+ * <script src="https://nf404.github.io/crypto-api/crypto-api.min.js"></script>
37+ * <script>
38+ * console.log(CryptoApi.hash('sm3', 'message'));
39+ * </script>
40+ */
41+ class Sm3 extends Hasher32be {
42+ /**
43+ * @param {Object } [options]
44+ * @param {number } [options.rounds=64] - Number of rounds (Must be greater than 16)
45+ * @param {number } [options.length=256] - Length of hash result
46+ */
47+ constructor ( options ) {
48+ options = options || { } ;
49+ options . length = options . length || 256 ;
50+ options . rounds = options . rounds || 64 ;
51+ super ( options ) ;
52+
53+ /**
54+ * Working variable (only for speed optimization)
55+ * @private
56+ * @ignore
57+ * @type {number[] }
58+ */
59+ this . W = new Array ( 132 ) ;
60+ }
61+
62+ /**
63+ * Reset hasher to initial state
64+ */
65+ reset ( ) {
66+ super . reset ( ) ;
67+ this . state . hash = [
68+ 0x7380166f | 0 , 0x4914b2b9 | 0 , 0x172442d7 | 0 , 0xda8a0600 | 0 ,
69+ 0xa96f30bc | 0 , 0x163138aa | 0 , 0xe38dee4d | 0 , 0xb0fb0e4e | 0
70+ ] ;
71+ }
72+
73+ /**
74+ * @protected
75+ * @ignore
76+ * @param {number } x
77+ * @returns {number }
78+ */
79+ static p0 ( x ) {
80+ return x ^ rotateLeft ( x , 9 ) ^ rotateLeft ( x , 17 )
81+ }
82+
83+ /**
84+ * @protected
85+ * @ignore
86+ * @param {number } x
87+ * @returns {number }
88+ */
89+ static p1 ( x ) {
90+ return x ^ rotateLeft ( x , 15 ) ^ rotateLeft ( x , 23 )
91+ }
92+
93+ /**
94+ * @protected
95+ * @ignore
96+ * @param {number } i
97+ * @returns {number }
98+ */
99+ static tj ( i ) {
100+ return i < 16 ? 0x79cc4519 : 0x7a879d8a ;
101+ }
102+
103+ /**
104+ * @protected
105+ * @ignore
106+ * @param {number } i
107+ * @param {number } a
108+ * @param {number } b
109+ * @param {number } c
110+ * @returns {number }
111+ */
112+ static ffj ( i , a , b , c ) {
113+ return i < 16 ? a ^ b ^ c : ( a & b ) | ( a & c ) | ( b & c )
114+ }
115+
116+ /**
117+ * @protected
118+ * @ignore
119+ * @param {number } i
120+ * @param {number } e
121+ * @param {number } f
122+ * @param {number } g
123+ * @returns {number }
124+ */
125+ static ggj ( i , e , f , g ) {
126+ return i < 16 ? e ^ f ^ g : ( e & f ) | ( ~ e & g )
127+ }
128+
129+ /**
130+ * Process ready blocks
131+ *
132+ * @protected
133+ * @ignore
134+ * @param {number[] } block - Block
135+ */
136+ processBlock ( block ) {
137+ // Working variables
138+ let a = this . state . hash [ 0 ] | 0 ;
139+ let b = this . state . hash [ 1 ] | 0 ;
140+ let c = this . state . hash [ 2 ] | 0 ;
141+ let d = this . state . hash [ 3 ] | 0 ;
142+ let e = this . state . hash [ 4 ] | 0 ;
143+ let f = this . state . hash [ 5 ] | 0 ;
144+ let g = this . state . hash [ 6 ] | 0 ;
145+ let h = this . state . hash [ 7 ] | 0 ;
146+ // Expand message
147+ for ( let i = 0 ; i < 132 ; i ++ ) {
148+ if ( i < 16 ) {
149+ this . W [ i ] = block [ i ] | 0 ;
150+ } else if ( i < 68 ) {
151+ this . W [ i ] = Sm3 . p1 ( this . W [ i - 16 ] ^ this . W [ i - 9 ] ^ rotateLeft ( this . W [ i - 3 ] , 15 ) ) ^
152+ rotateLeft ( this . W [ i - 13 ] , 7 ) ^ this . W [ i - 6 ]
153+ } else {
154+ this . W [ i ] = this . W [ i - 68 ] ^ this . W [ i - 64 ]
155+ }
156+ }
157+ // Calculate hash
158+ for ( let i = 0 ; i < this . options . rounds ; i ++ ) {
159+ let ss1 = rotateLeft ( ( rotateLeft ( a , 12 ) + e + rotateLeft ( Sm3 . tj ( i ) , i % 32 ) ) | 0 , 7 )
160+ let ss2 = ss1 ^ rotateLeft ( a , 12 )
161+ let tt1 = ( Sm3 . ffj ( i , a , b , c ) + d + ss2 + this . W [ i + 68 ] ) | 0
162+ let tt2 = ( Sm3 . ggj ( i , e , f , g ) + h + ss1 + this . W [ i ] ) | 0
163+
164+ d = c
165+ c = rotateLeft ( b , 9 )
166+ b = a
167+ a = tt1
168+ h = g
169+ g = rotateLeft ( f , 19 )
170+ f = e
171+ e = Sm3 . p0 ( tt2 )
172+ }
173+
174+ this . state . hash [ 0 ] = this . state . hash [ 0 ] ^ a ;
175+ this . state . hash [ 1 ] = this . state . hash [ 1 ] ^ b ;
176+ this . state . hash [ 2 ] = this . state . hash [ 2 ] ^ c ;
177+ this . state . hash [ 3 ] = this . state . hash [ 3 ] ^ d ;
178+ this . state . hash [ 4 ] = this . state . hash [ 4 ] ^ e ;
179+ this . state . hash [ 5 ] = this . state . hash [ 5 ] ^ f ;
180+ this . state . hash [ 6 ] = this . state . hash [ 6 ] ^ g ;
181+ this . state . hash [ 7 ] = this . state . hash [ 7 ] ^ h ;
182+ }
183+
184+ /**
185+ * Finalize hash and return result
186+ *
187+ * @returns {string }
188+ */
189+ finalize ( ) {
190+ this . addPaddingISO7816 (
191+ this . state . message . length < 56 ?
192+ 56 - this . state . message . length | 0 :
193+ 120 - this . state . message . length | 0 ) ;
194+ this . addLengthBits ( ) ;
195+ this . process ( ) ;
196+ return this . getStateHash ( ( this . options . length / 32 ) | 0 ) ;
197+ }
198+ }
199+
200+ export default Sm3 ;
0 commit comments