diff --git a/package-lock.json b/package-lock.json index fa7fa09..164ccf8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1959,6 +1959,43 @@ "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", "dev": true }, + "axios": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz", + "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==", + "requires": { + "follow-redirects": "1.5.10", + "is-buffer": "^2.0.2" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "requires": { + "debug": "=3.1.0" + } + }, + "is-buffer": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", + "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -2139,6 +2176,14 @@ } } }, + "base-x": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.6.tgz", + "integrity": "sha512-4PaF8u2+AlViJxRVjurkLTxpp7CaFRD/jo5rPT9ONnKxyhQ8f59yzamEvq7EkriG56yn5On4ONyaG75HLqr46w==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "base64-js": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", @@ -2178,6 +2223,11 @@ "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true }, + "bigi": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/bigi/-/bigi-1.4.2.tgz", + "integrity": "sha1-nGZalfiLiwj8Bc/XMfVhhZ1yWCU=" + }, "binary-extensions": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", @@ -2392,6 +2442,14 @@ "node-releases": "^1.1.25" } }, + "bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=", + "requires": { + "base-x": "^3.0.2" + } + }, "buffer": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", @@ -2427,6 +2485,14 @@ "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", "dev": true }, + "bytebuffer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz", + "integrity": "sha1-WC7qSxqHO20CCkjVjfhfC7ps/d0=", + "requires": { + "long": "~3" + } + }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -2654,7 +2720,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, "requires": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -3223,7 +3288,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, "requires": { "cipher-base": "^1.0.1", "inherits": "^2.0.1", @@ -3236,7 +3300,6 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, "requires": { "cipher-base": "^1.0.3", "create-hash": "^1.1.0", @@ -3278,6 +3341,11 @@ "randomfill": "^1.0.3" } }, + "crypto-js": { + "version": "3.1.9-1", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.9-1.tgz", + "integrity": "sha1-/aGedh/Ad+Af+/3G6f38WeiAbNg=" + }, "css-color-names": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", @@ -3591,8 +3659,7 @@ "deep-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", - "dev": true + "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=" }, "deep-is": { "version": "0.1.3", @@ -3984,6 +4051,15 @@ "safer-buffer": "^2.1.0" } }, + "ecurve": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/ecurve/-/ecurve-1.0.6.tgz", + "integrity": "sha512-/BzEjNfiSuB7jIWKcS/z8FK9jNjmEWvUV2YZ4RLSmcDtP7Lq0m6FvDuSnJpBlDpGRpfRQeTLGLBI8H+kEv0r+w==", + "requires": { + "bigi": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -6023,7 +6099,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", - "dev": true, "requires": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -6453,8 +6528,7 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "inquirer": { "version": "3.3.0", @@ -7128,8 +7202,7 @@ "lodash": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" }, "lodash.defaultsdeep": { "version": "4.6.1", @@ -7182,6 +7255,11 @@ "integrity": "sha512-LoEDv5pgpvWgPF4kNYuIp0qqSJVWak/dML0RY74xlzMZiT9w77teNAwKYKWBTYjlokMirg+o3jBwp+vlLrcfAA==", "dev": true }, + "long": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", + "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=" + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -7244,7 +7322,6 @@ "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dev": true, "requires": { "hash-base": "^3.0.0", "inherits": "^2.0.1", @@ -9456,7 +9533,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, "requires": { "hash-base": "^3.0.0", "inherits": "^2.0.1" @@ -9509,8 +9585,7 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "safe-regex": { "version": "1.1.0", @@ -9765,7 +9840,6 @@ "version": "2.4.11", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, "requires": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" diff --git a/package.json b/package.json index e0bbe3c..e976f5e 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,17 @@ "core-js": "^2.6.5", "vue": "^2.6.10", "vuetify": "^2.0.0", - "lodash": "^4.17.14" + "lodash": "^4.17.14", + "axios": "^0.19.0", + + "bigi": "^1.4.2", + "bs58": "^4.0.1", + "bytebuffer": "^5.0.1", + "create-hash": "^1.1.3", + "create-hmac": "^1.1.6", + "crypto-js": "^3.1.9-1", + "deep-equal": "^1.0.1", + "ecurve": "^1.0.5" }, "devDependencies": { "@mdi/font": "^4.1.95", diff --git a/public/chart.svg b/public/chart.svg new file mode 100644 index 0000000..c3d8274 --- /dev/null +++ b/public/chart.svg @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + 12:00 + + + 12:05 + + + 12:10 + + + 12:15 + + + 12:20 + + + 12:25 + + + 12:30 + + + + + 2000.0 + + + 2010.0 + + + 2020.0 + + + 2030.0 + + + 2040.0 + + + 2050.0 + + + + + + + + + + + 2028.5 + + + + + + + + + 2006.7 + + + + + + + + 2047.4 + + + + + + diff --git a/public/index.html b/public/index.html index c2953fb..163525c 100644 --- a/public/index.html +++ b/public/index.html @@ -15,5 +15,9 @@
+ + + + diff --git a/public/price.png b/public/price.png new file mode 100644 index 0000000..a97ac76 Binary files /dev/null and b/public/price.png differ diff --git a/src/components/HelloWorld.vue b/src/components/HelloWorld.vue index 30dab1c..1a42a77 100644 --- a/src/components/HelloWorld.vue +++ b/src/components/HelloWorld.vue @@ -12,24 +12,24 @@ - + -
BBB-全网门槛最低合约产品
-
低手续费、低合约单价
下载即用,操作简单
+
BBB-首个去中心化数字资产合约产品
+
低手续费、透明公平
杠杆交易,规则简单
- - + + mdi-eye-outline + > + + @@ -93,13 +98,86 @@ - + +
- + + + + +
+ 所有交易上链公开透明
+ 杜绝人为操作价格 +
+
+ + + + + + + + + +
不同杠杆产品
+ 1USDT即可交易 + +
+
+ + + + + + + +
+ 详尽指标展示
+ 轻松掌握平仓时机 +
+
+ + + + + + + +
+ +
+ @@ -113,78 +191,141 @@ no-gutters > - {{ link }} + {{item.title}} + + + + + 联系我们 + + diff --git a/src/ecc/src/AccountLogin.js b/src/ecc/src/AccountLogin.js new file mode 100644 index 0000000..37d82c9 --- /dev/null +++ b/src/ecc/src/AccountLogin.js @@ -0,0 +1,45 @@ +const PrivateKey = require("./PrivateKey"); + +const normalize_brainKey = function (brainKey) { + if (!(typeof brainKey === 'string')) { + throw new Error("string required for brainKey"); + } + + brainKey = brainKey.trim(); + return brainKey.split(/[\t\n\v\f\r ]+/).join(' '); +} + +const generateKeys = function (accountName, password, role = "active") { + + if (!accountName || !password) { + throw new Error("Account name or password required"); + } + if (password.length < 12) { + throw new Error("Password must have at least 12 characters"); + } + + let seed = accountName + role + password; + return PrivateKey.fromSeed(normalize_brainKey(seed)); + +} + +// const checkKeys = function ({accountName, password, auths}) { +// if (!accountName || !password || !auths) { +// throw new Error("checkKeys: Missing inputs"); +// } +// let hasKey = false; +// +// for (let role in auths) { +// let {privKeys, pubKeys} = this.generateKeys(accountName, password, [role]); +// auths[role].forEach(key => { +// if (key[0] === pubKeys[role]) { +// hasKey = true; +// this.set(role, {priv: privKeys[role], pub: pubKeys[role]}); +// } +// }); +// }; +// +// return hasKey; +// } + +exports.generateKeys = generateKeys; diff --git a/src/ecc/src/BrainKey.js b/src/ecc/src/BrainKey.js new file mode 100644 index 0000000..af60405 --- /dev/null +++ b/src/ecc/src/BrainKey.js @@ -0,0 +1,8 @@ + +export default function normalize(brainKey) { + if (typeof brainKey !== 'string') { + throw new Error("string required for brainKey"); + } + brainKey = brainKey.trim(); + return brainKey.split(/[\t\n\v\f\r ]+/).join(' '); +} diff --git a/src/ecc/src/PrivateKey.js b/src/ecc/src/PrivateKey.js new file mode 100644 index 0000000..5385256 --- /dev/null +++ b/src/ecc/src/PrivateKey.js @@ -0,0 +1,165 @@ +const ecurve = require('ecurve'); +const {Point, getCurveByName} = require('ecurve'); +const BigInteger = require('bigi'); +const {encode, decode} = require('bs58'); +const { sha256, sha512 } = require('./hash'); +const PublicKey = require('./PublicKey'); +const deepEqual = require("deep-equal"); +const assert = require("assert"); + +const secp256k1 = getCurveByName('secp256k1'); +const {G, n} = secp256k1; + +class PrivateKey { + + /** + @private see static functions + @param {BigInteger} + */ + constructor(d) { this.d = d; } + + static fromBuffer(buf) { + if (!Buffer.isBuffer(buf)) { + throw new Error("Expecting paramter to be a Buffer type"); + } + if (32 !== buf.length) { + console.log(`WARN: Expecting 32 bytes, instead got ${buf.length}, stack trace:`, new Error().stack); + } + if (buf.length === 0) { + throw new Error("Empty buffer"); + } + return new PrivateKey(BigInteger.fromBuffer(buf)); + } + + /** @arg {string} seed - any length string. This is private, the same seed produces the same private key every time. */ + static fromSeed(seed) { // generate_private_key + if (!(typeof seed === 'string')) { + throw new Error('seed must be of type string'); + } + return PrivateKey.fromBuffer(sha256(seed)); + } + + /** @return {string} Wallet Import Format (still a secret, Not encrypted) */ + static fromWif(_private_wif) { + var private_wif = new Buffer(decode(_private_wif)); + var version = private_wif.readUInt8(0); + assert.equal(0x80, version, `Expected version ${0x80}, instead got ${version}`); + // checksum includes the version + var private_key = private_wif.slice(0, -4); + var checksum = private_wif.slice(-4); + var new_checksum = sha256(private_key); + new_checksum = sha256(new_checksum); + new_checksum = new_checksum.slice(0, 4); + var isEqual = deepEqual(checksum, new_checksum); //, 'Invalid checksum' + if (!isEqual) { + throw new Error("Checksum did not match"); + } + private_key = private_key.slice(1); + return PrivateKey.fromBuffer(private_key); + } + + toWif() { + var private_key = this.toBuffer(); + // checksum includes the version + private_key = Buffer.concat([new Buffer([0x80]), private_key]); + var checksum = sha256(private_key); + checksum = sha256(checksum); + checksum = checksum.slice(0, 4); + var private_wif = Buffer.concat([private_key, checksum]); + return encode(private_wif); + } + + /** + @return {Point} + */ + toPublicKeyPoint() { + var Q; + return Q = secp256k1.G.multiply(this.d); + } + + toPublicKey() { + if (this.public_key) { return this.public_key; } + return this.public_key = PublicKey.fromPoint(this.toPublicKeyPoint()); + } + + toBuffer() { + return this.d.toBuffer(32); + } + + + /** ECIES */ + get_shared_secret(public_key, legacy = false) { + public_key = toPublic(public_key) + let KB = public_key.toUncompressed().toBuffer() + let KBP = Point.fromAffine( + secp256k1, + BigInteger.fromBuffer( KB.slice( 1,33 )), // x + BigInteger.fromBuffer( KB.slice( 33,65 )) // y + ) + let r = this.toBuffer() + let P = KBP.multiply(BigInteger.fromBuffer(r)) + let S = P.affineX.toBuffer({size: 32}) + /* + the input to sha512 must be exactly 32-bytes, to match the c++ implementation + of get_shared_secret. Right now S will be shorter if the most significant + byte(s) is zero. Pad it back to the full 32-bytes + */ + if (!legacy && S.length < 32) { + let pad = new Buffer(32 - S.length).fill(0); + S = Buffer.concat([pad, S]); + } + + // SHA512 used in ECIES + return sha512(S) + } + + // /** ECIES (does not always match the Point.fromAffine version above) */ + // get_shared_secret(public_key){ + // public_key = toPublic(public_key) + // var P = public_key.Q.multiply( this.d ); + // var S = P.affineX.toBuffer({size: 32}); + // // ECIES, adds an extra sha512 + // return sha512(S); + // } + + /** @throws {Error} - overflow of the key could not be derived */ + child( offset ) { + offset = Buffer.concat([ this.toPublicKey().toBuffer(), offset ]) + offset = sha256( offset ) + let c = BigInteger.fromBuffer(offset) + + if (c.compareTo(n) >= 0) + throw new Error("Child offset went out of bounds, try again") + + let derived = this.d.add(c)//.mod(n) + + if( derived.signum() === 0 ) + throw new Error("Child offset derived to an invalid key, try again") + + return new PrivateKey( derived ) + } + + /* */ + + toByteBuffer() { + var b = new ByteBuffer(ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN); + this.appendByteBuffer(b); + return b.copy(0, b.offset); + } + + static fromHex(hex) { + return PrivateKey.fromBuffer(new Buffer(hex, 'hex')); + } + + toHex() { + return this.toBuffer().toString('hex'); + } + + /* */ +} + +module.exports = PrivateKey; +// export default PrivateKey; + +let toPublic = data => data == null ? data : + data.Q ? data : PublicKey.fromStringOrThrow(data) diff --git a/src/ecc/src/PublicKey.js b/src/ecc/src/PublicKey.js new file mode 100644 index 0000000..7541e74 --- /dev/null +++ b/src/ecc/src/PublicKey.js @@ -0,0 +1,175 @@ +// const {ChainConfig} = require('cybexjs-ws'); + +// const ecurve = require('ecurve'); +const {Point, getCurveByName} = require('ecurve'); +const BigInteger = require('bigi'); +const { encode, decode } = require('bs58'); +const { sha256, sha512, ripemd160 } = require('./hash'); +const deepEqual = require("deep-equal"); +const assert = require("assert"); + +const secp256k1 = getCurveByName('secp256k1'); +const {G, n} = secp256k1; + +class PublicKey { + + /** @param {Point} public key */ + constructor(Q) { this.Q = Q; } + + static fromBinary(bin) { + return PublicKey.fromBuffer(new Buffer(bin, 'binary')); + } + + static fromBuffer(buffer) { + if (buffer.toString('hex') === '000000000000000000000000000000000000000000000000000000000000000000') + return new PublicKey(null); + return new PublicKey(Point.decodeFrom(secp256k1, buffer)); + } + + toBuffer(compressed = this.Q? this.Q.compressed : null) { + if (this.Q === null) + return new Buffer('000000000000000000000000000000000000000000000000000000000000000000', 'hex'); + return this.Q.getEncoded(compressed); + } + + static fromPoint(point) { + return new PublicKey(point); + } + + toUncompressed() { + var buf = this.Q.getEncoded(false); + var point = Point.decodeFrom(secp256k1, buf); + return PublicKey.fromPoint(point); + } + + /** cyb::blockchain::address (unique but not a full public key) */ + toBlockchainAddress() { + var pub_buf = this.toBuffer(); + var pub_sha = sha512(pub_buf); + return ripemd160(pub_sha); + } + + /** Alias for {@link toPublicKeyString} */ + toString(address_prefix = "CYB") { + return this.toPublicKeyString(address_prefix) + } + + /** + Full public key + {return} string + */ + toPublicKeyString(address_prefix = "CYB") { + var pub_buf = this.toBuffer(); + var checksum = ripemd160(pub_buf); + var addy = Buffer.concat([pub_buf, checksum.slice(0, 4)]); + return address_prefix + encode(addy); + } + + /** + @arg {string} public_key - like GPHXyz... + @arg {string} address_prefix - like GPH + @return PublicKey or `null` (if the public_key string is invalid) + */ + static fromPublicKeyString(public_key, address_prefix = "CYB") { + try { + return PublicKey.fromStringOrThrow(public_key, address_prefix) + } catch (e) { + return null; + } + } + + /** + @arg {string} public_key - like GPHXyz... + @arg {string} address_prefix - like GPH + @throws {Error} if public key is invalid + @return PublicKey + */ + static fromStringOrThrow(public_key, address_prefix = "CYB") { + var prefix = public_key.slice(0, address_prefix.length); + assert.equal( + address_prefix, prefix, + `Expecting key to begin with ${address_prefix}, instead got ${prefix}`); + public_key = public_key.slice(address_prefix.length); + + public_key = new Buffer(decode(public_key), 'binary'); + var checksum = public_key.slice(-4); + public_key = public_key.slice(0, -4); + var new_checksum = ripemd160(public_key); + new_checksum = new_checksum.slice(0, 4); + var isEqual = deepEqual(checksum, new_checksum); //, 'Invalid checksum' + if (!isEqual) { + throw new Error("Checksum did not match"); + } + return PublicKey.fromBuffer(public_key); + } + + toAddressString(address_prefix = "CYB") { + var pub_buf = this.toBuffer(); + var pub_sha = sha512(pub_buf); + var addy = ripemd160(pub_sha); + var checksum = ripemd160(addy); + addy = Buffer.concat([addy, checksum.slice(0, 4)]); + return address_prefix + encode(addy); + } + + toPtsAddy() { + var pub_buf = this.toBuffer(); + var pub_sha = sha256(pub_buf); + var addy = ripemd160(pub_sha); + addy = Buffer.concat([new Buffer([0x38]), addy]); //version 56(decimal) + + var checksum = sha256(addy); + checksum = sha256(checksum); + + addy = Buffer.concat([addy, checksum.slice(0, 4)]); + return encode(addy); + } + + child( offset ) { + + assert(Buffer.isBuffer(offset), "Buffer required: offset") + assert.equal(offset.length, 32, "offset length") + + offset = Buffer.concat([ this.toBuffer(), offset ]) + offset = sha256( offset ) + + let c = BigInteger.fromBuffer( offset ) + + if (c.compareTo(n) >= 0) + throw new Error("Child offset went out of bounds, try again") + + + let cG = G.multiply(c) + let Qprime = this.Q.add(cG) + + if( secp256k1.isInfinity(Qprime) ) + throw new Error("Child offset derived to an invalid key, try again") + + return PublicKey.fromPoint(Qprime) + } + + /* */ + + toByteBuffer() { + var b = new ByteBuffer(ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN); + this.appendByteBuffer(b); + return b.copy(0, b.offset); + } + + static fromHex(hex) { + return PublicKey.fromBuffer(new Buffer(hex, 'hex')); + } + + toHex() { + return this.toBuffer().toString('hex'); + } + + static fromPublicKeyStringHex(hex) { + return PublicKey.fromPublicKeyString(new Buffer(hex, 'hex')); + } + + /* */ +} + +module.exports = PublicKey; +// export default PublicKey; diff --git a/src/ecc/src/ecdsa.js b/src/ecc/src/ecdsa.js new file mode 100644 index 0000000..fe00ca6 --- /dev/null +++ b/src/ecc/src/ecdsa.js @@ -0,0 +1,217 @@ +const assert = require('assert'); // from github.com/bitcoinjs/bitcoinjs-lib from github.com/cryptocoinjs/ecdsa +const {sha256, HmacSHA256 } = require('./hash'); +const enforceType = require('./enforce_types'); + +const BigInteger = require('bigi'); +const ECSignature = require('./ecsignature'); + +// https://tools.ietf.org/html/rfc6979#section-3.2 +function deterministicGenerateK(curve, hash, d, checkSig, nonce) { + + enforceType('Buffer', hash) + enforceType(BigInteger, d) + + if (nonce) { + hash = sha256(Buffer.concat([hash, new Buffer(nonce)])) + } + + // sanity check + assert.equal(hash.length, 32, 'Hash must be 256 bit') + + var x = d.toBuffer(32) + var k = new Buffer(32) + var v = new Buffer(32) + + // Step B + v.fill(1) + + // Step C + k.fill(0) + + // Step D + k = HmacSHA256(Buffer.concat([v, new Buffer([0]), x, hash]), k) + + // Step E + v = HmacSHA256(v, k) + + // Step F + k = HmacSHA256(Buffer.concat([v, new Buffer([1]), x, hash]), k) + + // Step G + v = HmacSHA256(v, k) + + // Step H1/H2a, ignored as tlen === qlen (256 bit) + // Step H2b + v = HmacSHA256(v, k) + + var T = BigInteger.fromBuffer(v) + + // Step H3, repeat until T is within the interval [1, n - 1] + while ((T.signum() <= 0) || (T.compareTo(curve.n) >= 0) || !checkSig(T)) { + k = HmacSHA256(Buffer.concat([v, new Buffer([0])]), k) + v = HmacSHA256(v, k) + + // Step H1/H2a, again, ignored as tlen === qlen (256 bit) + // Step H2b again + v = HmacSHA256(v, k) + + T = BigInteger.fromBuffer(v) + } + + return T + +} + +function sign(curve, hash, d, nonce) { + + var e = BigInteger.fromBuffer(hash) + var n = curve.n + var G = curve.G + + var r, s + var k = deterministicGenerateK(curve, hash, d, function (k) { + // find canonically valid signature + var Q = G.multiply(k) + + if (curve.isInfinity(Q)) return false + + r = Q.affineX.mod(n) + if (r.signum() === 0) return false + + s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n) + if (s.signum() === 0) return false + + return true + }, nonce) + + var N_OVER_TWO = n.shiftRight(1) + + // enforce low S values, see bip62: 'low s values in signatures' + if (s.compareTo(N_OVER_TWO) > 0) { + s = n.subtract(s) + } + + return new ECSignature(r, s) +} + +function verifyRaw(curve, e, signature, Q) { + var n = curve.n + var G = curve.G + + var r = signature.r + var s = signature.s + + // 1.4.1 Enforce r and s are both integers in the interval [1, n − 1] + if (r.signum() <= 0 || r.compareTo(n) >= 0) return false + if (s.signum() <= 0 || s.compareTo(n) >= 0) return false + + // c = s^-1 mod n + var c = s.modInverse(n) + + // 1.4.4 Compute u1 = es^−1 mod n + // u2 = rs^−1 mod n + var u1 = e.multiply(c).mod(n) + var u2 = r.multiply(c).mod(n) + + // 1.4.5 Compute R = (xR, yR) = u1G + u2Q + var R = G.multiplyTwo(u1, Q, u2) + + // 1.4.5 (cont.) Enforce R is not at infinity + if (curve.isInfinity(R)) return false + + // 1.4.6 Convert the field element R.x to an integer + var xR = R.affineX + + // 1.4.7 Set v = xR mod n + var v = xR.mod(n) + + // 1.4.8 If v = r, output "valid", and if v != r, output "invalid" + return v.equals(r) +} + +function verify(curve, hash, signature, Q) { + // 1.4.2 H = Hash(M), already done by the user + // 1.4.3 e = H + var e = BigInteger.fromBuffer(hash) + return verifyRaw(curve, e, signature, Q) +} + +/** + * Recover a public key from a signature. + * + * See SEC 1: Elliptic Curve Cryptography, section 4.1.6, "Public + * Key Recovery Operation". + * + * http://www.secg.org/download/aid-780/sec1-v2.pdf + */ +function recoverPubKey(curve, e, signature, i) { + assert.strictEqual(i & 3, i, 'Recovery param is more than two bits') + + var n = curve.n + var G = curve.G + + var r = signature.r + var s = signature.s + + assert(r.signum() > 0 && r.compareTo(n) < 0, 'Invalid r value') + assert(s.signum() > 0 && s.compareTo(n) < 0, 'Invalid s value') + + // A set LSB signifies that the y-coordinate is odd + var isYOdd = i & 1 + + // The more significant bit specifies whether we should use the + // first or second candidate key. + var isSecondKey = i >> 1 + + // 1.1 Let x = r + jn + var x = isSecondKey ? r.add(n) : r + var R = curve.pointFromX(isYOdd, x) + + // 1.4 Check that nR is at infinity + var nR = R.multiply(n) + assert(curve.isInfinity(nR), 'nR is not a valid curve point') + + // Compute -e from e + var eNeg = e.negate().mod(n) + + // 1.6.1 Compute Q = r^-1 (sR - eG) + // Q = r^-1 (sR + -eG) + var rInv = r.modInverse(n) + + var Q = R.multiplyTwo(s, G, eNeg).multiply(rInv) + curve.validate(Q) + + return Q +} + +/** + * Calculate pubkey extraction parameter. + * + * When extracting a pubkey from a signature, we have to + * distinguish four different cases. Rather than putting this + * burden on the verifier, Bitcoin includes a 2-bit value with the + * signature. + * + * This function simply tries all four cases and returns the value + * that resulted in a successful pubkey recovery. + */ +function calcPubKeyRecoveryParam(curve, e, signature, Q) { + for (var i = 0; i < 4; i++) { + var Qprime = recoverPubKey(curve, e, signature, i) + + // 1.6.2 Verify Q + if (Qprime.equals(Q)) { + return i + } + } + + throw new Error('Unable to find valid recovery factor') +} + +// export { calcPubKeyRecoveryParam, deterministicGenerateK, recoverPubKey, sign, verify, verifyRaw }; +exports.calcPubKeyRecoveryParam = calcPubKeyRecoveryParam; +exports.deterministicGenerateK = deterministicGenerateK; +exports.recoverPubKey = recoverPubKey; +exports.sign = sign; +exports.verify = verify; +exports.verifyRaw = verifyRaw; \ No newline at end of file diff --git a/src/ecc/src/ecsignature.js b/src/ecc/src/ecsignature.js new file mode 100644 index 0000000..326ef2b --- /dev/null +++ b/src/ecc/src/ecsignature.js @@ -0,0 +1,127 @@ +const assert = require("assert"); // from https://github.com/bitcoinjs/bitcoinjs-lib +const enforceType = require('./enforce_types'); + +const BigInteger = require('bigi'); + +function ECSignature(r, s) { + enforceType(BigInteger, r) + enforceType(BigInteger, s) + + this.r = r + this.s = s +} + +// Import operations +ECSignature.parseCompact = function(buffer) { + assert.equal(buffer.length, 65, 'Invalid signature length') + var i = buffer.readUInt8(0) - 27 + + // At most 3 bits + assert.equal(i, i & 7, 'Invalid signature parameter') + var compressed = !!(i & 4) + + // Recovery param only + i = i & 3 + + var r = BigInteger.fromBuffer(buffer.slice(1, 33)) + var s = BigInteger.fromBuffer(buffer.slice(33)) + + return { + compressed: compressed, + i: i, + signature: new ECSignature(r, s) + } +} + +ECSignature.fromDER = function(buffer) { + assert.equal(buffer.readUInt8(0), 0x30, 'Not a DER sequence') + assert.equal(buffer.readUInt8(1), buffer.length - 2, 'Invalid sequence length') + assert.equal(buffer.readUInt8(2), 0x02, 'Expected a DER integer') + + var rLen = buffer.readUInt8(3) + assert(rLen > 0, 'R length is zero') + + var offset = 4 + rLen + assert.equal(buffer.readUInt8(offset), 0x02, 'Expected a DER integer (2)') + + var sLen = buffer.readUInt8(offset + 1) + assert(sLen > 0, 'S length is zero') + + var rB = buffer.slice(4, offset) + var sB = buffer.slice(offset + 2) + offset += 2 + sLen + + if (rLen > 1 && rB.readUInt8(0) === 0x00) { + assert(rB.readUInt8(1) & 0x80, 'R value excessively padded') + } + + if (sLen > 1 && sB.readUInt8(0) === 0x00) { + assert(sB.readUInt8(1) & 0x80, 'S value excessively padded') + } + + assert.equal(offset, buffer.length, 'Invalid DER encoding') + var r = BigInteger.fromDERInteger(rB) + var s = BigInteger.fromDERInteger(sB) + + assert(r.signum() >= 0, 'R value is negative') + assert(s.signum() >= 0, 'S value is negative') + + return new ECSignature(r, s) +} + +// FIXME: 0x00, 0x04, 0x80 are SIGHASH_* boundary constants, importing Transaction causes a circular dependency +ECSignature.parseScriptSignature = function(buffer) { + var hashType = buffer.readUInt8(buffer.length - 1) + var hashTypeMod = hashType & ~0x80 + + assert(hashTypeMod > 0x00 && hashTypeMod < 0x04, 'Invalid hashType') + + return { + signature: ECSignature.fromDER(buffer.slice(0, -1)), + hashType: hashType + } +} + +// Export operations +ECSignature.prototype.toCompact = function(i, compressed) { + if (compressed) i += 4 + i += 27 + + var buffer = new Buffer(65) + buffer.writeUInt8(i, 0) + + this.r.toBuffer(32).copy(buffer, 1) + this.s.toBuffer(32).copy(buffer, 33) + + return buffer +} + +ECSignature.prototype.toDER = function() { + var rBa = this.r.toDERInteger() + var sBa = this.s.toDERInteger() + + var sequence = [] + + // INTEGER + sequence.push(0x02, rBa.length) + sequence = sequence.concat(rBa) + + // INTEGER + sequence.push(0x02, sBa.length) + sequence = sequence.concat(sBa) + + // SEQUENCE + sequence.unshift(0x30, sequence.length) + + return new Buffer(sequence) +} + +ECSignature.prototype.toScriptSignature = function(hashType) { + var hashTypeBuffer = new Buffer(1) + hashTypeBuffer.writeUInt8(hashType, 0) + + return Buffer.concat([this.toDER(), hashTypeBuffer]) +} + +// export default ECSignature +module.exports = ECSignature; \ No newline at end of file diff --git a/src/ecc/src/enforce_types.js b/src/ecc/src/enforce_types.js new file mode 100644 index 0000000..01d24c4 --- /dev/null +++ b/src/ecc/src/enforce_types.js @@ -0,0 +1,42 @@ +function enforce(type, value) { // Copied from https://github.com/bitcoinjs/bitcoinjs-lib + switch (type) { + case 'Array': { + if (Array.isArray(value)) return + break + } + + case 'Boolean': { + if (typeof value === 'boolean') return + break + } + + case 'Buffer': { + if (Buffer.isBuffer(value)) return + break + } + + case 'Number': { + if (typeof value === 'number') return + break + } + + case 'String': { + if (typeof value === 'string') return + break + } + + default: { + if (getName(value.constructor) === getName(type)) return + } + } + + throw new TypeError('Expected ' + (getName(type) || type) + ', got ' + value) +} + +function getName(fn) { + // Why not fn.name: https://kangax.github.io/compat-table6/#function_name_property + var match = fn.toString().match(/function (.*?)\(/) + return match ? match[1] : null +} + +module.exports = enforce; \ No newline at end of file diff --git a/src/ecc/src/hash.js b/src/ecc/src/hash.js new file mode 100644 index 0000000..2c02039 --- /dev/null +++ b/src/ecc/src/hash.js @@ -0,0 +1,54 @@ +const createHash =require("create-hash"); +const createHmac =require("create-hmac"); + +/** @arg {string|Buffer} data + @arg {string} [digest = null] - 'hex', 'binary' or 'base64' + @return {string|Buffer} - Buffer when digest is null, or string +*/ +function sha1(data, encoding) { + return createHash('sha1').update(data).digest(encoding) +} + +/** @arg {string|Buffer} data + @arg {string} [digest = null] - 'hex', 'binary' or 'base64' + @return {string|Buffer} - Buffer when digest is null, or string +*/ +function sha256(data, encoding) { + return createHash('sha256').update(data).digest(encoding) +} + +/** @arg {string|Buffer} data + @arg {string} [digest = null] - 'hex', 'binary' or 'base64' + @return {string|Buffer} - Buffer when digest is null, or string +*/ +function sha512(data, encoding) { + return createHash('sha512').update(data).digest(encoding) +} + +function HmacSHA256(buffer, secret) { + return createHmac('sha256', secret).update(buffer).digest() +} + +function ripemd160(data) { + return createHash('rmd160').update(data).digest() +} + +// function hash160(buffer) { +// return ripemd160(sha256(buffer)) +// } +// +// function hash256(buffer) { +// return sha256(sha256(buffer)) +// } + +// +// function HmacSHA512(buffer, secret) { +// return crypto.createHmac('sha512', secret).update(buffer).digest() +// } + +// export { sha1, sha256, sha512, HmacSHA256, ripemd160 }; +exports.sha1 = sha1; +exports.sha256 = sha256; +exports.sha512 = sha512; +exports.HmacSHA256 = HmacSHA256; +exports.ripemd160 = ripemd160; \ No newline at end of file diff --git a/src/ecc/src/signature.js b/src/ecc/src/signature.js new file mode 100644 index 0000000..b8971b6 --- /dev/null +++ b/src/ecc/src/signature.js @@ -0,0 +1,155 @@ +const { sign, recoverPubKey, verify, calcPubKeyRecoveryParam } = require('./ecdsa'); +const { sha256 } = require('./hash'); +const {getCurveByName} = require('ecurve'); + +const assert = require("assert"); +const BigInteger = require('bigi'); +const PublicKey = require('./PublicKey'); + +var secp256k1 = getCurveByName('secp256k1'); + +class Signature { + + constructor(r1, s1, i1) { + this.r = r1; + this.s = s1; + this.i = i1; + assert.equal(this.r != null, true, 'Missing parameter'); + assert.equal(this.s != null, true, 'Missing parameter'); + assert.equal(this.i != null, true, 'Missing parameter'); + } + + static fromBuffer(buf) { + var i, r, s; + assert.equal(buf.length, 65, 'Invalid signature length'); + i = buf.readUInt8(0); + assert.equal(i - 27, i - 27 & 7, 'Invalid signature parameter'); + r = BigInteger.fromBuffer(buf.slice(1, 33)); + s = BigInteger.fromBuffer(buf.slice(33)); + return new Signature(r, s, i); + }; + + toBuffer() { + var buf; + buf = new Buffer(65); + buf.writeUInt8(this.i, 0); + this.r.toBuffer(32).copy(buf, 1); + this.s.toBuffer(32).copy(buf, 33); + return buf; + }; + + recoverPublicKeyFromBuffer(buffer) { + return this.recoverPublicKey(sha256(buffer)); + }; + + /** + @return {PublicKey} + */ + recoverPublicKey(sha256_buffer) { + let Q, e, i; + e = BigInteger.fromBuffer(sha256_buffer); + i = this.i; + i -= 27; + i = i & 3; + Q = recoverPubKey(secp256k1, e, this, i); + return PublicKey.fromPoint(Q); + }; + + + /** + @param {Buffer} buf + @param {PrivateKey} private_key + @return {Signature} + */ + static signBuffer(buf, private_key) { + var _hash = sha256(buf); + return Signature.signBufferSha256(_hash, private_key) + } + + /** Sign a buffer of exactally 32 bytes in size (sha256(text)) + @param {Buffer} buf - 32 bytes binary + @param {PrivateKey} private_key + @return {Signature} + */ + static signBufferSha256(buf_sha256, private_key) { + if( buf_sha256.length !== 32 || ! Buffer.isBuffer(buf_sha256) ) + throw new Error("buf_sha256: 32 byte buffer requred") + var der, e, ecsignature, i, lenR, lenS, nonce; + i = null; + nonce = 0; + e = BigInteger.fromBuffer(buf_sha256); + while (true) { + ecsignature = sign(secp256k1, buf_sha256, private_key.d, nonce++); + der = ecsignature.toDER(); + lenR = der[3]; + lenS = der[5 + lenR]; + if (lenR === 32 && lenS === 32) { + i = calcPubKeyRecoveryParam(secp256k1, e, ecsignature, private_key.toPublicKey().Q); + i += 4; // compressed + i += 27; // compact // 24 or 27 :( forcing odd-y 2nd key candidate) + break; + } + if (nonce % 10 === 0) { + console.log("WARN: " + nonce + " attempts to find canonical signature"); + } + } + return new Signature(ecsignature.r, ecsignature.s, i); + }; + + static sign(string, private_key) { + return Signature.signBuffer(new Buffer(string), private_key); + }; + + + /** + @param {Buffer} un-hashed + @param {./PublicKey} + @return {boolean} + */ + verifyBuffer(buf, public_key) { + var _hash = sha256(buf); + return this.verifyHash(_hash, public_key); + }; + + verifyHash(hash, public_key) { + assert.equal(hash.length, 32, "A SHA 256 should be 32 bytes long, instead got " + hash.length); + return verify(secp256k1, hash, { + r: this.r, + s: this.s + }, public_key.Q); + }; + + + /* */ + + toByteBuffer() { + var b; + b = new ByteBuffer(ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN); + this.appendByteBuffer(b); + return b.copy(0, b.offset); + }; + + static fromHex(hex) { + return Signature.fromBuffer(new Buffer(hex, "hex")); + }; + + toHex() { + return this.toBuffer().toString("hex"); + }; + + static signHex(hex, private_key) { + var buf; + buf = new Buffer(hex, 'hex'); + return Signature.signBuffer(buf, private_key); + }; + + verifyHex(hex, public_key) { + var buf; + buf = new Buffer(hex, 'hex'); + return this.verifyBuffer(buf, public_key); + }; + +} + +// export default Signature; +module.exports = Signature; \ No newline at end of file diff --git a/src/plugins/vuetify.js b/src/plugins/vuetify.js index d1adb68..12bc370 100644 --- a/src/plugins/vuetify.js +++ b/src/plugins/vuetify.js @@ -14,6 +14,9 @@ export default new Vuetify({ secondary: colors.red.darken2, // #FFCDD2 accent: colors.red.lighten1, // #3F51B5 }, + dark:{ + primary: "#c62c43", // #c62c43 + } }, }, icons: {