@hiveio/dhive 1.2.4 → 1.2.6-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ import * as ByteBuffer from 'bytebuffer';
2
+ export declare type Deserializer = (buffer: ByteBuffer) => void;
3
+ export declare const types: {
4
+ EncryptedMemoD: any;
5
+ };
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const ByteBuffer = require("bytebuffer");
4
+ const crypto_1 = require("../crypto");
5
+ const PublicKeyDeserializer = (buf) => {
6
+ const c = fixed_buf(buf, 33);
7
+ return crypto_1.PublicKey.fromBuffer(c);
8
+ };
9
+ const UInt64Deserializer = (b) => b.readUint64();
10
+ const UInt32Deserializer = (b) => b.readUint32();
11
+ const BinaryDeserializer = (b) => {
12
+ const len = b.readVarint32();
13
+ const b_copy = b.copy(b.offset, b.offset + len);
14
+ b.skip(len);
15
+ return Buffer.from(b_copy.toBinary(), 'binary');
16
+ };
17
+ const BufferDeserializer = (keyDeserializers) => (buf) => {
18
+ const obj = {};
19
+ for (const [key, deserializer] of keyDeserializers) {
20
+ try {
21
+ // Decodes a binary encoded string to a ByteBuffer.
22
+ buf = ByteBuffer.fromBinary(buf.toString('binary'), ByteBuffer.LITTLE_ENDIAN);
23
+ obj[key] = deserializer(buf);
24
+ }
25
+ catch (error) {
26
+ error.message = `${key}: ${error.message}`;
27
+ throw error;
28
+ }
29
+ }
30
+ return obj;
31
+ };
32
+ function fixed_buf(b, len) {
33
+ if (!b) {
34
+ throw Error('No buffer found on first parameter');
35
+ }
36
+ else {
37
+ const b_copy = b.copy(b.offset, b.offset + len);
38
+ b.skip(len);
39
+ return Buffer.from(b_copy.toBinary(), 'binary');
40
+ }
41
+ }
42
+ const EncryptedMemoDeserializer = BufferDeserializer([
43
+ ['from', PublicKeyDeserializer],
44
+ ['to', PublicKeyDeserializer],
45
+ ['nonce', UInt64Deserializer],
46
+ ['check', UInt32Deserializer],
47
+ ['encrypted', BinaryDeserializer]
48
+ ]);
49
+ exports.types = {
50
+ EncryptedMemoD: EncryptedMemoDeserializer
51
+ };
@@ -48,6 +48,9 @@ export declare const Types: {
48
48
  Binary: (size?: number | undefined) => (buffer: ByteBuffer, data: Buffer | HexBuffer) => void;
49
49
  Boolean: (buffer: ByteBuffer, data: boolean) => void;
50
50
  Date: (buffer: ByteBuffer, data: string) => void;
51
+ EncryptedMemo: (buffer: ByteBuffer, data: {
52
+ [key: string]: any;
53
+ }) => void;
51
54
  FlatMap: (keySerializer: Serializer, valueSerializer: Serializer) => (buffer: ByteBuffer, data: [any, any][]) => void;
52
55
  Int16: (buffer: ByteBuffer, data: number) => void;
53
56
  Int32: (buffer: ByteBuffer, data: number) => void;
@@ -530,6 +530,13 @@ const TransactionSerializer = ObjectSerializer([
530
530
  ['operations', ArraySerializer(OperationSerializer)],
531
531
  ['extensions', ArraySerializer(StringSerializer)]
532
532
  ]);
533
+ const EncryptedMemoSerializer = ObjectSerializer([
534
+ ['from', PublicKeySerializer],
535
+ ['to', PublicKeySerializer],
536
+ ['nonce', UInt64Serializer],
537
+ ['check', UInt32Serializer],
538
+ ['encrypted', BinarySerializer()]
539
+ ]);
533
540
  exports.Types = {
534
541
  Array: ArraySerializer,
535
542
  Asset: AssetSerializer,
@@ -537,6 +544,7 @@ exports.Types = {
537
544
  Binary: BinarySerializer,
538
545
  Boolean: BooleanSerializer,
539
546
  Date: DateSerializer,
547
+ EncryptedMemo: EncryptedMemoSerializer,
540
548
  FlatMap: FlatMapSerializer,
541
549
  Int16: Int16Serializer,
542
550
  Int32: Int32Serializer,
package/lib/crypto.d.ts CHANGED
@@ -33,6 +33,7 @@
33
33
  * in the design, construction, operation or maintenance of any military facility.
34
34
  */
35
35
  /// <reference types="node" />
36
+ import * as ByteBuffer from 'bytebuffer';
36
37
  import { SignedTransaction, Transaction } from './chain/transaction';
37
38
  /**
38
39
  * Network id used in WIF-encoding.
@@ -74,9 +75,13 @@ declare function isWif(privWif: string | Buffer): boolean;
74
75
  * ECDSA (secp256k1) public key.
75
76
  */
76
77
  export declare class PublicKey {
77
- readonly key: Buffer;
78
+ readonly key: any;
78
79
  readonly prefix: string;
79
- constructor(key: Buffer, prefix?: string);
80
+ readonly uncompressed: Buffer;
81
+ constructor(key: any, prefix?: string);
82
+ static fromBuffer(key: ByteBuffer): {
83
+ key: ByteBuffer;
84
+ };
80
85
  /**
81
86
  * Create a new instance from a WIF-encoded key.
82
87
  */
@@ -110,6 +115,7 @@ export declare type KeyRole = 'owner' | 'active' | 'posting' | 'memo';
110
115
  */
111
116
  export declare class PrivateKey {
112
117
  private key;
118
+ secret: Buffer;
113
119
  constructor(key: Buffer);
114
120
  /**
115
121
  * Convenience to create a new instance from WIF string or buffer.
@@ -127,6 +133,7 @@ export declare class PrivateKey {
127
133
  * Create key from username and password.
128
134
  */
129
135
  static fromLogin(username: string, password: string, role?: KeyRole): PrivateKey;
136
+ multiply(pub: any): Buffer;
130
137
  /**
131
138
  * Sign message.
132
139
  * @param message 32-byte message.
@@ -145,6 +152,10 @@ export declare class PrivateKey {
145
152
  * to get the full encoded key you need to explicitly call {@link toString}.
146
153
  */
147
154
  inspect(): string;
155
+ /**
156
+ * Get shared secret for memo cryptography
157
+ */
158
+ get_shared_secret(public_key: PublicKey): Buffer;
148
159
  }
149
160
  /**
150
161
  * ECDSA (secp256k1) signature.
package/lib/crypto.js CHANGED
@@ -35,15 +35,21 @@
35
35
  */
36
36
  Object.defineProperty(exports, "__esModule", { value: true });
37
37
  const assert = require("assert");
38
+ const crypto_1 = require("crypto");
39
+ const bigInteger = require("bigi");
38
40
  const bs58 = require("bs58");
39
41
  const ByteBuffer = require("bytebuffer");
40
- const crypto_1 = require("crypto");
42
+ const ecurve = require("ecurve");
41
43
  const Ripemd160 = require("ripemd160");
42
44
  const secp256k1 = require("secp256k1");
43
45
  const verror_1 = require("verror");
44
46
  const serializer_1 = require("./chain/serializer");
45
47
  const client_1 = require("./client");
46
48
  const utils_1 = require("./utils");
49
+ /**
50
+ * secp256k1 ecurve
51
+ */
52
+ const secp256k1Curve = ecurve.getCurveByName('secp256k1');
47
53
  /**
48
54
  * Network id used in WIF-encoding.
49
55
  */
@@ -64,6 +70,14 @@ function sha256(input) {
64
70
  .update(input)
65
71
  .digest();
66
72
  }
73
+ /**
74
+ * Return sha512 hash of input
75
+ */
76
+ function sha512(input) {
77
+ return crypto_1.createHash('sha512')
78
+ .update(input)
79
+ .digest();
80
+ }
67
81
  /**
68
82
  * Return 2-round sha256 hash of input.
69
83
  */
@@ -145,6 +159,11 @@ class PublicKey {
145
159
  this.key = key;
146
160
  this.prefix = prefix;
147
161
  assert(secp256k1.publicKeyVerify(key), 'invalid public key');
162
+ this.uncompressed = Buffer.from(secp256k1.publicKeyConvert(key, false));
163
+ }
164
+ static fromBuffer(key) {
165
+ assert(secp256k1.publicKeyVerify(key), 'invalid buffer as public key');
166
+ return { key };
148
167
  }
149
168
  /**
150
169
  * Create a new instance from a WIF-encoded key.
@@ -230,6 +249,9 @@ class PrivateKey {
230
249
  const seed = username + role + password;
231
250
  return PrivateKey.fromSeed(seed);
232
251
  }
252
+ multiply(pub) {
253
+ return Buffer.from(secp256k1.publicKeyTweakMul(pub.key, this.secret, false));
254
+ }
233
255
  /**
234
256
  * Sign message.
235
257
  * @param message 32-byte message.
@@ -265,6 +287,15 @@ class PrivateKey {
265
287
  const key = this.toString();
266
288
  return `PrivateKey: ${key.slice(0, 6)}...${key.slice(-6)}`;
267
289
  }
290
+ /**
291
+ * Get shared secret for memo cryptography
292
+ */
293
+ get_shared_secret(public_key) {
294
+ const KBP = ecurve.Point.fromAffine(secp256k1Curve, bigInteger.fromBuffer(public_key.uncompressed.slice(1, 33)), bigInteger.fromBuffer(public_key.uncompressed.slice(33, 65)));
295
+ const P = KBP.multiply(bigInteger.fromBuffer(this.key));
296
+ const S = P.affineX.toBuffer({ size: 32 });
297
+ return sha512(S);
298
+ }
268
299
  }
269
300
  exports.PrivateKey = PrivateKey;
270
301
  /**
@@ -0,0 +1,4 @@
1
+ /// <reference types="node" />
2
+ import { PrivateKey, PublicKey } from '../crypto';
3
+ export declare const encrypt: (private_key: PrivateKey, public_key: PublicKey, message: Buffer, nonce?: string) => any;
4
+ export declare const decrypt: (private_key: PrivateKey, public_key: PublicKey, nonce: any, message: any, checksum: number) => any;
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const assert = require("assert");
4
+ const crypto_1 = require("crypto");
5
+ const ByteBuffer = require('bytebuffer');
6
+ const Long = ByteBuffer.Long;
7
+ exports.encrypt = (private_key, public_key, message, nonce = uniqueNonce()) => crypt(private_key, public_key, nonce, message);
8
+ exports.decrypt = (private_key, public_key, nonce, message, checksum) => crypt(private_key, public_key, nonce, message, checksum).message;
9
+ /**
10
+ * @arg {Buffer} message - Encrypted or plain text message (see checksum)
11
+ * @arg {number} checksum - shared secret checksum (null to encrypt, non-null to decrypt)
12
+ */
13
+ const crypt = (private_key, public_key, nonce, message, checksum) => {
14
+ const nonceL = toLongObj(nonce);
15
+ // const bufferMessage = Buffer.from(messageString, 'binary')
16
+ const S = private_key.get_shared_secret(public_key);
17
+ let ebuf = new ByteBuffer(ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN);
18
+ ebuf.writeUint64(nonceL);
19
+ ebuf.append(S.toString('binary'), 'binary');
20
+ ebuf = Buffer.from(ebuf.copy(0, ebuf.offset).toBinary(), 'binary');
21
+ const encryption_key = crypto_1.createHash('sha512').update(ebuf).digest();
22
+ const iv = encryption_key.slice(32, 48);
23
+ const tag = encryption_key.slice(0, 32);
24
+ // check if first 64 bit of sha256 hash treated as uint64_t truncated to 32 bits.
25
+ const check = crypto_1.createHash('sha256').update(encryption_key).digest().slice(0, 4);
26
+ const cbuf = ByteBuffer.fromBinary(check.toString('binary'), ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN);
27
+ ByteBuffer.fromBinary(check.toString('binary'), ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN);
28
+ const check32 = cbuf.readUint32();
29
+ if (checksum) {
30
+ if (check32 !== checksum) {
31
+ throw new Error('Invalid key');
32
+ }
33
+ message = cryptoJsDecrypt(message, tag, iv);
34
+ }
35
+ else {
36
+ message = cryptoJsEncrypt(message, tag, iv);
37
+ }
38
+ return { nonce: nonceL, message, checksum: check32 };
39
+ };
40
+ /**
41
+ * This method does not use a checksum, the returned data must be validated some other way.
42
+ * @arg {string|Buffer} ciphertext - binary format
43
+ * @return {Buffer} the decrypted message
44
+ */
45
+ const cryptoJsDecrypt = (message, tag, iv) => {
46
+ assert(message, 'Missing cipher text');
47
+ let messageBuffer = message;
48
+ const decipher = crypto_1.createDecipheriv('aes-256-cbc', tag, iv);
49
+ messageBuffer = Buffer.concat([decipher.update(messageBuffer), decipher.final()]);
50
+ return messageBuffer;
51
+ };
52
+ /**
53
+ * This method does not use a checksum, the returned data must be validated some other way.
54
+ * @arg {string|Buffer} plaintext - binary format
55
+ * @return {Buffer} binary
56
+ */
57
+ const cryptoJsEncrypt = (message, tag, iv) => {
58
+ assert(message, 'Missing plain text');
59
+ let messageBuffer = message;
60
+ const cipher = crypto_1.createCipheriv('aes-256-cbc', tag, iv);
61
+ messageBuffer = Buffer.concat([cipher.update(messageBuffer), cipher.final()]);
62
+ return messageBuffer;
63
+ };
64
+ /** @return {string} unique 64 bit unsigned number string. Being time based,
65
+ * this is careful to never choose the same nonce twice. This value could
66
+ * clsbe recorded in the blockchain for a long time.
67
+ */
68
+ let unique_nonce_entropy = null;
69
+ const uniqueNonce = () => {
70
+ if (unique_nonce_entropy === null) {
71
+ const uint8randomArr = new Uint8Array(2);
72
+ for (let i = 0; i < 2; ++i) {
73
+ uint8randomArr[i] = crypto_1.randomBytes(2).readUInt8(i);
74
+ }
75
+ unique_nonce_entropy = Math.round((uint8randomArr[0] << 8) | uint8randomArr[1]);
76
+ }
77
+ let long = Long.fromNumber(Date.now());
78
+ const entropy = ++unique_nonce_entropy % 0xffff;
79
+ long = long.shiftLeft(16).or(Long.fromNumber(entropy));
80
+ return long.toString();
81
+ };
82
+ const toLongObj = (o) => (o ? (Long.isLong(o) ? o : Long.fromString(o)) : o);
package/lib/index.d.ts CHANGED
@@ -47,5 +47,7 @@ export * from './chain/misc';
47
47
  export * from './chain/operation';
48
48
  export * from './chain/serializer';
49
49
  export * from './chain/transaction';
50
+ export * from './chain/hivemind';
50
51
  export * from './client';
51
52
  export * from './crypto';
53
+ export * from './memo';
package/lib/index.js CHANGED
@@ -50,3 +50,4 @@ __export(require("./chain/misc"));
50
50
  __export(require("./chain/serializer"));
51
51
  __export(require("./client"));
52
52
  __export(require("./crypto"));
53
+ __export(require("./memo"));
package/lib/memo.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { PrivateKey, PublicKey } from './crypto';
2
+ export declare const Memo: {
3
+ decode: (private_key: string | PrivateKey, memo: string) => string;
4
+ encode: (private_key: string | PrivateKey, public_key: string | PublicKey, memo: string, testNonce?: string | undefined) => string;
5
+ };
package/lib/memo.js ADDED
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const bs58 = require("bs58");
4
+ const ByteBuffer = require("bytebuffer");
5
+ const deserializer_1 = require("./chain/deserializer");
6
+ const serializer_1 = require("./chain/serializer");
7
+ const crypto_1 = require("./crypto");
8
+ const Aes = require("./helpers/aes");
9
+ /**
10
+ * Memo/Any message encoding using AES (aes-cbc algorithm)
11
+ * @param {Buffer|string} private_key Private memo key of sender
12
+ * @param {Buffer|string} public_key public memo key of recipient
13
+ * @param {string} memo message to be encrypted
14
+ * @param {number} testNonce nonce with high entropy
15
+ */
16
+ const encode = (private_key, public_key, memo, testNonce) => {
17
+ if (!memo.startsWith('#')) {
18
+ return memo;
19
+ }
20
+ memo = memo.substring(1);
21
+ checkEncryption();
22
+ private_key = toPrivateObj(private_key);
23
+ public_key = toPublicObj(public_key);
24
+ const mbuf = new ByteBuffer(ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN);
25
+ mbuf.writeVString(memo);
26
+ const memoBuffer = Buffer.from(mbuf.copy(0, mbuf.offset).toBinary(), 'binary');
27
+ const { nonce, message, checksum } = Aes.encrypt(private_key, public_key, memoBuffer, testNonce);
28
+ const mbuf2 = new ByteBuffer(ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN);
29
+ serializer_1.Types.EncryptedMemo(mbuf2, {
30
+ check: checksum,
31
+ encrypted: message,
32
+ from: private_key.createPublic(),
33
+ nonce,
34
+ to: public_key
35
+ });
36
+ mbuf2.flip();
37
+ const data = Buffer.from(mbuf2.toBuffer());
38
+ return '#' + bs58.encode(data);
39
+ };
40
+ /**
41
+ * Encrypted memo/message decryption
42
+ * @param {PrivateKey|string} private_key Private memo key of recipient
43
+ * @param {string}memo Encrypted message/memo
44
+ */
45
+ const decode = (private_key, memo) => {
46
+ if (!memo.startsWith('#')) {
47
+ return memo;
48
+ }
49
+ memo = memo.substring(1);
50
+ checkEncryption();
51
+ private_key = toPrivateObj(private_key);
52
+ memo = bs58.decode(memo);
53
+ let memoBuffer = deserializer_1.types.EncryptedMemoD(Buffer.from(memo, 'binary'));
54
+ const { from, to, nonce, check, encrypted } = memoBuffer;
55
+ const pubkey = private_key.createPublic().toString();
56
+ const otherpub = pubkey === new crypto_1.PublicKey(from.key).toString()
57
+ ? new crypto_1.PublicKey(to.key)
58
+ : new crypto_1.PublicKey(from.key);
59
+ memoBuffer = Aes.decrypt(private_key, otherpub, nonce, encrypted, check);
60
+ const mbuf = ByteBuffer.fromBinary(memoBuffer.toString('binary'), ByteBuffer.LITTLE_ENDIAN);
61
+ try {
62
+ mbuf.mark();
63
+ return '#' + mbuf.readVString();
64
+ }
65
+ catch (e) {
66
+ mbuf.reset();
67
+ // Sender did not length-prefix the memo
68
+ memo = Buffer.from(mbuf.toString('binary'), 'binary').toString('utf-8');
69
+ return '#' + memo;
70
+ }
71
+ };
72
+ let encodeTest;
73
+ const checkEncryption = () => {
74
+ if (encodeTest === undefined) {
75
+ let plaintext;
76
+ encodeTest = true; // prevent infinate looping
77
+ try {
78
+ const wif = '5JdeC9P7Pbd1uGdFVEsJ41EkEnADbbHGq6p1BwFxm6txNBsQnsw';
79
+ const pubkey = 'STM8m5UgaFAAYQRuaNejYdS8FVLVp9Ss3K1qAVk5de6F8s3HnVbvA';
80
+ const cyphertext = encode(wif, pubkey, '#memo爱');
81
+ plaintext = decode(wif, cyphertext);
82
+ }
83
+ catch (e) {
84
+ throw new Error(e);
85
+ }
86
+ finally {
87
+ encodeTest = plaintext === '#memo爱';
88
+ }
89
+ }
90
+ if (encodeTest === false) {
91
+ throw new Error('This environment does not support encryption.');
92
+ }
93
+ };
94
+ const toPrivateObj = (o) => o ? (o.key ? o : crypto_1.PrivateKey.fromString(o)) : o; /* null or undefined*/
95
+ const toPublicObj = (o) => o ? (o.key ? o : crypto_1.PublicKey.fromString(o)) : o; /* null or undefined*/
96
+ exports.Memo = {
97
+ decode,
98
+ encode
99
+ };
package/lib/utils.d.ts CHANGED
@@ -71,7 +71,7 @@ export interface WitnessProps {
71
71
  hbd_interest_rate?: number;
72
72
  url?: string;
73
73
  }
74
- export declare function buildWitnessUpdateOp(owner: string, props: WitnessProps): WitnessSetPropertiesOperation;
74
+ export declare const buildWitnessUpdateOp: (owner: string, props: WitnessProps) => WitnessSetPropertiesOperation;
75
75
  export declare const operationOrders: {
76
76
  vote: number;
77
77
  comment: number;
package/lib/utils.js CHANGED
@@ -182,15 +182,15 @@ const failover = (url, urls, currentAddress, consoleOnFailover) => {
182
182
  // Can hopefully be removed when hived's JSON representation is fixed
183
183
  const ByteBuffer = require("bytebuffer");
184
184
  const serializer_1 = require("./chain/serializer");
185
- function serialize(serializer, data) {
185
+ const serialize = (serializer, data) => {
186
186
  const buffer = new ByteBuffer(ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN);
187
187
  serializer(buffer, data);
188
188
  buffer.flip();
189
189
  // `props` values must be hex
190
190
  return buffer.toString('hex');
191
191
  // return Buffer.from(buffer.toBuffer());
192
- }
193
- function buildWitnessUpdateOp(owner, props) {
192
+ };
193
+ exports.buildWitnessUpdateOp = (owner, props) => {
194
194
  const data = {
195
195
  extensions: [],
196
196
  owner,
@@ -227,8 +227,7 @@ function buildWitnessUpdateOp(owner, props) {
227
227
  }
228
228
  data.props.sort((a, b) => a[0].localeCompare(b[0]));
229
229
  return ['witness_set_properties', data];
230
- }
231
- exports.buildWitnessUpdateOp = buildWitnessUpdateOp;
230
+ };
232
231
  const JSBI = require('jsbi');
233
232
  exports.operationOrders = {
234
233
  vote: 0,
package/lib/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.default = '1.2.3';
3
+ exports.default = '1.2.6';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hiveio/dhive",
3
- "version": "1.2.4",
3
+ "version": "1.2.6-rc.0",
4
4
  "description": "Hive blockchain RPC client library",
5
5
  "author": "hive-network",
6
6
  "license": "BSD-3-Clause-No-Military-License",
@@ -32,10 +32,12 @@
32
32
  "dhive"
33
33
  ],
34
34
  "dependencies": {
35
+ "bigi": "^1.4.2",
35
36
  "bs58": "^4.0.1",
36
37
  "bytebuffer": "^5.0.1",
37
38
  "core-js": "^3.6.4",
38
39
  "cross-fetch": "^3.0.4",
40
+ "ecurve": "^1.0.6",
39
41
  "https": "^1.0.0",
40
42
  "jsbi": "^3.1.4",
41
43
  "node-fetch": "^2.6.0",