Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 15 additions & 14 deletions src/core/crypto/Crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { WalletAlgorithm } from '../../model/wallet/WalletAlgorithm';
import { Convert as convert } from '../format/Convert';
import { KeyPair } from './KeyPair';
import * as utility from './Utilities';
import { SignSchema } from './SignSchema';
const CryptoJS = require('crypto-js');
export class Crypto {
/**
Expand Down Expand Up @@ -218,16 +219,16 @@ export class Crypto {
* @param {string} msg - A text message
* @param {Uint8Array} iv - An initialization vector
* @param {Uint8Array} salt - A salt
*
* @param {SignSchema} signSchema The Sign Schema. (KECCAK_REVERSED_KEY / SHA3)
* @return {string} - The encoded message
*/
public static _encode = function(senderPriv, recipientPub, msg, iv, salt) {
public static _encode = (senderPriv, recipientPub, msg, iv, salt, signSchema: SignSchema = SignSchema.SHA3) => {
// Errors
if (!senderPriv || !recipientPub || !msg || !iv || !salt) { throw new Error('Missing argument !'); }
// Processing
const keyPair = KeyPair.createKeyPairFromPrivateKeyString(senderPriv);
const keyPair = KeyPair.createKeyPairFromPrivateKeyString(senderPriv, signSchema);
const pk = convert.hexToUint8(recipientPub);
const encKey = utility.ua2words(KeyPair.deriveSharedKey(keyPair, pk, salt), 32);
const encKey = utility.ua2words(KeyPair.deriveSharedKey(keyPair, pk, salt, signSchema), 32);
const encIv = {
iv: utility.ua2words(iv, 16),
};
Expand All @@ -243,16 +244,16 @@ export class Crypto {
* @param {string} senderPriv - A sender private key
* @param {string} recipientPub - A recipient public key
* @param {string} msg - A text message
*
* @param {SignSchema} signSchema The Sign Schema. (KECCAK_REVERSED_KEY / SHA3)
* @return {string} - The encoded message
*/
public static encode = (senderPriv, recipientPub, msg) => {
public static encode = (senderPriv, recipientPub, msg, signSchema: SignSchema = SignSchema.SHA3) => {
// Errors
if (!senderPriv || !recipientPub || !msg) { throw new Error('Missing argument !'); }
// Processing
const iv = Crypto.randomBytes(16);
const salt = Crypto.randomBytes(32);
const encoded = Crypto._encode(senderPriv, recipientPub, msg, iv, salt);
const encoded = Crypto._encode(senderPriv, recipientPub, msg, iv, salt, signSchema);
// Result
return encoded;
}
Expand All @@ -263,16 +264,16 @@ export class Crypto {
* @param {string} recipientPrivate - A recipient private key
* @param {string} senderPublic - A sender public key
* @param {Uint8Array} _payload - An encrypted message payload in bytes
*
* @param {SignSchema} signSchema The Sign Schema. (KECCAK_REVERSED_KEY / SHA3)
* @return {string} - The decoded payload as hex
*/
public static _decode = (recipientPrivate, senderPublic, payload, iv, salt) => {
public static _decode = (recipientPrivate, senderPublic, payload, iv, salt, signSchema: SignSchema = SignSchema.SHA3) => {
// Error
if (!recipientPrivate || !senderPublic || !payload) { throw new Error('Missing argument !'); }
// Processing
const keyPair = KeyPair.createKeyPairFromPrivateKeyString(recipientPrivate);
const keyPair = KeyPair.createKeyPairFromPrivateKeyString(recipientPrivate, signSchema);
const pk = convert.hexToUint8(senderPublic);
const encKey = utility.ua2words(KeyPair.deriveSharedKey(keyPair, pk, salt), 32);
const encKey = utility.ua2words(KeyPair.deriveSharedKey(keyPair, pk, salt, signSchema), 32);
const encIv = {
iv: utility.ua2words(iv, 16),
};
Expand All @@ -290,18 +291,18 @@ export class Crypto {
* @param {string} recipientPrivate - A recipient private key
* @param {string} senderPublic - A sender public key
* @param {string} _payload - An encrypted message payload
*
* @param {SignSchema} signSchema The Sign Schema. (KECCAK_REVERSED_KEY / SHA3)
* @return {string} - The decoded payload as hex
*/
public static decode = (recipientPrivate, senderPublic, _payload) => {
public static decode = (recipientPrivate, senderPublic, _payload, signSchema: SignSchema = SignSchema.SHA3) => {
// Error
if (!recipientPrivate || !senderPublic || !_payload) { throw new Error('Missing argument !'); }
// Processing
const binPayload = convert.hexToUint8(_payload);
const payload = new Uint8Array(binPayload.buffer, 48);
const salt = new Uint8Array(binPayload.buffer, 0, 32);
const iv = new Uint8Array(binPayload.buffer, 32, 16);
const decoded = Crypto._decode(recipientPrivate, senderPublic, payload, iv, salt);
const decoded = Crypto._decode(recipientPrivate, senderPublic, payload, iv, salt, signSchema);
return decoded;
}

Expand Down
44 changes: 32 additions & 12 deletions src/core/crypto/KeyPair.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2018 NEM
* Copyright 2019 NEM
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -14,21 +14,25 @@
* limitations under the License.
*/
import { Convert as convert } from '../format';
import { SignSchema } from './SignSchema';
import * as Utility from './Utilities';

export class KeyPair {
/**
* Creates a key pair from a private key string.
* @param {string} privateKeyString A hex encoded private key string.
* @param {SignSchema} signSchema The Sign Schema. (KECCAK_REVERSED_KEY / SHA3)
* @returns {module:crypto/keyPair~KeyPair} The key pair.
*/
public static createKeyPairFromPrivateKeyString = (privateKeyString) => {
public static createKeyPairFromPrivateKeyString = (privateKeyString, signSchema = SignSchema.SHA3) => {
const privateKey = convert.hexToUint8(privateKeyString);

// KECCAK_REVERSED_KEY uses reversed private key.
const secretKey = signSchema === SignSchema.SHA3 ? privateKey : convert.hexToUint8Reverse(privateKeyString);
if (Utility.Key_Size !== privateKey.length) {
throw Error(`private key has unexpected size: ${privateKey.length}`);
}

const publicKey = Utility.catapult_crypto.extractPublicKey(privateKey, Utility.catapult_hash.func);
const publicKey = Utility.catapult_crypto.extractPublicKey(secretKey, Utility.catapult_hash.func, signSchema);
return {
privateKey,
publicKey,
Expand All @@ -39,36 +43,52 @@ export class KeyPair {
* Signs a data buffer with a key pair.
* @param {module:crypto/keyPair~KeyPair} keyPair The key pair to use for signing.
* @param {Uint8Array} data The data to sign.
* @param {SignSchema} signSchema The Sign Schema. (KECCAK_REVERSED_KEY / SHA3)
* @returns {Uint8Array} The signature.
*/
public static sign = (keyPair, data) => {
return Utility.catapult_crypto.sign(data, keyPair.publicKey, keyPair.privateKey, Utility.catapult_hash.createHasher());
public static sign = (keyPair, data, signSchema = SignSchema.SHA3) => {
let secretKey = keyPair.privateKey;
// KECCAK_REVERSED_KEY uses reversed private key.
if (signSchema === SignSchema.KECCAK_REVERSED_KEY) {
secretKey = convert.hexToUint8Reverse(convert.uint8ToHex(secretKey));
}
return Utility.catapult_crypto.sign(data, keyPair.publicKey, secretKey,
Utility.catapult_hash.createHasher(64, signSchema));
}

/**
* Verifies a signature.
* @param {module:crypto/keyPair~PublicKey} publicKey The public key to use for verification.
* @param {Uint8Array} data The data to verify.
* @param {Uint8Array} signature The signature to verify.
* @param {SignSchema} signSchema The Sign Schema. (KECCAK_REVERSED_KEY / SHA3)
* @returns {boolean} true if the signature is verifiable, false otherwise.
*/
public static verify = (publicKey, data, signature) => {
return Utility.catapult_crypto.verify(publicKey, data, signature, Utility.catapult_hash.createHasher());
public static verify = (publicKey, data, signature, signSchema = SignSchema.SHA3) => {
return Utility.catapult_crypto.verify(publicKey, data, signature, Utility.catapult_hash.createHasher(64, signSchema));
}

/**
* Creates a shared key given a key pair and an arbitrary public key.
* The shared key can be used for encrypted message passing between the two.
* @param {module:crypto/keyPair~KeyPair} keyPair The key pair for which to create the shared key.
* @param {module:crypto/keyPair~PublicKey} publicKey The public key for which to create the shared key.
* @param {Uint8Array} publicKey The public key for which to create the shared key.
* @param {Uint8Array} salt A salt that should be applied to the shared key.
* @param {SignSchema} signSchema The Sign Schema. (KECCAK_REVERSED_KEY / SHA3)
* @returns {Uint8Array} The shared key.
*/
public static deriveSharedKey = (keyPair, publicKey, salt) => {
public static deriveSharedKey = (keyPair, publicKey, salt, signSchema = SignSchema.SHA3) => {
if (Utility.Key_Size !== salt.length) {
throw Error(`salt has unexpected size: ${salt.length}`);
}

return Utility.catapult_crypto.deriveSharedKey(salt, keyPair.privateKey, publicKey, Utility.catapult_hash.func);
if (Utility.Key_Size !== publicKey.length) {
throw Error(`public key has unexpected size: ${salt.length}`);
}
let secretKey = keyPair.privateKey;
// KECCAK_REVERSED_KEY uses reversed private key.
if (signSchema === SignSchema.KECCAK_REVERSED_KEY) {
secretKey = convert.hexToUint8Reverse(convert.uint8ToHex(secretKey));
}
return Utility.catapult_crypto.deriveSharedKey(salt, secretKey, publicKey, Utility.catapult_hash.func, signSchema);
}
}
20 changes: 12 additions & 8 deletions src/core/crypto/SHA3Hasher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,35 @@
* limitations under the License.
*/

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

export class SHA3Hasher {
/**
* Calculates the hash of data.
* @param {Uint8Array} dest The computed hash destination.
* @param {Uint8Array} data The data to hash.
* @param {numeric} length The hash length in bytes.
* @param {SignSchema} signSchema The Sign Schema. (KECCAK_REVERSED_KEY / SHA3)
*/
public static func = (dest, data, length) => {
const hasher = SHA3Hasher.getHasher(length);
public static func = (dest, data, length, signSchema = SignSchema.SHA3) => {
const hasher = SHA3Hasher.getHasher(length, signSchema);
const hash = hasher.arrayBuffer(data);
array.copy(dest, array.uint8View(hash));
}

/**
* Creates a hasher object.
* @param {numeric} length The hash length in bytes.
* @param {SignSchema} signSchema The Sign Schema. (KECCAK_REVERSED_KEY / SHA3)
* @returns {object} The hasher.
*/
public static createHasher = (length = 64) => {
public static createHasher = (length = 64, signSchema = SignSchema.SHA3) => {
let hash;
return {
reset: () => {
hash = SHA3Hasher.getHasher(length).create();
hash = SHA3Hasher.getHasher(length, signSchema).create();
},
update: (data: any) => {
if (data instanceof Uint8Array) {
Expand All @@ -59,12 +62,13 @@ export class SHA3Hasher {
/**
* Get a hasher instance.
* @param {numeric} length The hash length in bytes.
* @param {SignSchema} signSchema The Sign Schema. (KECCAK_REVERSED_KEY / SHA3)
* @returns {object} The hasher.
*/
public static getHasher = (length = 64) => {
public static getHasher = (length = 64, signSchema = SignSchema.SHA3) => {
return {
32: sha3_256,
64: sha3_512,
32: signSchema === SignSchema.SHA3 ? sha3_256 : keccak256,
64: signSchema === SignSchema.SHA3 ? sha3_512 : keccak512 ,
} [length];
}
}
24 changes: 24 additions & 0 deletions src/core/crypto/SignSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright 2019 NEM
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* [KECCAK_REVERSED_KEY]: Keccak hash algorithm with reversed private keys.
* [SHA3]: SHA3 hash algorithm without key reversal
*/
export enum SignSchema {
KECCAK_REVERSED_KEY = 1,
SHA3 = 2,
}
20 changes: 9 additions & 11 deletions src/core/crypto/Utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import { RawArray as array } from '../format';
import * as nacl from './nacl_catapult';
import { SHA3Hasher as sha3Hasher } from './SHA3Hasher';

import { SignSchema } from './SignSchema';
export const CryptoJS = require('crypto-js');
export const Key_Size = 32;
export const Signature_Size = 64;
Expand Down Expand Up @@ -74,9 +74,9 @@ export const catapult_crypto = (function() {
d[31] |= 64;
}

function prepareForScalarMult(sk, hashfunc) {
function prepareForScalarMult(sk, hashfunc, signSchema: SignSchema) {
const d = new Uint8Array(Hash_Size);
hashfunc(d, sk);
hashfunc(d, sk, Hash_Size, signSchema);
clamp(d);
return d;
}
Expand Down Expand Up @@ -108,9 +108,9 @@ export const catapult_crypto = (function() {
})();

return {
extractPublicKey: (sk, hashfunc) => {
extractPublicKey: (sk, hashfunc, signSchema: SignSchema) => {
const c = nacl;
const d = prepareForScalarMult(sk, hashfunc);
const d = prepareForScalarMult(sk, hashfunc, signSchema);

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

deriveSharedKey: (salt, sk, pk, hashfunc) => {
deriveSharedKey: (salt, sk, pk, hashfunc, signSchema: SignSchema) => {
const c = nacl;
const d = prepareForScalarMult(sk, hashfunc);
const d = prepareForScalarMult(sk, hashfunc, signSchema);

// sharedKey = pack(p = d (derived from sk) * q (derived from pk))
const q = [c.gf(), c.gf(), c.gf(), c.gf()];
const p = [c.gf(), c.gf(), c.gf(), c.gf()];
const sharedKey = new Uint8Array(Key_Size);
c.unpackneg(q, pk);
c.unpack(q, pk);
c.scalarmult(p, q, d);
c.pack(sharedKey, p);

// salt the shared key
for (let i = 0; i < Key_Size; ++i) {
sharedKey[i] ^= salt[i];
}

// return the hash of the result
const sharedKeyHash = new Uint8Array(Key_Size);
hashfunc(sharedKeyHash, sharedKey, Key_Size);
hashfunc(sharedKeyHash, sharedKey, Key_Size, signSchema);
return sharedKeyHash;
},
};
Expand Down
1 change: 1 addition & 0 deletions src/core/crypto/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ export * from './Crypto';
export * from './KeyPair';
export * from './SHA3Hasher';
export * from './nacl_catapult';
export * from './SignSchema';
Loading