mirror of
https://github.com/tlsnotary/explorer.git
synced 2026-01-09 14:58:09 -05:00
fix: ui updates and notary key parsing
This commit is contained in:
@@ -9,7 +9,6 @@ COPY . .
|
||||
RUN curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh -s -- -y
|
||||
RUN npm i
|
||||
RUN npm i --prefix rs/verifier/
|
||||
RUN npm i --prefix rs/0.1.0-alpha.6/
|
||||
RUN npm i --prefix rs/0.1.0-alpha.7/
|
||||
RUN npm run build
|
||||
|
||||
|
||||
228
pnpm-lock.yaml
generated
228
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -14,6 +14,7 @@ import { verify } from '../rs/verifier/index.node';
|
||||
// @ts-ignore
|
||||
import { verify as verifyV7 } from '../rs/0.1.0-alpha.7/index.node';
|
||||
import { Attestation } from '../web/utils/types/types';
|
||||
import { convertNotaryWsToHttp } from '../web/utils';
|
||||
|
||||
const app = express();
|
||||
const port = 3000;
|
||||
@@ -97,19 +98,37 @@ app.get('/ipfs/:cid', async (req, res) => {
|
||||
* redirect to root if verification fails
|
||||
*/
|
||||
if (!jsonProof.version && jsonProof.notaryUrl) {
|
||||
const proof = await verify(
|
||||
file,
|
||||
await fetchPublicKeyFromNotary(jsonProof.notaryUrl),
|
||||
);
|
||||
const notaryPem = await fetchPublicKeyFromNotary(jsonProof.notaryUrl);
|
||||
const proof = await verify(file, notaryPem);
|
||||
proof.notaryUrl = jsonProof.notaryUrl;
|
||||
storeConfig.proofs.ipfs[req.params.cid].proof = proof;
|
||||
storeConfig.proofs.ipfs[req.params.cid].proof = {
|
||||
...proof,
|
||||
version: '0.1.0-alpha.5',
|
||||
notaryUrl: jsonProof.notaryUrl,
|
||||
notaryKey: notaryPem,
|
||||
};
|
||||
} else if (jsonProof.version === '0.1.0-alpha.7') {
|
||||
const proof = await verifyV7(
|
||||
jsonProof.data,
|
||||
await fetchPublicKeyFromNotary(jsonProof.meta.notaryUrl),
|
||||
);
|
||||
const notaryUrl = convertNotaryWsToHttp(jsonProof.meta.notaryUrl);
|
||||
const notaryPem = await fetchPublicKeyFromNotary(notaryUrl);
|
||||
const proof = await verifyV7(jsonProof.data, notaryPem);
|
||||
proof.notaryUrl = jsonProof.meta.notaryUrl;
|
||||
storeConfig.proofs.ipfs[req.params.cid].proof = proof;
|
||||
storeConfig.proofs.ipfs[req.params.cid].proof = {
|
||||
version: '0.1.0-alpha.7',
|
||||
time: proof.time,
|
||||
sent: proof.sent,
|
||||
recv: proof.recv,
|
||||
notaryUrl: notaryUrl,
|
||||
websocketProxyUrl: jsonProof.meta.websocketProxyUrl,
|
||||
notaryKey: Buffer.from(
|
||||
notaryPem
|
||||
.replace('-----BEGIN PUBLIC KEY-----', '')
|
||||
.replace('-----END PUBLIC KEY-----', '')
|
||||
.replace(/\n/g, ''),
|
||||
'base64',
|
||||
)
|
||||
.slice(23)
|
||||
.toString('hex'),
|
||||
};
|
||||
}
|
||||
|
||||
const store = configureAppStore(storeConfig);
|
||||
@@ -205,14 +224,7 @@ app.listen(port, () => {
|
||||
});
|
||||
|
||||
async function fetchPublicKeyFromNotary(notaryUrl: string) {
|
||||
let host;
|
||||
|
||||
let httpUrl = notaryUrl.replace(/^ws:/, 'http:').replace(/^wss:/, 'https:');
|
||||
let hostMatch = httpUrl.match(/^(https?:\/\/[^\/]+)/);
|
||||
if (hostMatch) {
|
||||
host = hostMatch[1];
|
||||
}
|
||||
const res = await fetch(host + '/info');
|
||||
const res = await fetch(notaryUrl + '/info');
|
||||
const json: any = await res.json();
|
||||
if (!json.publicKey) throw new Error('invalid response');
|
||||
return json.publicKey;
|
||||
|
||||
@@ -7,7 +7,10 @@ import React, {
|
||||
} from 'react';
|
||||
import c from 'classnames';
|
||||
import classNames from 'classnames';
|
||||
import { Attestation, Proof as VerifiedProof } from '../../utils/types/types';
|
||||
import {
|
||||
Attestation,
|
||||
AttestedData as VerifiedProof,
|
||||
} from '../../utils/types/types';
|
||||
import Modal, { ModalContent, ModalFooter, ModalHeader } from '../Modal';
|
||||
import Icon from '../Icon';
|
||||
import { useDispatch } from 'react-redux';
|
||||
@@ -55,15 +58,15 @@ export default function ProofViewer(props: {
|
||||
)}
|
||||
<div className="flex flex-col px-2">
|
||||
<div className="flex flex-row gap-2 items-center">
|
||||
<TabLabel onClick={() => setTab('info')} active={tab === 'info'}>
|
||||
Info
|
||||
</TabLabel>
|
||||
<TabLabel onClick={() => setTab('sent')} active={tab === 'sent'}>
|
||||
Sent
|
||||
</TabLabel>
|
||||
<TabLabel onClick={() => setTab('recv')} active={tab === 'recv'}>
|
||||
Recv
|
||||
</TabLabel>
|
||||
<TabLabel onClick={() => setTab('info')} active={tab === 'info'}>
|
||||
Metadata
|
||||
</TabLabel>
|
||||
<div className="flex flex-row flex-grow items-center justify-end">
|
||||
<button className="button" onClick={onClickShare}>
|
||||
Share
|
||||
@@ -73,15 +76,30 @@ export default function ProofViewer(props: {
|
||||
</div>
|
||||
<div className="flex flex-col flex-grow px-2">
|
||||
{tab === 'info' && (
|
||||
<div className="w-full bg-slate-100 text-slate-800 border p-2 text-xs break-all h-full outline-none font-mono">
|
||||
<div>
|
||||
<div>Notary URL:</div>
|
||||
<div>
|
||||
{props.proof.version === '0.1.0-alpha.7'
|
||||
? props.proof.meta.notaryUrl
|
||||
: <></>}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col w-full bg-slate-100 text-slate-800 border p-2 text-xs break-all h-full outline-none font-mono gap-4">
|
||||
<MetadataRow label="Version" value={props.verifiedProof.version} />
|
||||
<MetadataRow
|
||||
label="Notary URL"
|
||||
value={props.verifiedProof.notaryUrl}
|
||||
/>
|
||||
{props.verifiedProof.websocketProxyUrl && (
|
||||
<MetadataRow
|
||||
label="Websocket Proxy URL"
|
||||
value={props.verifiedProof.websocketProxyUrl}
|
||||
/>
|
||||
)}
|
||||
{props.verifiedProof.verifierKey && (
|
||||
<MetadataRow
|
||||
label="Verifying Key"
|
||||
value={props.verifiedProof.verifierKey}
|
||||
/>
|
||||
)}
|
||||
{props.verifiedProof.notaryKey && (
|
||||
<MetadataRow
|
||||
label="Notary Key"
|
||||
value={props.verifiedProof.notaryKey}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{tab === 'sent' && (
|
||||
@@ -103,6 +121,15 @@ export default function ProofViewer(props: {
|
||||
);
|
||||
}
|
||||
|
||||
function MetadataRow({ label, value }: { label: string; value: string }) {
|
||||
return (
|
||||
<div>
|
||||
<div>{label}:</div>
|
||||
<div className="text-sm font-semibold whitespace-pre-wrap">{value}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function TabLabel(props: {
|
||||
children: ReactNode;
|
||||
onClick: MouseEventHandler;
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useDispatch } from 'react-redux';
|
||||
import ProofViewer from '../ProofViewer';
|
||||
import { FileDropdown } from '../FileDropdown';
|
||||
import { PubkeyInput } from '../../pages/PubkeyInput';
|
||||
import { Proof } from '../../utils/types/types';
|
||||
import { AttestedData } from '../../utils/types/types';
|
||||
import { File } from '@web-std/file';
|
||||
import { verify } from '../../utils';
|
||||
|
||||
@@ -18,7 +18,7 @@ export default function SharedProof(): ReactElement {
|
||||
const file = new File([JSON.stringify(proofData?.raw)], `${cid}.json`, {
|
||||
type: 'text/plain',
|
||||
});
|
||||
const [verifiedProof, setVerifiedProof] = useState<Proof | null>(
|
||||
const [verifiedProof, setVerifiedProof] = useState<AttestedData | null>(
|
||||
proofData?.proof || null,
|
||||
);
|
||||
|
||||
|
||||
@@ -3,7 +3,10 @@ import { useDispatch } from 'react-redux';
|
||||
import { readFileAsync, safeParseJSON, verify } from '../../utils';
|
||||
import FileUploadInput from '../../components/FileUploadInput';
|
||||
import ProofViewer from '../../components/ProofViewer';
|
||||
import { Attestation, Proof as VerifiedProof } from '../../utils/types/types';
|
||||
import {
|
||||
Attestation,
|
||||
AttestedData as VerifiedProof,
|
||||
} from '../../utils/types/types';
|
||||
import { FileDropdown } from '../../components/FileDropdown';
|
||||
import { PubkeyInput } from '../PubkeyInput';
|
||||
|
||||
@@ -25,6 +28,7 @@ export default function FileDrop(): ReactElement {
|
||||
setVerifiedProof(resp);
|
||||
setStep('result');
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
if (e?.message !== 'Failed to fetch') {
|
||||
setError(
|
||||
typeof e === 'string'
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { Proof } from 'tlsn-js-v5/build/types';
|
||||
import { useSelector } from 'react-redux';
|
||||
import deepEqual from 'fast-deep-equal';
|
||||
import { EXPLORER_URL } from '../utils/constants';
|
||||
import { Attestation } from '../utils/types/types';
|
||||
import { Attestation, AttestedData } from '../utils/types/types';
|
||||
import { verify } from '../utils';
|
||||
|
||||
enum ActionType {
|
||||
@@ -18,19 +18,14 @@ export type Action<payload = any> = {
|
||||
meta?: any;
|
||||
};
|
||||
|
||||
type ProofData = {
|
||||
type AttestationData = {
|
||||
raw: Attestation;
|
||||
proof?: {
|
||||
time: number;
|
||||
sent: string;
|
||||
recv: string;
|
||||
notaryUrl: string;
|
||||
};
|
||||
proof?: AttestedData;
|
||||
};
|
||||
|
||||
type State = {
|
||||
ipfs: {
|
||||
[cid: string]: ProofData;
|
||||
[cid: string]: AttestationData;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -66,7 +61,7 @@ export const fetchProofFromIPFS =
|
||||
};
|
||||
|
||||
export const setIPFSProof = (
|
||||
payload: ProofData & {
|
||||
payload: AttestationData & {
|
||||
cid: string;
|
||||
},
|
||||
) => ({
|
||||
@@ -76,16 +71,11 @@ export const setIPFSProof = (
|
||||
|
||||
export default function proofs(
|
||||
state = initState,
|
||||
action: Action<{
|
||||
cid: string;
|
||||
raw: Proof;
|
||||
proof: {
|
||||
time: number;
|
||||
sent: string;
|
||||
recv: string;
|
||||
notaryUrl: string;
|
||||
};
|
||||
}>,
|
||||
action: Action<
|
||||
AttestationData & {
|
||||
cid: string;
|
||||
}
|
||||
>,
|
||||
): State {
|
||||
switch (action.type) {
|
||||
case ActionType.SetIPFSProof:
|
||||
@@ -104,7 +94,7 @@ export default function proofs(
|
||||
}
|
||||
}
|
||||
|
||||
export const useIPFSProof = (cid?: string): ProofData | null => {
|
||||
export const useIPFSProof = (cid?: string): AttestationData | null => {
|
||||
return useSelector((state: AppRootState) => {
|
||||
if (!cid) return null;
|
||||
return state.proofs.ipfs[cid] || null;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AppRootState } from '.';
|
||||
import type { Proof } from '../utils/types/types';
|
||||
import type { AttestedData } from '../utils/types/types';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
export enum ActionType {
|
||||
@@ -8,7 +8,7 @@ export enum ActionType {
|
||||
UploadFileSuccess = 'proofupload/uploadFileSuccess',
|
||||
}
|
||||
|
||||
export const uploadFile = (fileName: string, proof: Proof) => ({
|
||||
export const uploadFile = (fileName: string, proof: AttestedData) => ({
|
||||
type: ActionType.AddFile,
|
||||
payload: { fileName, proof },
|
||||
});
|
||||
@@ -31,8 +31,12 @@ export type Action<payload = any> = {
|
||||
};
|
||||
|
||||
type State = {
|
||||
proofs: { fileName: string; proof: Proof }[];
|
||||
selectedProof?: { fileName: string; proof: Proof; ipfsCID?: string } | null;
|
||||
proofs: { fileName: string; proof: AttestedData }[];
|
||||
selectedProof?: {
|
||||
fileName: string;
|
||||
proof: AttestedData;
|
||||
ipfsCID?: string;
|
||||
} | null;
|
||||
};
|
||||
|
||||
const initState: State = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { ReactElement, useRef } from 'react';
|
||||
import { Attestation, Proof } from './types/types';
|
||||
import { Attestation, AttestedData } from './types/types';
|
||||
|
||||
export const readFileAsync = (file: File): Promise<string> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -98,36 +98,50 @@ async function initTlsnJs() {
|
||||
export async function verify(
|
||||
attestation: Attestation,
|
||||
pubKey: string,
|
||||
): Promise<Proof> {
|
||||
): Promise<AttestedData> {
|
||||
let key = pubKey;
|
||||
const { NotaryServer } = await import('tlsn-js');
|
||||
console.log(attestation.version)
|
||||
await initTlsnJs();
|
||||
|
||||
switch (attestation.version) {
|
||||
case undefined: {
|
||||
const { verify } = await import('tlsn-js-v5');
|
||||
key = key || (await NotaryServer.from(attestation.notaryUrl).publicKey());
|
||||
return await verify(attestation, key);
|
||||
key =
|
||||
key ||
|
||||
(await NotaryServer.from(attestation.notaryUrl).publicKey('pem'));
|
||||
const data = await verify(attestation, key);
|
||||
return {
|
||||
...data,
|
||||
version: '0.1.0-alpha.5',
|
||||
notaryUrl: attestation.notaryUrl,
|
||||
notaryKey: key,
|
||||
};
|
||||
}
|
||||
case '0.1.0-alpha.7': {
|
||||
const { Presentation, Transcript } = await import('tlsn-js');
|
||||
console.log(Buffer.from(attestation.data, 'hex').toString('binary'));
|
||||
await initTlsnJs();
|
||||
key =
|
||||
key ||
|
||||
(await NotaryServer.from(attestation.meta.notaryUrl).publicKey());
|
||||
const tlsProof = new Presentation(attestation.data);
|
||||
console.log(tlsProof);
|
||||
const data = await tlsProof.verify();
|
||||
const transcript = new Transcript({
|
||||
sent: data.transcript.sent,
|
||||
recv: data.transcript.recv,
|
||||
})
|
||||
const vk = tlsProof.verifyingKey();
|
||||
});
|
||||
const vk = await tlsProof.verifyingKey();
|
||||
const verifyingKey = Buffer.from(vk.data).toString('hex');
|
||||
const notaryUrl = convertNotaryWsToHttp(attestation.meta.notaryUrl);
|
||||
const publicKey = await new NotaryServer(notaryUrl).publicKey();
|
||||
|
||||
if (verifyingKey !== publicKey)
|
||||
throw new Error(`Notary key doesn't match verifying key`);
|
||||
|
||||
return {
|
||||
version: '0.1.0-alpha.7',
|
||||
sent: transcript.sent(),
|
||||
recv: transcript.recv(),
|
||||
time: data.connection_info.time,
|
||||
notaryUrl: attestation.meta.notaryUrl,
|
||||
notaryUrl: notaryUrl,
|
||||
notaryKey: publicKey,
|
||||
websocketProxyUrl: attestation.meta.websocketProxyUrl,
|
||||
verifierKey: verifyingKey,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -135,6 +149,13 @@ export async function verify(
|
||||
throw new Error('Invalid Proof');
|
||||
}
|
||||
|
||||
export function convertNotaryWsToHttp(notaryWs: string) {
|
||||
const { protocol, pathname, hostname } = new URL(notaryWs);
|
||||
const p = protocol === 'wss:' ? 'https:' : 'http';
|
||||
const path = pathname === '/' ? '' : pathname.replace('/notarize', '');
|
||||
return p + '//' + hostname + path;
|
||||
}
|
||||
|
||||
function defer(): {
|
||||
promise: Promise<any>;
|
||||
resolve: (args?: any) => any;
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
export interface Proof {
|
||||
export interface AttestedData {
|
||||
version: '0.1.0-alpha.7' | '0.1.0-alpha.5';
|
||||
time: number;
|
||||
sent: string;
|
||||
recv: string;
|
||||
notaryUrl: string;
|
||||
notaryKey: string;
|
||||
websocketProxyUrl?: string;
|
||||
verifierKey?: string;
|
||||
}
|
||||
|
||||
export type AttestationV0 = {
|
||||
@@ -13,7 +17,7 @@ export type AttestationV0 = {
|
||||
};
|
||||
|
||||
export type AttestationV1 = {
|
||||
version: '0.1.0-alpha.7' | '0.1.0-alpha.6';
|
||||
version: '0.1.0-alpha.7';
|
||||
data: string;
|
||||
meta: {
|
||||
notaryUrl: string;
|
||||
|
||||
Reference in New Issue
Block a user