Skip to content

Commit 61dd0f0

Browse files
author
Grégory Saive
authored
Merge pull request #190 from rg911/task/g189_Keccak_KeyPair_Key_Derive
[Elephant] Issue #189 Added SignSchema
2 parents 544c7ff + 94be804 commit 61dd0f0

25 files changed

+1104
-138
lines changed

src/core/crypto/Crypto.ts

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { WalletAlgorithm } from '../../model/wallet/WalletAlgorithm';
1818
import { Convert as convert } from '../format/Convert';
1919
import { KeyPair } from './KeyPair';
2020
import * as utility from './Utilities';
21+
import { SignSchema } from './SignSchema';
2122
const CryptoJS = require('crypto-js');
2223
export class Crypto {
2324
/**
@@ -218,16 +219,16 @@ export class Crypto {
218219
* @param {string} msg - A text message
219220
* @param {Uint8Array} iv - An initialization vector
220221
* @param {Uint8Array} salt - A salt
221-
*
222+
* @param {SignSchema} signSchema The Sign Schema. (KECCAK_REVERSED_KEY / SHA3)
222223
* @return {string} - The encoded message
223224
*/
224-
public static _encode = function(senderPriv, recipientPub, msg, iv, salt) {
225+
public static _encode = (senderPriv, recipientPub, msg, iv, salt, signSchema: SignSchema = SignSchema.SHA3) => {
225226
// Errors
226227
if (!senderPriv || !recipientPub || !msg || !iv || !salt) { throw new Error('Missing argument !'); }
227228
// Processing
228-
const keyPair = KeyPair.createKeyPairFromPrivateKeyString(senderPriv);
229+
const keyPair = KeyPair.createKeyPairFromPrivateKeyString(senderPriv, signSchema);
229230
const pk = convert.hexToUint8(recipientPub);
230-
const encKey = utility.ua2words(KeyPair.deriveSharedKey(keyPair, pk, salt), 32);
231+
const encKey = utility.ua2words(KeyPair.deriveSharedKey(keyPair, pk, salt, signSchema), 32);
231232
const encIv = {
232233
iv: utility.ua2words(iv, 16),
233234
};
@@ -243,16 +244,16 @@ export class Crypto {
243244
* @param {string} senderPriv - A sender private key
244245
* @param {string} recipientPub - A recipient public key
245246
* @param {string} msg - A text message
246-
*
247+
* @param {SignSchema} signSchema The Sign Schema. (KECCAK_REVERSED_KEY / SHA3)
247248
* @return {string} - The encoded message
248249
*/
249-
public static encode = (senderPriv, recipientPub, msg) => {
250+
public static encode = (senderPriv, recipientPub, msg, signSchema: SignSchema = SignSchema.SHA3) => {
250251
// Errors
251252
if (!senderPriv || !recipientPub || !msg) { throw new Error('Missing argument !'); }
252253
// Processing
253254
const iv = Crypto.randomBytes(16);
254255
const salt = Crypto.randomBytes(32);
255-
const encoded = Crypto._encode(senderPriv, recipientPub, msg, iv, salt);
256+
const encoded = Crypto._encode(senderPriv, recipientPub, msg, iv, salt, signSchema);
256257
// Result
257258
return encoded;
258259
}
@@ -263,16 +264,16 @@ export class Crypto {
263264
* @param {string} recipientPrivate - A recipient private key
264265
* @param {string} senderPublic - A sender public key
265266
* @param {Uint8Array} _payload - An encrypted message payload in bytes
266-
*
267+
* @param {SignSchema} signSchema The Sign Schema. (KECCAK_REVERSED_KEY / SHA3)
267268
* @return {string} - The decoded payload as hex
268269
*/
269-
public static _decode = (recipientPrivate, senderPublic, payload, iv, salt) => {
270+
public static _decode = (recipientPrivate, senderPublic, payload, iv, salt, signSchema: SignSchema = SignSchema.SHA3) => {
270271
// Error
271272
if (!recipientPrivate || !senderPublic || !payload) { throw new Error('Missing argument !'); }
272273
// Processing
273-
const keyPair = KeyPair.createKeyPairFromPrivateKeyString(recipientPrivate);
274+
const keyPair = KeyPair.createKeyPairFromPrivateKeyString(recipientPrivate, signSchema);
274275
const pk = convert.hexToUint8(senderPublic);
275-
const encKey = utility.ua2words(KeyPair.deriveSharedKey(keyPair, pk, salt), 32);
276+
const encKey = utility.ua2words(KeyPair.deriveSharedKey(keyPair, pk, salt, signSchema), 32);
276277
const encIv = {
277278
iv: utility.ua2words(iv, 16),
278279
};
@@ -290,18 +291,18 @@ export class Crypto {
290291
* @param {string} recipientPrivate - A recipient private key
291292
* @param {string} senderPublic - A sender public key
292293
* @param {string} _payload - An encrypted message payload
293-
*
294+
* @param {SignSchema} signSchema The Sign Schema. (KECCAK_REVERSED_KEY / SHA3)
294295
* @return {string} - The decoded payload as hex
295296
*/
296-
public static decode = (recipientPrivate, senderPublic, _payload) => {
297+
public static decode = (recipientPrivate, senderPublic, _payload, signSchema: SignSchema = SignSchema.SHA3) => {
297298
// Error
298299
if (!recipientPrivate || !senderPublic || !_payload) { throw new Error('Missing argument !'); }
299300
// Processing
300301
const binPayload = convert.hexToUint8(_payload);
301302
const payload = new Uint8Array(binPayload.buffer, 48);
302303
const salt = new Uint8Array(binPayload.buffer, 0, 32);
303304
const iv = new Uint8Array(binPayload.buffer, 32, 16);
304-
const decoded = Crypto._decode(recipientPrivate, senderPublic, payload, iv, salt);
305+
const decoded = Crypto._decode(recipientPrivate, senderPublic, payload, iv, salt, signSchema);
305306
return decoded;
306307
}
307308

src/core/crypto/KeyPair.ts

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2018 NEM
2+
* Copyright 2019 NEM
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -14,21 +14,25 @@
1414
* limitations under the License.
1515
*/
1616
import { Convert as convert } from '../format';
17+
import { SignSchema } from './SignSchema';
1718
import * as Utility from './Utilities';
1819

1920
export class KeyPair {
2021
/**
2122
* Creates a key pair from a private key string.
2223
* @param {string} privateKeyString A hex encoded private key string.
24+
* @param {SignSchema} signSchema The Sign Schema. (KECCAK_REVERSED_KEY / SHA3)
2325
* @returns {module:crypto/keyPair~KeyPair} The key pair.
2426
*/
25-
public static createKeyPairFromPrivateKeyString = (privateKeyString) => {
27+
public static createKeyPairFromPrivateKeyString = (privateKeyString, signSchema = SignSchema.SHA3) => {
2628
const privateKey = convert.hexToUint8(privateKeyString);
29+
30+
// KECCAK_REVERSED_KEY uses reversed private key.
31+
const secretKey = signSchema === SignSchema.SHA3 ? privateKey : convert.hexToUint8Reverse(privateKeyString);
2732
if (Utility.Key_Size !== privateKey.length) {
2833
throw Error(`private key has unexpected size: ${privateKey.length}`);
2934
}
30-
31-
const publicKey = Utility.catapult_crypto.extractPublicKey(privateKey, Utility.catapult_hash.func);
35+
const publicKey = Utility.catapult_crypto.extractPublicKey(secretKey, Utility.catapult_hash.func, signSchema);
3236
return {
3337
privateKey,
3438
publicKey,
@@ -39,36 +43,52 @@ export class KeyPair {
3943
* Signs a data buffer with a key pair.
4044
* @param {module:crypto/keyPair~KeyPair} keyPair The key pair to use for signing.
4145
* @param {Uint8Array} data The data to sign.
46+
* @param {SignSchema} signSchema The Sign Schema. (KECCAK_REVERSED_KEY / SHA3)
4247
* @returns {Uint8Array} The signature.
4348
*/
44-
public static sign = (keyPair, data) => {
45-
return Utility.catapult_crypto.sign(data, keyPair.publicKey, keyPair.privateKey, Utility.catapult_hash.createHasher());
49+
public static sign = (keyPair, data, signSchema = SignSchema.SHA3) => {
50+
let secretKey = keyPair.privateKey;
51+
// KECCAK_REVERSED_KEY uses reversed private key.
52+
if (signSchema === SignSchema.KECCAK_REVERSED_KEY) {
53+
secretKey = convert.hexToUint8Reverse(convert.uint8ToHex(secretKey));
54+
}
55+
return Utility.catapult_crypto.sign(data, keyPair.publicKey, secretKey,
56+
Utility.catapult_hash.createHasher(64, signSchema));
4657
}
4758

4859
/**
4960
* Verifies a signature.
5061
* @param {module:crypto/keyPair~PublicKey} publicKey The public key to use for verification.
5162
* @param {Uint8Array} data The data to verify.
5263
* @param {Uint8Array} signature The signature to verify.
64+
* @param {SignSchema} signSchema The Sign Schema. (KECCAK_REVERSED_KEY / SHA3)
5365
* @returns {boolean} true if the signature is verifiable, false otherwise.
5466
*/
55-
public static verify = (publicKey, data, signature) => {
56-
return Utility.catapult_crypto.verify(publicKey, data, signature, Utility.catapult_hash.createHasher());
67+
public static verify = (publicKey, data, signature, signSchema = SignSchema.SHA3) => {
68+
return Utility.catapult_crypto.verify(publicKey, data, signature, Utility.catapult_hash.createHasher(64, signSchema));
5769
}
5870

5971
/**
6072
* Creates a shared key given a key pair and an arbitrary public key.
6173
* The shared key can be used for encrypted message passing between the two.
6274
* @param {module:crypto/keyPair~KeyPair} keyPair The key pair for which to create the shared key.
63-
* @param {module:crypto/keyPair~PublicKey} publicKey The public key for which to create the shared key.
75+
* @param {Uint8Array} publicKey The public key for which to create the shared key.
6476
* @param {Uint8Array} salt A salt that should be applied to the shared key.
77+
* @param {SignSchema} signSchema The Sign Schema. (KECCAK_REVERSED_KEY / SHA3)
6578
* @returns {Uint8Array} The shared key.
6679
*/
67-
public static deriveSharedKey = (keyPair, publicKey, salt) => {
80+
public static deriveSharedKey = (keyPair, publicKey, salt, signSchema = SignSchema.SHA3) => {
6881
if (Utility.Key_Size !== salt.length) {
6982
throw Error(`salt has unexpected size: ${salt.length}`);
7083
}
71-
72-
return Utility.catapult_crypto.deriveSharedKey(salt, keyPair.privateKey, publicKey, Utility.catapult_hash.func);
84+
if (Utility.Key_Size !== publicKey.length) {
85+
throw Error(`public key has unexpected size: ${salt.length}`);
86+
}
87+
let secretKey = keyPair.privateKey;
88+
// KECCAK_REVERSED_KEY uses reversed private key.
89+
if (signSchema === SignSchema.KECCAK_REVERSED_KEY) {
90+
secretKey = convert.hexToUint8Reverse(convert.uint8ToHex(secretKey));
91+
}
92+
return Utility.catapult_crypto.deriveSharedKey(salt, secretKey, publicKey, Utility.catapult_hash.func, signSchema);
7393
}
7494
}

src/core/crypto/SHA3Hasher.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,32 +14,35 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { sha3_256, sha3_512 } from 'js-sha3';
17+
import { keccak256, keccak512, sha3_256, sha3_512 } from 'js-sha3';
1818
import { Convert as convert, RawArray as array } from '../format';
19+
import { SignSchema } from './SignSchema';
1920

2021
export class SHA3Hasher {
2122
/**
2223
* Calculates the hash of data.
2324
* @param {Uint8Array} dest The computed hash destination.
2425
* @param {Uint8Array} data The data to hash.
2526
* @param {numeric} length The hash length in bytes.
27+
* @param {SignSchema} signSchema The Sign Schema. (KECCAK_REVERSED_KEY / SHA3)
2628
*/
27-
public static func = (dest, data, length) => {
28-
const hasher = SHA3Hasher.getHasher(length);
29+
public static func = (dest, data, length, signSchema = SignSchema.SHA3) => {
30+
const hasher = SHA3Hasher.getHasher(length, signSchema);
2931
const hash = hasher.arrayBuffer(data);
3032
array.copy(dest, array.uint8View(hash));
3133
}
3234

3335
/**
3436
* Creates a hasher object.
3537
* @param {numeric} length The hash length in bytes.
38+
* @param {SignSchema} signSchema The Sign Schema. (KECCAK_REVERSED_KEY / SHA3)
3639
* @returns {object} The hasher.
3740
*/
38-
public static createHasher = (length = 64) => {
41+
public static createHasher = (length = 64, signSchema = SignSchema.SHA3) => {
3942
let hash;
4043
return {
4144
reset: () => {
42-
hash = SHA3Hasher.getHasher(length).create();
45+
hash = SHA3Hasher.getHasher(length, signSchema).create();
4346
},
4447
update: (data: any) => {
4548
if (data instanceof Uint8Array) {
@@ -59,12 +62,13 @@ export class SHA3Hasher {
5962
/**
6063
* Get a hasher instance.
6164
* @param {numeric} length The hash length in bytes.
65+
* @param {SignSchema} signSchema The Sign Schema. (KECCAK_REVERSED_KEY / SHA3)
6266
* @returns {object} The hasher.
6367
*/
64-
public static getHasher = (length = 64) => {
68+
public static getHasher = (length = 64, signSchema = SignSchema.SHA3) => {
6569
return {
66-
32: sha3_256,
67-
64: sha3_512,
70+
32: signSchema === SignSchema.SHA3 ? sha3_256 : keccak256,
71+
64: signSchema === SignSchema.SHA3 ? sha3_512 : keccak512 ,
6872
} [length];
6973
}
7074
}

src/core/crypto/SignSchema.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright 2019 NEM
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
/**
18+
* [KECCAK_REVERSED_KEY]: Keccak hash algorithm with reversed private keys.
19+
* [SHA3]: SHA3 hash algorithm without key reversal
20+
*/
21+
export enum SignSchema {
22+
KECCAK_REVERSED_KEY = 1,
23+
SHA3 = 2,
24+
}

src/core/crypto/Utilities.ts

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import { RawArray as array } from '../format';
1717
import * as nacl from './nacl_catapult';
1818
import { SHA3Hasher as sha3Hasher } from './SHA3Hasher';
19-
19+
import { SignSchema } from './SignSchema';
2020
export const CryptoJS = require('crypto-js');
2121
export const Key_Size = 32;
2222
export const Signature_Size = 64;
@@ -74,9 +74,9 @@ export const catapult_crypto = (function() {
7474
d[31] |= 64;
7575
}
7676

77-
function prepareForScalarMult(sk, hashfunc) {
77+
function prepareForScalarMult(sk, hashfunc, signSchema: SignSchema) {
7878
const d = new Uint8Array(Hash_Size);
79-
hashfunc(d, sk);
79+
hashfunc(d, sk, Hash_Size, signSchema);
8080
clamp(d);
8181
return d;
8282
}
@@ -108,9 +108,9 @@ export const catapult_crypto = (function() {
108108
})();
109109

110110
return {
111-
extractPublicKey: (sk, hashfunc) => {
111+
extractPublicKey: (sk, hashfunc, signSchema: SignSchema) => {
112112
const c = nacl;
113-
const d = prepareForScalarMult(sk, hashfunc);
113+
const d = prepareForScalarMult(sk, hashfunc, signSchema);
114114

115115
const p = [c.gf(), c.gf(), c.gf(), c.gf()];
116116
const pk = new Uint8Array(Key_Size);
@@ -201,26 +201,24 @@ export const catapult_crypto = (function() {
201201
return 0 === c.crypto_verify_32(signature, 0, t, 0);
202202
},
203203

204-
deriveSharedKey: (salt, sk, pk, hashfunc) => {
204+
deriveSharedKey: (salt, sk, pk, hashfunc, signSchema: SignSchema) => {
205205
const c = nacl;
206-
const d = prepareForScalarMult(sk, hashfunc);
206+
const d = prepareForScalarMult(sk, hashfunc, signSchema);
207207

208208
// sharedKey = pack(p = d (derived from sk) * q (derived from pk))
209209
const q = [c.gf(), c.gf(), c.gf(), c.gf()];
210210
const p = [c.gf(), c.gf(), c.gf(), c.gf()];
211211
const sharedKey = new Uint8Array(Key_Size);
212-
c.unpackneg(q, pk);
212+
c.unpack(q, pk);
213213
c.scalarmult(p, q, d);
214214
c.pack(sharedKey, p);
215-
216215
// salt the shared key
217216
for (let i = 0; i < Key_Size; ++i) {
218217
sharedKey[i] ^= salt[i];
219218
}
220-
221219
// return the hash of the result
222220
const sharedKeyHash = new Uint8Array(Key_Size);
223-
hashfunc(sharedKeyHash, sharedKey, Key_Size);
221+
hashfunc(sharedKeyHash, sharedKey, Key_Size, signSchema);
224222
return sharedKeyHash;
225223
},
226224
};

src/core/crypto/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ export * from './Crypto';
1919
export * from './KeyPair';
2020
export * from './SHA3Hasher';
2121
export * from './nacl_catapult';
22+
export * from './SignSchema';

0 commit comments

Comments
 (0)