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
1 change: 0 additions & 1 deletion e2e/service/MosaicRestrictionTransactionService.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ describe('MosaicRestrictionTransactionService', () => {
UInt64.fromUint(1000),
networkType, helper.maxFee,
);
console.log(mosaicId.toHex());
const signedTransaction = mosaicDefinitionTransaction.signWith(account, generationHash);
return helper.announce(signedTransaction);
});
Expand Down
5 changes: 5 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,14 @@
"dependencies": {
"bluebird": "^3.5.5",
"catbuffer": "0.0.7",
"nem2-sdk-openapi-typescript-node-client": "0.7.20-beta.6",
"crypto-js": "^3.1.9-1",
"futoin-hkdf": "^1.3.1",
"js-joda": "^1.6.2",
"js-sha256": "^0.9.0",
"js-sha3": "^0.8.0",
"long": "^4.0.0",
"merkletreejs": "^0.1.7",
"nem2-sdk-openapi-typescript-node-client": "0.7.20-beta.6",
"request": "^2.88.0",
"request-promise-native": "^1.0.5",
"ripemd160": "^2.0.2",
Expand Down
21 changes: 8 additions & 13 deletions src/core/crypto/Crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,20 +228,19 @@ export class Crypto {
recipientPub: string,
msg: string,
iv: Uint8Array,
salt: Uint8Array,
signSchema: SignSchema): string => {
// Errors
if (!senderPriv || !recipientPub || !msg || !iv || !salt) { throw new Error('Missing argument !'); }
if (!senderPriv || !recipientPub || !msg || !iv) { throw new Error('Missing argument !'); }
// Processing
const keyPair = KeyPair.createKeyPairFromPrivateKeyString(senderPriv, signSchema);
const pk = convert.hexToUint8(recipientPub);
const encKey = utility.ua2words(KeyPair.deriveSharedKey(keyPair, pk, salt, signSchema), 32);
const encKey = utility.ua2words(KeyPair.deriveSharedKey(keyPair, pk, signSchema), 32);
const encIv = {
iv: utility.ua2words(iv, 16),
};
const encrypted = CryptoJS.AES.encrypt(CryptoJS.enc.Hex.parse(msg), encKey, encIv);
// Result
const result = convert.uint8ToHex(salt) + convert.uint8ToHex(iv) + CryptoJS.enc.Hex.stringify(encrypted.ciphertext);
const result = convert.uint8ToHex(iv) + CryptoJS.enc.Hex.stringify(encrypted.ciphertext);
return result;
}

Expand All @@ -264,8 +263,7 @@ export class Crypto {
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, isHexString ? msg : convert.utf8ToHex(msg), iv, salt, signSchema);
const encoded = Crypto._encode(senderPriv, recipientPub, isHexString ? msg : convert.utf8ToHex(msg), iv, signSchema);
// Result
return encoded;
}
Expand All @@ -277,22 +275,20 @@ export class Crypto {
* @param {string} senderPublic - A sender public key
* @param {Uint8Array} payload - An encrypted message payload in bytes
* @param {Uint8Array} iv - 16-byte AES initialization vector
* @param {Uint8Array} salt - 32-byte salt
* @param {SignSchema} signSchema The Sign Schema. (KECCAK(NIS1) / SHA3(Catapult))
* @return {string} - The decoded payload as hex
*/
public static _decode = (recipientPrivate: string,
senderPublic: string,
payload: Uint8Array,
iv: Uint8Array,
salt: Uint8Array,
signSchema: SignSchema): string => {
// Error
if (!recipientPrivate || !senderPublic || !payload) { throw new Error('Missing argument !'); }
// Processing
const keyPair = KeyPair.createKeyPairFromPrivateKeyString(recipientPrivate, signSchema);
const pk = convert.hexToUint8(senderPublic);
const encKey = utility.ua2words(KeyPair.deriveSharedKey(keyPair, pk, salt, signSchema), 32);
const encKey = utility.ua2words(KeyPair.deriveSharedKey(keyPair, pk, signSchema), 32);
const encIv = {
iv: utility.ua2words(iv, 16),
};
Expand Down Expand Up @@ -321,10 +317,9 @@ export class Crypto {
if (!recipientPrivate || !senderPublic || !payload) { throw new Error('Missing argument !'); }
// Processing
const binPayload = convert.hexToUint8(payload);
const payloadBuffer = 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, payloadBuffer, iv, salt, signSchema);
const payloadBuffer = new Uint8Array(binPayload.buffer, 16);
const iv = new Uint8Array(binPayload.buffer, 0, 16);
const decoded = Crypto._decode(recipientPrivate, senderPublic, payloadBuffer, iv, signSchema);
return decoded.toUpperCase();
}

Expand Down
17 changes: 6 additions & 11 deletions src/core/crypto/KeyPair.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { NetworkType } from '../../model/blockchain/NetworkType';
import { Convert as convert } from '../format';
import { SignSchema } from './SignSchema';
import * as Utility from './Utilities';
Expand Down Expand Up @@ -44,20 +43,20 @@ export class KeyPair {
* @param {SignSchema} signSchema The Sign Schema. (KECCAK(NIS1) / SHA3(Catapult))
* @returns {Uint8Array} The signature.
*/
public static sign = (keyPair, data: Uint8Array, signSchema: SignSchema) => {
public static sign = (keyPair, data: Uint8Array, signSchema: SignSchema): Uint8Array => {
return Utility.catapult_crypto.sign(data, keyPair.publicKey, keyPair.privateKey,
Utility.catapult_hash.createHasher(64, signSchema));
}

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

Expand All @@ -66,17 +65,13 @@ export class KeyPair {
* 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 {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(NIS1) / SHA3(Catapult))
* @returns {Uint8Array} The shared key.
*/
public static deriveSharedKey = (keyPair, publicKey: Uint8Array, salt: Uint8Array, signSchema: SignSchema) => {
if (Utility.Key_Size !== salt.length) {
throw Error(`salt has unexpected size: ${salt.length}`);
}
public static deriveSharedKey = (keyPair, publicKey: Uint8Array, signSchema: SignSchema) => {
if (Utility.Key_Size !== publicKey.length) {
throw Error(`public key has unexpected size: ${salt.length}`);
throw Error(`public key has unexpected size: ${publicKey.length}`);
}
return Utility.catapult_crypto.deriveSharedKey(salt, keyPair.privateKey, publicKey, Utility.catapult_hash.func, signSchema);
return Utility.catapult_crypto.deriveSharedKey(keyPair.privateKey, publicKey, Utility.catapult_hash.func, signSchema);
}
}
35 changes: 19 additions & 16 deletions src/core/crypto/Utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ 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;
export const Half_Signature_Size = Signature_Size / 2;
export const Hash_Size = 64;
export const Half_Hash_Size = Hash_Size / 2;
export const hkdf = require('futoin-hkdf');

/**
* Convert an Uint8Array to WordArray
Expand Down Expand Up @@ -68,7 +70,7 @@ export const catapult_hash = {
};

// custom catapult crypto functions
export const catapult_crypto = (function() {
export const catapult_crypto = (() => {
function clamp(d) {
d[0] &= 248;
d[31] &= 127;
Expand All @@ -82,7 +84,7 @@ export const catapult_crypto = (function() {
return d;
}

const encodedSChecker = (function() {
const encodedSChecker = (() => {
const Is_Reduced = 1;
const Is_Zero = 2;

Expand Down Expand Up @@ -202,25 +204,26 @@ export const catapult_crypto = (function() {
return 0 === c.crypto_verify_32(signature, 0, t, 0);
},

deriveSharedKey: (salt, sk, pk, hashfunc, signSchema: SignSchema) => {
deriveSharedKey: (privateKey: Uint8Array, publicKey: Uint8Array, hashfunc, signSchema: SignSchema): Uint8Array => {
const sharedSecret = catapult_crypto.deriveSharedSecret(privateKey, publicKey, hashfunc, signSchema);
const info = 'catapult';
const hash = 'SHA-256';
return hkdf(sharedSecret, 32, {salt: new Uint8Array(32), info, hash});
},

deriveSharedSecret: (privateKey: Uint8Array, publicKey: Uint8Array, hashfunc, signSchema: SignSchema): Uint8Array => {
const c = nacl;
const d = prepareForScalarMult(sk, hashfunc, signSchema);
const d = prepareForScalarMult(privateKey, hashfunc, signSchema);

// sharedKey = pack(p = d (derived from sk) * q (derived from pk))
// sharedKey = pack(p = d (derived from privateKey) * q (derived from publicKey))
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.unpack(q, pk);
const sharedSecret = new Uint8Array(Key_Size);

c.unpack(q, publicKey);
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, signSchema);
return sharedKeyHash;
c.pack(sharedSecret, p);
return sharedSecret;
},
};
})();
2 changes: 1 addition & 1 deletion src/core/format/Convert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export class Convert {
* @param {Uint8Array} input A uint8 array.
* @returns {string} A hex encoded string corresponding to the input.
*/
public static uint8ToHex = (input) => {
public static uint8ToHex = (input): string => {
let s = '';
for (const byte of input) {
s += utilities.Nibble_To_Char_Map[byte >> 4];
Expand Down
4 changes: 2 additions & 2 deletions src/model/message/MessageMarker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

export class MessageMarker {
/**
* 8-byte marker: FE CC 71 C7 64 BF E5 98 for PersistentDelegationRequestTransaction message
* 8-byte marker: FE2A8061577301E2 for PersistentDelegationRequestTransaction message
*/
public static readonly PersistentDelegationUnlock = 'FECC71C764BFE598';
public static readonly PersistentDelegationUnlock = 'FE2A8061577301E2';
}
17 changes: 8 additions & 9 deletions src/model/message/PersistentHarvestingDelegationMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@

import {Crypto, SHA3Hasher} from '../../core/crypto';
import { Convert } from '../../core/format/Convert';
import { Account } from '../account/Account';
import { NetworkType } from '../blockchain/NetworkType';
import { Message } from './Message';
import { MessageMarker } from './MessageMarker';
import { MessageType } from './MessageType';
import { PlainMessage } from './PlainMessage';

export class PersistentHarvestingDelegationMessage extends Message {
constructor(payload: string) {
Expand All @@ -33,18 +33,17 @@ export class PersistentHarvestingDelegationMessage extends Message {
/**
*
* @param delegatedPrivateKey - Private key of delegated account
* @param senderPrivateKey - Sender private key
* @param recipientPublicKey - Recipient public key
* @param {NetworkType} networkType - Catapult network type
* @return {PersistentHarvestingDelegationMessage}
*/
public static create(delegatedPrivateKey: string,
senderPrivateKey: string,
recipientPublicKey: string,
networkType: NetworkType): PersistentHarvestingDelegationMessage {
const signSchema = SHA3Hasher.resolveSignSchema(networkType);
const encrypted = MessageMarker.PersistentDelegationUnlock +
Crypto.encode(senderPrivateKey, recipientPublicKey, delegatedPrivateKey, signSchema, true).toUpperCase();
const ephemeralKeypair = Account.generateNewAccount(networkType);
const encrypted = MessageMarker.PersistentDelegationUnlock + ephemeralKeypair.publicKey +
Crypto.encode(ephemeralKeypair.privateKey, recipientPublicKey, delegatedPrivateKey, signSchema, true).toUpperCase();
return new PersistentHarvestingDelegationMessage(encrypted);
}

Expand All @@ -61,17 +60,17 @@ export class PersistentHarvestingDelegationMessage extends Message {
*
* @param encryptMessage - Encrypted message to be decrypted
* @param privateKey - Recipient private key
* @param senderPublicKey - Sender public key
* @param {NetworkType} networkType - Catapult network type
* @return {string}
*/
public static decrypt(encryptMessage: PersistentHarvestingDelegationMessage,
privateKey: string,
senderPublicKey: string,
networkType: NetworkType): string {
const signSchema = SHA3Hasher.resolveSignSchema(networkType);
const payload = encryptMessage.payload.substring(MessageMarker.PersistentDelegationUnlock.length);
const decrypted = Crypto.decode(privateKey, senderPublicKey, payload, signSchema);
const markerLength = MessageMarker.PersistentDelegationUnlock.length;
const ephemeralPublicKey = encryptMessage.payload.substring(markerLength, markerLength + 64);
const payload = encryptMessage.payload.substring(markerLength + ephemeralPublicKey.length);
const decrypted = Crypto.decode(privateKey, ephemeralPublicKey, payload, signSchema);
return decrypted.toUpperCase();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ export class PersistentDelegationRequestTransaction extends TransferTransaction
* @param deadline - The deadline to include the transaction.
* @param delegatedPrivateKey - The private key of delegated account
* @param recipientPublicKey - The recipient public key
* @param senderPrivateKey - The sender's private key
* @param networkType - The network type.
* @param maxFee - (Optional) Max fee defined by the sender
* @returns {TransferTransaction}
Expand All @@ -37,11 +36,10 @@ export class PersistentDelegationRequestTransaction extends TransferTransaction
deadline: Deadline,
delegatedPrivateKey: string,
recipientPublicKey: string,
senderPrivateKey: string,
networkType: NetworkType,
maxFee: UInt64 = new UInt64([0, 0])): PersistentDelegationRequestTransaction {
const message = PersistentHarvestingDelegationMessage
.create(delegatedPrivateKey, senderPrivateKey, recipientPublicKey, networkType);
.create(delegatedPrivateKey, recipientPublicKey, networkType);
return super.create(deadline, Address.createFromPublicKey(recipientPublicKey, networkType), [], message, networkType, maxFee);
}
}
1 change: 0 additions & 1 deletion src/service/MosaicRestrictionTransactionService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,6 @@ export class MosaicRestrictionTransactionService {
private getGlobalRestrictionEntry(mosaicId: MosaicId, restrictionKey: UInt64): Observable<MosaicGlobalRestrictionItem | undefined> {
return this.restrictionMosaicRepository.getMosaicGlobalRestriction(mosaicId).pipe(
map((mosaicRestriction: MosaicGlobalRestriction) => {
console.log(mosaicRestriction.restrictions);
return mosaicRestriction.restrictions.get(restrictionKey.toString());
}),
catchError((err: Error) => {
Expand Down
30 changes: 30 additions & 0 deletions test/core/crypto/hkdf.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 2020 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.
*/
import { expect } from 'chai';
import { Convert } from '../../../src/core/format';

const hkdf = require('futoin-hkdf');

describe('hkdf', () => {
describe('Example tests', () => {
// This test is used to validate that values are the same in both java and ts implementation.
const sharedSecret = 'string-or-buffer';
const hash = 'SHA-256';
const info = 'catapult';
const sharedKey = hkdf(sharedSecret, 32, {salt: new Uint8Array(32), info, hash});
expect(Convert.uint8ToHex(sharedKey)).equal('E618ACB2558E1721492E4AE3BED3F4D86F26C2B0CE6AD939943A6A540855D23F');
});
});
Loading