Merge pull request #115 from DeepDoge/master

Bug Fixes
This commit is contained in:
kodxana 2022-05-01 16:38:50 +02:00 committed by GitHub
commit 6b8f193bfd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 85 additions and 74 deletions

View file

@ -1,14 +1,15 @@
import { getExtensionSettingsAsync } from "./settings" import { getExtensionSettingsAsync, ytUrlResolversSettings } from "./settings"
import { setSetting } from "./useSettings" import { setSetting } from "./useSettings"
import path from 'path'
async function generateKeys() { async function generateKeys() {
const keys = await window.crypto.subtle.generateKey( const keys = await window.crypto.subtle.generateKey(
{ {
name: "RSASSA-PKCS1-v1_5", name: "RSASSA-PKCS1-v1_5",
// Consider using a 4096-bit key for systems that require long-term security // Consider using a 4096-bit key for systems that require long-term security
modulusLength: 2048, modulusLength: 384,
publicExponent: new Uint8Array([1, 0, 1]), publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256", hash: "SHA-1",
}, },
true, true,
["sign", "verify"] ["sign", "verify"]
@ -28,22 +29,26 @@ async function exportPrivateKey(key: CryptoKey) {
return Buffer.from(exported).toString('base64') return Buffer.from(exported).toString('base64')
} }
const publicKeyPrefix = `MEwwDQYJKoZIhvcNAQEBBQADOwAwOAIxA`
const publicKeySuffix = `IDAQAB` //`wIDAQAB` `WIDAQAB`
const publicKeyLength = 65
async function exportPublicKey(key: CryptoKey) { async function exportPublicKey(key: CryptoKey) {
const exported = await window.crypto.subtle.exportKey( const exported = await window.crypto.subtle.exportKey(
"spki", "spki",
key key
) )
return Buffer.from(exported).toString('base64') const publicKey = Buffer.from(exported).toString('base64')
console.log(publicKey)
return publicKey.substring(publicKeyPrefix.length, publicKeyPrefix.length + publicKeyLength)
} }
function importPrivateKey(base64: string) { function importPrivateKey(base64: string) {
return window.crypto.subtle.importKey( return window.crypto.subtle.importKey(
"pkcs8", "pkcs8",
Buffer.from(base64, 'base64'), Buffer.from(base64, 'base64'),
{ {
name: "RSASSA-PKCS1-v1_5", name: "RSASSA-PKCS1-v1_5",
hash: "SHA-256", hash: "SHA-1",
}, },
true, true,
["sign"] ["sign"]
@ -54,7 +59,7 @@ export async function sign(data: string, privateKey: string) {
return Buffer.from(await window.crypto.subtle.sign( return Buffer.from(await window.crypto.subtle.sign(
{ name: "RSASSA-PKCS1-v1_5" }, { name: "RSASSA-PKCS1-v1_5" },
await importPrivateKey(privateKey), await importPrivateKey(privateKey),
Buffer.from(data) await crypto.subtle.digest({ name: 'SHA-1' }, Buffer.from(data))
)).toString('base64') )).toString('base64')
} }
@ -63,6 +68,33 @@ export function resetProfileSettings() {
setSetting('privateKey', null) setSetting('privateKey', null)
} }
async function apiRequest<T extends object>(method: 'GET' | 'POST', pathname: string, data: T) {
const settings = await getExtensionSettingsAsync()
/* const urlResolverSettings = ytUrlResolversSettings[settings.urlResolver]
if (!urlResolverSettings.signRequest) throw new Error() */
console.log(ytUrlResolversSettings)
const url = new URL(ytUrlResolversSettings.madiatorFinder.href/* urlResolverSettings.href */)
console.log(url)
url.pathname = path.join(url.pathname, pathname)
url.searchParams.set('data', JSON.stringify(data))
if (true/* requiresSignature */) {
if (!settings.privateKey || !settings.publicKey)
throw new Error('There is no profile.')
url.searchParams.set('keys', JSON.stringify({
signature: await sign(url.searchParams.toString(), settings.privateKey!),
publicKey: settings.publicKey
}))
}
const respond = await fetch(url.href, { method })
if (respond.ok) return respond.json()
throw new Error((await respond.json()).message)
}
export async function generateProfileAndSetNickname(overwrite = false) { export async function generateProfileAndSetNickname(overwrite = false) {
let { publicKey, privateKey } = await getExtensionSettingsAsync() let { publicKey, privateKey } = await getExtensionSettingsAsync()
@ -74,80 +106,43 @@ export async function generateProfileAndSetNickname(overwrite = false) {
alert("Invalid nickname") alert("Invalid nickname")
} }
if (overwrite || !privateKey || !publicKey) { try {
resetProfileSettings() if (overwrite || !privateKey || !publicKey) {
await generateKeys().then((keys) => { resetProfileSettings()
publicKey = keys.publicKey await generateKeys().then((keys) => {
privateKey = keys.privateKey publicKey = keys.publicKey
}) privateKey = keys.privateKey
} })
setSetting('publicKey', publicKey)
const url = new URL('https://finder.madiator.com/api/v1/profile') setSetting('privateKey', privateKey)
url.searchParams.set('data', JSON.stringify({ nickname })) }
url.searchParams.set('keys', JSON.stringify({ await apiRequest('POST', '/profile', { nickname })
signature: await sign(url.searchParams.toString(), privateKey!),
publicKey
}))
const respond = await fetch(url.href, { method: "POST" })
if (respond.ok) {
setSetting('publicKey', publicKey)
setSetting('privateKey', privateKey)
alert(`Your nickname has been set to ${nickname}`) alert(`Your nickname has been set to ${nickname}`)
} catch (error: any) {
resetProfileSettings()
alert(error.message)
} }
else alert((await respond.json()).message)
} }
export async function purgeProfile() { export async function purgeProfile() {
try { try {
if (!confirm("This will purge all of your online and offline profile data.\nStill wanna continue?")) return if (!confirm("This will purge all of your online and offline profile data.\nStill wanna continue?")) return
const settings = await getExtensionSettingsAsync() await apiRequest('POST', '/profile/purge', {})
resetProfileSettings()
if (!settings.privateKey || !settings.publicKey) alert(`Your profile has been purged`)
throw new Error('There is no profile to be purged.')
const url = new URL('https://finder.madiator.com/api/v1/profile/purge')
url.searchParams.set('keys', JSON.stringify({
signature: await sign(url.searchParams.toString(), settings.privateKey!),
publicKey: settings.publicKey
}))
const respond = await fetch(url.href, { method: "POST" })
if (respond.ok) {
resetProfileSettings()
alert(`Your profile has been purged`)
}
else throw new Error((await respond.json()).message)
} catch (error: any) { } catch (error: any) {
alert(error.message) alert(error.message)
} }
} }
export async function getProfile() { export async function getProfile() {
try { let { publicKey, privateKey } = await getExtensionSettingsAsync()
const settings = await getExtensionSettingsAsync() return (await apiRequest('GET', '/profile', { publicKey })) as { nickname: string, score: number, publickKey: string }
if (!settings.privateKey || !settings.publicKey)
throw new Error('There is no profile.')
const url = new URL('https://finder.madiator.com/api/v1/profile')
url.searchParams.set('data', JSON.stringify({ publicKey: settings.publicKey }))
url.searchParams.set('keys', JSON.stringify({
signature: await sign(url.searchParams.toString(), settings.privateKey!),
publicKey: settings.publicKey
}))
const respond = await fetch(url.href, { method: "GET" })
if (respond.ok) {
const profile = await respond.json() as { nickname: string, score: number, publickKey: string }
return profile
}
else throw new Error((await respond.json()).message)
} catch (error: any) {
console.error(error)
}
} }
export function friendlyPublicKey(publicKey: string | null) { export function friendlyPublicKey(publicKey: string | null) {
// This is copy paste of Madiator Finder's friendly public key // This is copy paste of Madiator Finder's friendly public key
return publicKey?.substring(publicKey.length - 64, publicKey.length - 32) return `${publicKey?.substring(0, 32)}...`
} }
function download(data: string, filename: string, type: string) { function download(data: string, filename: string, type: string) {
@ -177,7 +172,7 @@ async function readFile() {
const reader = new FileReader() const reader = new FileReader()
reader.addEventListener('load', () => resolve(reader.result?.toString() ?? null)) reader.addEventListener('load', () => resolve(reader.result?.toString() ?? null))
reader.readAsBinaryString(myFile) reader.readAsText(myFile)
}) })
}) })
} }

View file

@ -118,12 +118,17 @@ export const getYtUrlResolversSettingsEntiries = () => Object.entries(ytUrlResol
export const ytUrlResolversSettings = { export const ytUrlResolversSettings = {
odyseeApi: ytUrlResolver({ odyseeApi: ytUrlResolver({
name: "Odysee", name: "Odysee",
href: "https://api.odysee.com/yt/resolve", href: "https://api.odysee.com/yt",
signRequest: false signRequest: false
}), }),
madiatorFinder: ytUrlResolver({ madiatorFinder: ytUrlResolver({
name: "Madiator Finder", name: "Madiator Finder",
href: "https://finder.madiator.com/api/v1/resolve", href: "https://finder.madiator.com/api/v1",
signRequest: true signRequest: true
}) }),
/* madiatorFinderLocal: ytUrlResolver({
name: "Madiator Finder Local",
href: "http://127.0.0.1:3001/api/v1",
signRequest: true
}) */
} }

View file

@ -2,6 +2,7 @@ import { chunk } from "lodash"
import { sign } from "../crypto" import { sign } from "../crypto"
import { getExtensionSettingsAsync, ytUrlResolversSettings } from "../settings" import { getExtensionSettingsAsync, ytUrlResolversSettings } from "../settings"
import { LbryPathnameCache } from "./urlCache" import { LbryPathnameCache } from "./urlCache"
import path from "path"
const QUERY_CHUNK_SIZE = 100 const QUERY_CHUNK_SIZE = 100
@ -41,6 +42,7 @@ export async function resolveById(params: Paramaters, progressCallback?: (progre
if (params.length === 0) return results if (params.length === 0) return results
const url = new URL(`${urlResolverSetting.href}`) const url = new URL(`${urlResolverSetting.href}`)
url.pathname = path.join(url.pathname, '/resolve')
url.searchParams.set('video_ids', params.filter((item) => item.type === 'video').map((item) => item.id).join(',')) url.searchParams.set('video_ids', params.filter((item) => item.type === 'video').map((item) => item.id).join(','))
url.searchParams.set('channel_ids', params.filter((item) => item.type === 'channel').map((item) => item.id).join(',')) url.searchParams.set('channel_ids', params.filter((item) => item.type === 'channel').map((item) => item.id).join(','))
if (urlResolverSetting.signRequest && publicKey && privateKey) if (urlResolverSetting.signRequest && publicKey && privateKey)

View file

@ -49,7 +49,12 @@ function WatchOnLbryPopup(params: { profile: Awaited<ReturnType<typeof getProfil
} }
</section> </section>
</header> </header>
: <header><a className='button filled' onClick={() => updateRoute('profile')} href="#profile">Your Profile</a> : <header>
{
popupRoute === 'profile'
? <a onClick={() => updateRoute('')} className="button filled"> Back</a>
: <a className='button filled' onClick={() => updateRoute('profile')} href="#profile">Your Profile</a>
}
</header> </header>
} }
{ {

View file

@ -25,4 +25,4 @@ chrome.runtime.onMessage.addListener(({ json }, sender, sendResponse) => {
return true return true
}) })
chrome.tabs.onUpdated.addListener((tabId, changeInfo) => changeInfo.url && chrome.tabs.sendMessage(tabId, {})) chrome.tabs.onUpdated.addListener((tabId, changeInfo) => changeInfo.status === 'complete' && chrome.tabs.sendMessage(tabId, { message: 'url-changed' }))

View file

@ -1,8 +1,9 @@
import { DEFAULT_SETTINGS, ExtensionSettings, getExtensionSettingsAsync } from '../common/settings' import { DEFAULT_SETTINGS, ExtensionSettings, getExtensionSettingsAsync, sourcePlatfromSettings, targetPlatformSettings, ytUrlResolversSettings } from '../common/settings'
import { setSetting } from '../common/useSettings'
/** Reset settings to default value and update the browser badge text */ /** Reset settings to default value and update the browser badge text */
async function initSettings() { async function initSettings() {
const settings = await getExtensionSettingsAsync() let settings = await getExtensionSettingsAsync()
// get all the values that aren't set and use them as a change set // get all the values that aren't set and use them as a change set
const invalidEntries = (Object.entries(DEFAULT_SETTINGS) as Array<[keyof ExtensionSettings, ExtensionSettings[keyof ExtensionSettings]]>) const invalidEntries = (Object.entries(DEFAULT_SETTINGS) as Array<[keyof ExtensionSettings, ExtensionSettings[keyof ExtensionSettings]]>)
@ -11,10 +12,13 @@ async function initSettings() {
// fix our local var and set it in storage for later // fix our local var and set it in storage for later
if (invalidEntries.length > 0) { if (invalidEntries.length > 0) {
const changeSet = Object.fromEntries(invalidEntries) const changeSet = Object.fromEntries(invalidEntries)
Object.assign(settings, changeSet)
chrome.storage.local.set(changeSet) chrome.storage.local.set(changeSet)
settings = await getExtensionSettingsAsync()
} }
if (!Object.keys(targetPlatformSettings).includes(settings.targetPlatform)) setSetting('targetPlatform', DEFAULT_SETTINGS.targetPlatform)
if (!Object.keys(ytUrlResolversSettings).includes(settings.urlResolver)) setSetting('urlResolver', DEFAULT_SETTINGS.urlResolver)
chrome.browserAction.setBadgeText({ text: settings.redirect ? 'ON' : 'OFF' }) chrome.browserAction.setBadgeText({ text: settings.redirect ? 'ON' : 'OFF' })
} }

View file

@ -104,7 +104,7 @@ async function requestResolveById(...params: Parameters<typeof resolveById>): Re
* history.pushState changes from a content script * history.pushState changes from a content script
*/ */
// Listen URL Change // Listen URL Change
chrome.runtime.onMessage.addListener(() => updater()) chrome.runtime.onMessage.addListener(({ message }, sender) => message === 'url-changed' && updater())
async function getTargetByURL(url: URL) { async function getTargetByURL(url: URL) {
if (url.pathname === '/watch' && url.searchParams.has('v')) { if (url.pathname === '/watch' && url.searchParams.has('v')) {