add support for compressed secp256k1 keys encoded with base58btc

Signed-off-by: Jakub Sokołowski <jakub@status.im>
This commit is contained in:
Jakub Sokołowski 2020-07-02 15:35:49 +02:00
parent b4d7daf379
commit bf1b855328
No known key found for this signature in database
GPG Key ID: 4EF064D0E6D63020
5 changed files with 168 additions and 6 deletions

View File

@ -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": {

View File

@ -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)

View File

@ -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 <span>${multibaseKey}</span> 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 <span>${compressedKey}</span> 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')

View File

@ -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,
}

View File

@ -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"