diff --git a/package.json b/package.json
index 18d13a8..50c6912 100644
--- a/package.json
+++ b/package.json
@@ -18,7 +18,9 @@
"is-html": "^2.0.0",
"js-status-chat-name": "git+https://github.com/status-im/js-status-chat-name.git#v0.1.2",
"morgan": "^1.9.1",
+ "multibase": "^1.0.1",
"qrcode": "^1.3.0",
+ "secp256k1": "^4.0.1",
"univeil": "^0.1.14"
},
"devDependencies": {
diff --git a/routes/index.js b/routes/index.js
index ea377ea..a718845 100644
--- a/routes/index.js
+++ b/routes/index.js
@@ -68,12 +68,18 @@ const handleSite = (req, res) => {
/* Open User Profile from Chat Key in Status */
const handleChatKey = (req, res) => {
- /* We accept upper case for chat keys */
- const chatKey = req.params[0].toLowerCase()
+ let chatKey = req.params[0]
+ let uncompressedKey = chatKey
+
try {
- chatName = StatusIm.chatKeyToChatName(chatKey)
+ if (!chatKey.startsWith('0x')) { /* decompress/deserialize key */
+ uncompressedKey = utils.decompressKey(chatKey)
+ } else { /* We accept upper case for hexadecimal public keys */
+ chatKey = chatKey.toLowerCase()
+ }
+ chatName = StatusIm.chatKeyToChatName(uncompressedKey)
} catch(error) {
- console.error(`Failed to parse: "${req.params[0]}", Error:`, error.message)
+ console.error(`Failed to parse: "${uncompressedKey}", Error:`, error.message)
res.render('index', { title: 'Invalid chat key format!', error })
return
}
@@ -130,9 +136,14 @@ router.get('/health', (req, res) => res.send('OK'))
router.get('/b/:url(*)', handleSite)
router.get('/browse/:url(*)', handleSite) /* Legacy */
+router.get(/^\/u\/(z[0-9a-zA-Z]{46,49})$/, handleChatKey)
+router.get(/^\/u\/(z[0-9a-zA-Z]+)$/, handleError('Incorrect length of chat key'))
+router.get(/^\/u\/(fe701[0-9a-fA-F]{66})$/, handleChatKey)
+router.get(/^\/u\/(fe701[0-9a-fA-F]+)$/, handleError('Incorrect length of chat key'))
+router.get(/^\/u\/(f[0-9a-fA-F]{66})$/, handleChatKey)
+router.get(/^\/u\/(f[0-9a-fA-F]+)$/, handleError('Incorrect length of chat key'))
router.get(/^\/u\/(0[xX]04[0-9a-fA-F]{128})$/, handleChatKey)
-router.get(/^\/u\/(0[xX]04[0-9a-fA-F]{1,127})$/, handleError('Incorrect length of chat key'))
-router.get(/^\/u\/(0[xX]04[0-9a-fA-F]{129,})$/, handleError('Incorrect length of chat key'))
+router.get(/^\/u\/(0[xX]04[0-9a-fA-F]+)$/, handleError('Incorrect length of chat key'))
router.get(/^\/user\/(0[xX]04[0-9a-fA-F]{128})$/, handleChatKey) /* Legacy */
router.get(/^\/u\/([^><]*[A-Z]+[^><]*)$/, handleRedirect)
diff --git a/tests/main.js b/tests/main.js
index 4491ad3..1f58a41 100644
--- a/tests/main.js
+++ b/tests/main.js
@@ -8,6 +8,8 @@ import appleSiteAssociation from '../resources/apple-app-site-association.json'
const host = 'join.status.im'
const chatKey = 'e139115a1acc72510388fcf7e1cf492784c9a839888b25271465f4f1baa38c2d3997f8fd78828eb8628bc3bb55ababd884c6002d18330d59c404cc9ce3e4fb35'
+const multibaseKey = 'fe70103e139115a1acc72510388fcf7e1cf492784c9a839888b25271465f4f1baa38c2d'
+const compressedKey = 'zQ3shuoHL7WZEfKdexM6EyDRDhXBgcKz5SVw79stVMpmeyUvG'
const chatName = 'Lavender Trivial Goral'
const srv = request(app)
@@ -103,6 +105,40 @@ test('test chat key routes', t => {
})
})
+test('test multibase chat key routes', t => {
+ t.test(`/u/${multibaseKey.substr(0,12)}... - VALID`, async t => {
+ const res = await get(`/u/${multibaseKey}`)
+ t.eq(res.statusCode, 200, 'returns 200')
+ t.eq(meta(res, 'al:ios:url'), `status-im://u/${multibaseKey}`, 'contains ios url')
+ t.eq(meta(res, 'al:android:url'), `status-im://u/${multibaseKey}`, 'contains android url')
+ t.eq(html(res, 'div.info'), `Chat and transact with ${multibaseKey} in Status.`, 'contains prompt')
+ t.eq(html(res, '#header'), chatName, 'contains chat name')
+ })
+
+ t.test(`/u/${multibaseKey.substr(0,12)}... - TOO SHORT`, async t => { /* error on too short chat key */
+ const res = await get(`/u/${multibaseKey.substr(0,46)}`)
+ t.eq(res.statusCode, 400, 'returns 400')
+ t.eq(html(res, 'code#error'), 'Incorrect length of chat key', 'contains error')
+ })
+})
+
+test('test compressed chat key routes', t => {
+ t.test(`/u/${compressedKey.substr(0,12)}... - VALID`, async t => {
+ const res = await get(`/u/${compressedKey}`)
+ t.eq(res.statusCode, 200, 'returns 200')
+ t.eq(meta(res, 'al:ios:url'), `status-im://u/${compressedKey}`, 'contains ios url')
+ t.eq(meta(res, 'al:android:url'), `status-im://u/${compressedKey}`, 'contains android url')
+ t.eq(html(res, 'div.info'), `Chat and transact with ${compressedKey} in Status.`, 'contains prompt')
+ t.eq(html(res, '#header'), chatName, 'contains chat name')
+ })
+
+ t.test(`/u/${compressedKey.substr(0,12)}... - TOO SHORT`, async t => { /* error on too short chat key */
+ const res = await get(`/u/${compressedKey.substr(0,46)}`)
+ t.eq(res.statusCode, 400, 'returns 400')
+ t.eq(html(res, 'code#error'), 'Incorrect length of chat key', 'contains error')
+ })
+})
+
test('test public channel routes', t => {
t.test('/status-test - VALID', async t => {
const res = await get('/status-test')
diff --git a/utils/index.js b/utils/index.js
index d549dca..1706b5c 100644
--- a/utils/index.js
+++ b/utils/index.js
@@ -2,6 +2,9 @@ const QRCode = require('qrcode')
const uts46 = require('idna-uts46-hx')
const isHtml = require('is-html')
const univeil = require('univeil')
+const { Buffer } = require('buffer')
+const multibase = require('multibase')
+const secp256k1 = require('secp256k1')
const isAndroid = (req) => (
req.headers['user-agent'].toLowerCase().indexOf("android") > -1
@@ -42,6 +45,23 @@ const normalizeEns = (name) => (
const showSpecialChars = (str) => univeil(str)
+/* check for multiformat variant encoding of the
+ * multicodec secp256k1 key identifier e7 */
+const isMultiFormatSecp256k1 = (bytes) => (
+ Buffer.from([231, 1]).compare(bytes.slice(0, 2)) == 0
+)
+
+/* decodes base58btc encoding and decompresses a serialized secp256k1 */
+const decompressKey = (key) => {
+ let cBytes = multibase.decode(key)
+ if (isMultiFormatSecp256k1(cBytes)) {
+ cBytes = cBytes.slice(2)
+ }
+ let pubKey = secp256k1.publicKeyConvert(cBytes, compressed=false)
+ let multibaseHex = multibase.encode('base16', pubKey).toString()
+ return '0x' + multibaseHex.substr(1)
+}
+
module.exports = {
isAndroid,
isIOS,
@@ -49,4 +69,5 @@ module.exports = {
isValidUrl,
normalizeEns,
showSpecialChars,
+ decompressKey,
}
diff --git a/yarn.lock b/yarn.lock
index 410bd6f..b7371ad 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -102,6 +102,13 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
+base-x@^3.0.8:
+ version "3.0.8"
+ resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.8.tgz#1e1106c2537f0162e8b52474a557ebb09000018d"
+ integrity sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA==
+ dependencies:
+ safe-buffer "^5.0.1"
+
base64-js@^1.0.2:
version "1.3.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
@@ -139,6 +146,11 @@ bindings@^1.5.0:
dependencies:
file-uri-to-path "1.0.0"
+bn.js@^4.4.0:
+ version "4.11.9"
+ resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828"
+ integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==
+
body-parser@1.18.3:
version "1.18.3"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.3.tgz#5b292198ffdd553b3a0f20ded0592b956955c8b4"
@@ -197,6 +209,11 @@ braces@^2.3.1, braces@^2.3.2:
split-string "^3.0.2"
to-regex "^3.0.1"
+brorand@^1.0.1:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
+ integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
+
buffer-alloc-unsafe@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
@@ -228,6 +245,14 @@ buffer@^5.4.3:
base64-js "^1.0.2"
ieee754 "^1.1.4"
+buffer@^5.5.0:
+ version "5.6.0"
+ resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786"
+ integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==
+ dependencies:
+ base64-js "^1.0.2"
+ ieee754 "^1.1.4"
+
byline@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1"
@@ -619,6 +644,19 @@ ejs@~2.5.7:
resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.9.tgz#7ba254582a560d267437109a68354112475b0ce5"
integrity sha512-GJCAeDBKfREgkBtgrYSf9hQy9kTb3helv0zGdzqhM7iAkW8FA/ZF97VQDbwFiwIT8MQLLOe5VlPZOEvZAqtUAQ==
+elliptic@^6.5.2:
+ version "6.5.3"
+ resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6"
+ integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==
+ dependencies:
+ bn.js "^4.4.0"
+ brorand "^1.0.1"
+ hash.js "^1.0.0"
+ hmac-drbg "^1.0.0"
+ inherits "^2.0.1"
+ minimalistic-assert "^1.0.0"
+ minimalistic-crypto-utils "^1.0.0"
+
emoji-regex@^7.0.1:
version "7.0.3"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
@@ -922,6 +960,23 @@ has-values@^1.0.0:
is-number "^3.0.0"
kind-of "^4.0.0"
+hash.js@^1.0.0, hash.js@^1.0.3:
+ version "1.1.7"
+ resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42"
+ integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==
+ dependencies:
+ inherits "^2.0.3"
+ minimalistic-assert "^1.0.1"
+
+hmac-drbg@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
+ integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=
+ dependencies:
+ hash.js "^1.0.3"
+ minimalistic-assert "^1.0.0"
+ minimalistic-crypto-utils "^1.0.1"
+
html-tags@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.1.0.tgz#7b5e6f7e665e9fb41f30007ed9e0d41e97fb2140"
@@ -1338,6 +1393,16 @@ mime@^1.4.1:
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
+minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
+ integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
+
+minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
+ integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
+
minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
@@ -1379,6 +1444,14 @@ ms@^2.1.1:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
+multibase@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/multibase/-/multibase-1.0.1.tgz#4adbe1de0be8a1ab0274328b653c3f1903476724"
+ integrity sha512-KcCxpBVY8fdVKu4dJMAahq4F/2Z/9xqEjIiR7PiMe7LRGeorFn2NLmicN6nLBCqQvft6MG2Lc9X5P0IdyvnxEw==
+ dependencies:
+ base-x "^3.0.8"
+ buffer "^5.5.0"
+
nan@^2.12.1:
version "2.14.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
@@ -1406,6 +1479,16 @@ negotiator@0.6.2:
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
+node-addon-api@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32"
+ integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==
+
+node-gyp-build@^4.2.0:
+ version "4.2.2"
+ resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.2.tgz#3f44b65adaafd42fb6c3d81afd630e45c847eb66"
+ integrity sha512-Lqh7mrByWCM8Cf9UPqpeoVBBo5Ugx+RKu885GAzmLBVYjeywScxHXPGLa4JfYNZmcNGwzR0Glu5/9GaQZMFqyA==
+
nodemon@^1.17.5:
version "1.19.4"
resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.19.4.tgz#56db5c607408e0fdf8920d2b444819af1aae0971"
@@ -1783,6 +1866,15 @@ safe-regex@^1.1.0:
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+secp256k1@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.1.tgz#b9570ca26ace9e74c3171512bba253da9c0b6d60"
+ integrity sha512-iGRjbGAKfXMqhtdkkuNxsgJQfJO8Oo78Rm7DAvsG3XKngq+nJIOGqrCSXcQqIVsmCj0wFanE5uTKFxV3T9j2wg==
+ dependencies:
+ elliptic "^6.5.2"
+ node-addon-api "^2.0.0"
+ node-gyp-build "^4.2.0"
+
semver-diff@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36"