parent
aa5e436c1b
commit
efacc8eba5
@ -0,0 +1,37 @@
|
||||
---
|
||||
layout: page
|
||||
title: Affiliates
|
||||
subtitle: Support NixNet by making purchases and using these links
|
||||
description: Support NixNet by making purchases with affiliate links and vouchers
|
||||
---
|
||||
One of the easiest ways to support NixNet is to purchase products on these websites using the provided affiliate links.
|
||||
|
||||
# netcup
|
||||
[netcup](https://netcup.eu) is currently where the most services are running. They have fantastic offerings at most price points and incredible deals. I've been very happy with their service and *highly* recommend them.
|
||||
|
||||
## Voucher codes
|
||||
These are single-use vouchers for various products. When you use one, please [send me a message](/contact) so I can generate a new one.
|
||||
|
||||
### 5€ for anything except domains
|
||||
* [36nc15758387844](https://www.netcup.eu/bestellen/gutschein_einloesen.php?gutschein=36nc15758387844)
|
||||
* [36nc15758387843](https://www.netcup.eu/bestellen/gutschein_einloesen.php?gutschein=36nc15758387843)
|
||||
* [36nc15758387842](https://www.netcup.eu/bestellen/gutschein_einloesen.php?gutschein=36nc15758387842)
|
||||
* [36nc15758387841](https://www.netcup.eu/bestellen/gutschein_einloesen.php?gutschein=36nc15758387841)
|
||||
* [36nc15758387840](https://www.netcup.eu/bestellen/gutschein_einloesen.php?gutschein=36nc15758387840)
|
||||
|
||||
### 10% off the 200 G8
|
||||
* [2052nc15758390090](https://www.netcup.eu/bestellen/gutschein_einloesen.php?gutschein=2052nc15758390090)
|
||||
* [2052nc15758390091](https://www.netcup.eu/bestellen/gutschein_einloesen.php?gutschein=2052nc15758390091)
|
||||
|
||||
### 10% off the 500 G8
|
||||
* [2053nc15758393980](https://www.netcup.eu/bestellen/gutschein_einloesen.php?gutschein=2053nc15758393980)
|
||||
* [2053nc15758393981](https://www.netcup.eu/bestellen/gutschein_einloesen.php?gutschein=2053nc15758393981)
|
||||
|
||||
### 10% off the 1000 G8
|
||||
* [2054nc15758394201](https://www.netcup.eu/bestellen/gutschein_einloesen.php?gutschein=2054nc15758394201)
|
||||
* [2054nc15758394200](https://www.netcup.eu/bestellen/gutschein_einloesen.php?gutschein=2054nc15758394200)
|
||||
|
||||
### 10% off the 2000 G8
|
||||
* [2056nc15758394800](https://www.netcup.eu/bestellen/gutschein_einloesen.php?gutschein=2056nc15758394800)
|
||||
* [2056nc15758394801](https://www.netcup.eu/bestellen/gutschein_einloesen.php?gutschein=2056nc15758394801)
|
||||
|
@ -1,108 +0,0 @@
|
||||
|
||||
$(function(){
|
||||
|
||||
var model = {
|
||||
// renomear
|
||||
dat: [
|
||||
],
|
||||
read_json: function() {
|
||||
$.ajax({
|
||||
url: "/cards.json",
|
||||
dataType: 'json',
|
||||
async: false,
|
||||
success: function(data) {
|
||||
$(data["cards"]).each(function(){
|
||||
var card_info = $(this)[0];
|
||||
model.set_data(card_info);
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
set_data: function(d) {
|
||||
model.dat.push(d);
|
||||
},
|
||||
get_data: function() {
|
||||
return model.dat;
|
||||
},
|
||||
filter_data: function(conteudo) {
|
||||
var $d = model.dat;
|
||||
var $filter = [];
|
||||
var $c = conteudo.toLowerCase();
|
||||
$($d).each(function(i){
|
||||
if(
|
||||
$d[i].name.toLowerCase().indexOf($c) >= 0 ||
|
||||
$d[i].description.toLowerCase().indexOf($c) >= 0 ||
|
||||
$d[i].button_text.toLowerCase().indexOf($c) >= 0
|
||||
) {
|
||||
$filter.push($d[i]);
|
||||
}
|
||||
});
|
||||
return $filter;
|
||||
},
|
||||
init: function() {
|
||||
model.read_json();
|
||||
}
|
||||
};
|
||||
var octopus = {
|
||||
init: function() {
|
||||
model.init();
|
||||
view.init();
|
||||
octopus.create_card(model.get_data());
|
||||
},
|
||||
create_card: function(d) {
|
||||
$.each(d, function(i){
|
||||
view.create_card(d[i].name, d[i].description, d[i].button_text, d[i].link, d[i].tor);
|
||||
});
|
||||
},
|
||||
filter_data: function(c) {
|
||||
var d = model.filter_data(c);
|
||||
$.each(d, function(i){
|
||||
view.create_card(d[i].name, d[i].description, d[i].button_text, d[i].link, d[i].tor);
|
||||
});
|
||||
},
|
||||
recreate_cards: function() {
|
||||
octopus.create_card(model.get_data());
|
||||
}
|
||||
};
|
||||
|
||||
// Renomear
|
||||
var view = {
|
||||
init: function() {
|
||||
this.container = $(".flex-cards");
|
||||
this.search_field = $("#search_field");
|
||||
view.events();
|
||||
},
|
||||
events: function(){
|
||||
this.search_field.on('input',function(){
|
||||
var $conteudo = $(this).val();
|
||||
view.clean_cards();
|
||||
if($conteudo.length == 0){
|
||||
octopus.recreate_cards();
|
||||
}else {
|
||||
octopus.filter_data($conteudo);
|
||||
}
|
||||
});
|
||||
},
|
||||
create_card: function(name, desc, bt_txt, link, tor){
|
||||
var $card = $("<div>").addClass("card");
|
||||
var $h1 = $("<h1>").text(name);
|
||||
var $desc = $("<p>").text(desc);
|
||||
var $button = $("<button>").addClass("button").text(bt_txt);
|
||||
var $icon = $("<button>").addClass("tor").text("Tor");
|
||||
var $a = $("<a>").attr("href", link);
|
||||
var $tor = $("<a>").attr("href", tor);
|
||||
$a.append($button);
|
||||
$tor.append($icon);
|
||||
if (tor == "#") {
|
||||
$card.append($h1).append($desc).append($a);
|
||||
} else {
|
||||
$card.append($h1).append($desc).append($a).append($tor);
|
||||
}
|
||||
this.container.append($card);
|
||||
},
|
||||
clean_cards: function() {
|
||||
this.container.empty();
|
||||
}
|
||||
};
|
||||
octopus.init();
|
||||
});
|
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 6.2 KiB |
@ -1,4 +0,0 @@
|
||||
{
|
||||
"title": "NixNet",
|
||||
"description": "NixNet.xyz is a network of websites and services hosted by the pseudonymous Amolith (me). It's also a blog!"
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -1,537 +0,0 @@
|
||||
System.register("local", [], function (exports_1, context_1) {
|
||||
"use strict";
|
||||
var __moduleName = context_1 && context_1.id;
|
||||
function createElement(name, attributes, ...children) {
|
||||
return {
|
||||
name,
|
||||
attributes: attributes || {},
|
||||
children: Array.prototype.concat(...(children || []))
|
||||
};
|
||||
}
|
||||
exports_1("createElement", createElement);
|
||||
return {
|
||||
setters: [],
|
||||
execute: function () {
|
||||
}
|
||||
};
|
||||
});
|
||||
System.register("renderer", [], function (exports_2, context_2) {
|
||||
"use strict";
|
||||
var __moduleName = context_2 && context_2.id;
|
||||
function render(element) {
|
||||
if (element == null)
|
||||
return '';
|
||||
if (typeof element !== "object")
|
||||
element = String(element);
|
||||
if (typeof element === "string")
|
||||
return element.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
//if (element instanceof Raw) return element.html;
|
||||
console.assert(!!element.attributes, 'Element attributes must be defined:\n' + JSON.stringify(element));
|
||||
const elementAttributes = element.attributes;
|
||||
let attributes = Object.keys(elementAttributes).filter(key => {
|
||||
const value = elementAttributes[key];
|
||||
return value != null;
|
||||
}).map(key => {
|
||||
const value = elementAttributes[key];
|
||||
if (value === true) {
|
||||
return key;
|
||||
}
|
||||
return `${key}="${String(value).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"')}"`;
|
||||
}).join(' ');
|
||||
if (attributes.length > 0) {
|
||||
attributes = ' ' + attributes;
|
||||
}
|
||||
const children = element.children.length > 0 ? `>${element.children.map(child => render(child)).join('')}` : '>';
|
||||
return `<${element.name}${attributes}${children}</${element.name}>`;
|
||||
}
|
||||
exports_2("render", render);
|
||||
return {
|
||||
setters: [],
|
||||
execute: function () {
|
||||
}
|
||||
};
|
||||
});
|
||||
/*
|
||||
Copyright 2019 Wiktor Kwapisiewicz
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
System.register("openpgp-key", ["local", "renderer"], function (exports_3, context_3) {
|
||||
"use strict";
|
||||
var local, renderer, proofs, dateFormat;
|
||||
var __moduleName = context_3 && context_3.id;
|
||||
function getLatestSignature(signatures, date = new Date()) {
|
||||
let signature = signatures[0];
|
||||
for (let i = 1; i < signatures.length; i++) {
|
||||
if (signatures[i].created >= signature.created &&
|
||||
(signatures[i].created <= date || date === null)) {
|
||||
signature = signatures[i];
|
||||
}
|
||||
}
|
||||
return signature;
|
||||
}
|
||||
function getVerifier(proofUrl, fingerprint) {
|
||||
for (const proof of proofs) {
|
||||
const matches = proofUrl.match(proof.matcher);
|
||||
if (!matches)
|
||||
continue;
|
||||
const bound = Object.entries(proof.variables).map(([key, value]) => [key, matches[value || 0]]).reduce((previous, current) => { previous[current[0]] = current[1]; return previous; }, { FINGERPRINT: fingerprint });
|
||||
const profile = proof.profile.replace(/\{([A-Z]+)\}/g, (_, name) => bound[name]);
|
||||
const proofJson = proof.proof.replace(/\{([A-Z]+)\}/g, (_, name) => bound[name]);
|
||||
const username = proof.username.replace(/\{([A-Z]+)\}/g, (_, name) => bound[name]);
|
||||
return {
|
||||
profile,
|
||||
proofUrl,
|
||||
proofJson,
|
||||
username,
|
||||
service: proof.service,
|
||||
checks: (proof.checks || []).map((check) => ({
|
||||
relation: check.relation,
|
||||
proof: check.proof,
|
||||
claim: check.claim.replace(/\{([A-Z]+)\}/g, (_, name) => bound[name])
|
||||
}))
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
async function verify(proofJson, checks) {
|
||||
const response = await fetch(proofJson, {
|
||||
headers: {
|
||||
Accept: 'application/json'
|
||||
},
|
||||
credentials: 'omit'
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error('Response failed: ' + response.status);
|
||||
}
|
||||
const json = await response.json();
|
||||
for (const check of checks) {
|
||||
const proofValue = check.proof.reduce((previous, current) => {
|
||||
if (current == null || previous == null)
|
||||
return null;
|
||||
if (Array.isArray(previous) && typeof current === 'string') {
|
||||
return previous.map(value => value[current]);
|
||||
}
|
||||
return previous[current];
|
||||
}, json);
|
||||
const claimValue = check.claim;
|
||||
if (check.relation === 'eq') {
|
||||
if (proofValue !== claimValue) {
|
||||
throw new Error(`Proof value ${proofValue} !== claim value ${claimValue}`);
|
||||
}
|
||||
}
|
||||
else if (check.relation === 'contains') {
|
||||
if (!proofValue || proofValue.indexOf(claimValue) === -1) {
|
||||
throw new Error(`Proof value ${proofValue} does not contain claim value ${claimValue}`);
|
||||
}
|
||||
}
|
||||
else if (check.relation === 'oneOf') {
|
||||
if (!proofValue || proofValue.indexOf(claimValue) === -1) {
|
||||
throw new Error(`Proof value ${proofValue} does not contain claim value ${claimValue}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function serviceToClassName(service) {
|
||||
if (service === 'github') {
|
||||
return 'fab fa-github';
|
||||
}
|
||||
else if (service === 'reddit') {
|
||||
return 'fab fa-reddit';
|
||||
}
|
||||
else if (service === 'hackernews') {
|
||||
return 'fab fa-hacker-news';
|
||||
}
|
||||
else if (service === 'mastodon') {
|
||||
return 'fab fa-mastodon';
|
||||
}
|
||||
else if (service === 'dns') {
|
||||
return 'fas fa-globe';
|
||||
}
|
||||
else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
async function lookupKey(query) {
|
||||
const result = document.getElementById('result');
|
||||
result.innerHTML = renderer.render(local.createElement("span", null,
|
||||
"Looking up ",
|
||||
query,
|
||||
"..."));
|
||||
let keys, keyUrl;
|
||||
const keyLink = document.querySelector('[rel="pgpkey"]');
|
||||
if (!keyLink) {
|
||||
const keyserver = document.querySelector('meta[name="keyserver"]').content;
|
||||
keyUrl = `https://${keyserver}/pks/lookup?op=get&options=mr&search=${query}`;
|
||||
const response = await fetch(keyUrl);
|
||||
const key = await response.text();
|
||||
keys = (await openpgp.key.readArmored(key)).keys;
|
||||
}
|
||||
else {
|
||||
keyUrl = keyLink.href;
|
||||
const response = await fetch(keyUrl);
|
||||
const key = await response.arrayBuffer();
|
||||
keys = (await openpgp.key.read(new Uint8Array(key))).keys;
|
||||
}
|
||||
if (keys.length > 0) {
|
||||
loadKeys(keyUrl, keys).catch(e => {
|
||||
result.innerHTML = renderer.render(local.createElement("span", null,
|
||||
"Could not display this key: ",
|
||||
String(e)));
|
||||
});
|
||||
}
|
||||
else {
|
||||
result.innerHTML = renderer.render(local.createElement("span", null,
|
||||
query,
|
||||
": not found"));
|
||||
}
|
||||
}
|
||||
async function loadKeys(keyUrl, _keys) {
|
||||
const key = _keys[0];
|
||||
window.key = key;
|
||||
const primaryUser = await key.getPrimaryUser();
|
||||
for (var i = key.users.length - 1; i >= 0; i--) {
|
||||
try {
|
||||
if (await key.users[i].verify(key.primaryKey) === openpgp.enums.keyStatus.valid) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.error('User verification error:', e);
|
||||
}
|
||||
//key.users.splice(i, 1);
|
||||
}
|
||||
for (const user of key.users) {
|
||||
user.revoked = await user.isRevoked();
|
||||
}
|
||||
const lastPrimarySig = primaryUser.selfCertification;
|
||||
const keys = [{
|
||||
fingerprint: key.primaryKey.getFingerprint(),
|
||||
status: await key.verifyPrimaryKey(),
|
||||
keyFlags: lastPrimarySig.keyFlags,
|
||||
created: key.primaryKey.created,
|
||||
algorithmInfo: key.primaryKey.getAlgorithmInfo(),
|
||||
expirationTime: lastPrimarySig.getExpirationTime()
|
||||
}];
|
||||
//console.log(lastPrimarySig);
|
||||
const proofs = (lastPrimarySig.notations || [])
|
||||
.filter((notation) => notation[0] === 'proof@metacode.biz' && typeof notation[1] === 'string')
|
||||
.map((notation) => notation[1])
|
||||
.map((proofUrl) => getVerifier(proofUrl, key.primaryKey.getFingerprint()))
|
||||
.filter((verifier) => !!verifier);
|
||||
/*
|
||||
proofs.push(getVerifier('https://www.reddit.com/user/wiktor-k/comments/bo5oih/test/', key.primaryKey.getFingerprint()));
|
||||
proofs.push(getVerifier('https://news.ycombinator.com/user?id=wiktor-k', key.primaryKey.getFingerprint()));
|
||||
proofs.push(getVerifier('https://gist.github.com/wiktor-k/389d589dd19250e1f9a42bc3d5d40c16', key.primaryKey.getFingerprint()));
|
||||
proofs.push(getVerifier('https://metacode.biz/@wiktor', key.primaryKey.getFingerprint()));
|
||||
proofs.push(getVerifier('dns:metacode.biz?type=TXT', key.primaryKey.getFingerprint()));
|
||||
*/
|
||||
for (const subKey of key.subKeys) {
|
||||
const lastSig = getLatestSignature(subKey.bindingSignatures);
|
||||
let reasonForRevocation;
|
||||
if (subKey.revocationSignatures.length > 0) {
|
||||
reasonForRevocation = subKey.revocationSignatures[subKey.revocationSignatures.length - 1].reasonForRevocationString;
|
||||
}
|
||||
keys.push({
|
||||
fingerprint: subKey.keyPacket.getFingerprint(),
|
||||
status: await subKey.verify(key.primaryKey),
|
||||
reasonForRevocation,
|
||||
keyFlags: lastSig.keyFlags,
|
||||
created: lastSig.created,
|
||||
algorithmInfo: subKey.keyPacket.getAlgorithmInfo(),
|
||||
expirationTime: await subKey.getExpirationTime()
|
||||
});
|
||||
}
|
||||
//key.users.splice(primaryUser.index, 1);
|
||||
const profileHash = await openpgp.crypto.hash.md5(openpgp.util.str_to_Uint8Array(primaryUser.user.userId.email)).then((u) => openpgp.util.str_to_hex(openpgp.util.Uint8Array_to_str(u)));
|
||||
const now = new Date();
|
||||
// there is index property on primaryUser
|
||||
document.title = primaryUser.user.userId.name + ' - OpenPGP key';
|
||||
const info = local.createElement("div", null,
|
||||
local.createElement("div", { class: "wrapper" },
|
||||
local.createElement("div", { class: "bio" },
|
||||
local.createElement("img", { class: "avatar", src: "https://seccdn.libravatar.org/avatar/" + profileHash + "?s=148&d=" + encodeURIComponent("https://www.gravatar.com/avatar/" + profileHash + "?s=148&d=mm") }),
|
||||
local.createElement("h2", null, primaryUser.user.userId.name)),
|
||||
local.createElement("div", null,
|
||||
local.createElement("ul", { class: "props" },
|
||||
local.createElement("li", { title: key.primaryKey.getFingerprint() },
|
||||
local.createElement("a", { href: keyUrl, target: "_blank", rel: "nofollow noopener" },
|
||||
"\uD83D\uDD11\u00A0",
|
||||
local.createElement("code", null, key.primaryKey.getFingerprint()))),
|
||||
key.users.filter((user) => !user.revoked && user.userId).map((user) => user.userId.email).filter((email) => !!email).map((email) => local.createElement("li", null,
|
||||
local.createElement("a", { href: "mailto:" + email },
|
||||
"\uD83D\uDCE7 ",
|
||||
email
|
||||
//formatAttribute(user.userAttribute)
|
||||
))),
|
||||
proofs.filter((proof) => !!proof).map((proof) => local.createElement("li", null,
|
||||
local.createElement("a", { rel: "me noopener nofollow", target: "_blank", href: proof.profile },
|
||||
local.createElement("i", { class: serviceToClassName(proof.service) }),
|
||||
proof.username),
|
||||
local.createElement("a", { rel: "noopener nofollow", target: "_blank", href: proof.proofUrl, class: "proof", "data-proof-json": proof.proofJson, "data-checks": JSON.stringify(proof.checks) },
|
||||
local.createElement("i", { class: "fas fa-certificate" }),
|
||||
"proof")))))),
|
||||
local.createElement("details", null,
|
||||
local.createElement("summary", null, "\uD83D\uDD12 Encrypt"),
|
||||
local.createElement("textarea", { placeholder: "Message to encrypt...", id: "message" }),
|
||||
local.createElement("input", { type: "button", value: "Encrypt", id: "encrypt" }),
|
||||
' ',
|
||||
local.createElement("input", { type: "button", id: "send", "data-recipient": primaryUser.user.userId.email, value: "Send to " + primaryUser.user.userId.email })),
|
||||
local.createElement("details", null,
|
||||
local.createElement("summary", null, "\uD83D\uDD8B Verify"),
|
||||
local.createElement("textarea", { placeholder: "Clearsigned message to verify...", id: "signed" }),
|
||||
local.createElement("input", { type: "button", value: "Verify", id: "verify" })),
|
||||
local.createElement("details", null,
|
||||
local.createElement("summary", null, "\uD83D\uDD11 Key details"),
|
||||
local.createElement("p", null, "Subkeys:"),
|
||||
local.createElement("ul", null, keys.map((subKey) => local.createElement("li", null,
|
||||
local.createElement("div", null,
|
||||
getStatus(subKey.status, subKey.reasonForRevocation),
|
||||
" ",
|
||||
getIcon(subKey.keyFlags),
|
||||
" ",
|
||||
local.createElement("code", null, subKey.fingerprint.substring(24).match(/.{4}/g).join(" ")),
|
||||
" ",
|
||||
formatAlgorithm(subKey.algorithmInfo.algorithm),
|
||||
" (",
|
||||
subKey.algorithmInfo.bits,
|
||||
")"),
|
||||
local.createElement("div", null,
|
||||
"created: ",
|
||||
formatDate(subKey.created),
|
||||
", expire",
|
||||
now > subKey.expirationTime ? "d" : "s",
|
||||
": ",
|
||||
formatDate(subKey.expirationTime)))))));
|
||||
document.getElementById('result').innerHTML = renderer.render(info);
|
||||
checkProofs();
|
||||
}
|
||||
async function checkProofs() {
|
||||
const proofs = document.querySelectorAll('[data-checks]');
|
||||
for (const proofLink of proofs) {
|
||||
const checks = JSON.parse(proofLink.dataset.checks || '');
|
||||
const url = proofLink.dataset.proofJson || '';
|
||||
try {
|
||||
await verify(url, checks);
|
||||
proofLink.textContent = 'verified proof';
|
||||
proofLink.classList.add('verified');
|
||||
}
|
||||
catch (e) {
|
||||
console.error('Could not verify proof: ' + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
async function verifyProof(e) {
|
||||
const target = e.target;
|
||||
if (target.id === 'encrypt') {
|
||||
const text = document.getElementById('message');
|
||||
openpgp.encrypt({
|
||||
message: openpgp.message.fromText(text.value),
|
||||
publicKeys: [window.key],
|
||||
armor: true
|
||||
}).then((cipherText) => {
|
||||
text.value = cipherText.data;
|
||||
}, (e) => alert(e));
|
||||
}
|
||||
else if (target.id === 'send') {
|
||||
location.href = "mailto:" + target.dataset.recipient + "?subject=Encrypted%20message&body=" + encodeURIComponent(document.getElementById('message').value);
|
||||
}
|
||||
else if (target.id === 'verify') {
|
||||
const text = document.getElementById('signed');
|
||||
const message = await openpgp.cleartext.readArmored(text.value);
|
||||
const verified = await openpgp.verify({
|
||||
message,
|
||||
publicKeys: [window.key]
|
||||
});
|
||||
console.log(verified);
|
||||
alert('The signature is ' + (verified.signatures[0].valid ? '✅ correct.' : '❌ incorrect.'));
|
||||
}
|
||||
}
|
||||
function formatAttribute(userAttribute) {
|
||||
if (userAttribute.attributes[0][0] === String.fromCharCode(1)) {
|
||||
return local.createElement("img", { src: "data:image/jpeg;base64," + btoa(userAttribute.attributes[0].substring(17)) });
|
||||
}
|
||||
if (userAttribute.attributes[0][0] === 'e') {
|
||||
const url = userAttribute.attributes[0].substring(userAttribute.attributes[0].indexOf('@') + 1);
|
||||
return local.createElement("a", { href: url, rel: "noopener nofollow" }, url);
|
||||
}
|
||||
return 'unknown attribute';
|
||||
}
|
||||
function formatAlgorithm(name) {
|
||||
if (name === 'rsa_encrypt_sign')
|
||||
return "RSA";
|
||||
return name;
|
||||
}
|
||||
function formatDate(date) {
|
||||
if (date === Infinity)
|
||||
return "never";
|
||||
if (typeof date === 'number')
|
||||
return 'x';
|
||||
return dateFormat.format(date);
|
||||
}
|
||||
function getStatus(status, details) {
|
||||
if (status === openpgp.enums.keyStatus.invalid) {
|
||||
return local.createElement("span", { title: "Invalid key" }, "\u274C");
|
||||
}
|
||||
if (status === openpgp.enums.keyStatus.expired) {
|
||||
return local.createElement("span", { title: "Key expired" }, "\u23F0");
|
||||
}
|
||||
if (status === openpgp.enums.keyStatus.revoked) {
|
||||
return local.createElement("span", { title: "Key revoked: " + details }, "\u274C");
|
||||
}
|
||||
if (status === openpgp.enums.keyStatus.valid) {
|
||||
return local.createElement("span", { title: "Valid key" }, "\u2705");
|
||||
}
|
||||
if (status === openpgp.enums.keyStatus.no_self_cert) {
|
||||
return local.createElement("span", { title: "Key not certified" }, "\u274C");
|
||||
}
|
||||
return "unknown:" + status;
|
||||
}
|
||||
function getIcon(keyFlags) {
|
||||
if (!keyFlags || !keyFlags[0]) {
|
||||
return "";
|
||||
}
|
||||
let flags = [];
|
||||
if ((keyFlags[0] & openpgp.enums.keyFlags.certify_keys) !== 0) {
|
||||
flags.push(local.createElement("span", { title: "Certyfing key" }, "\uD83C\uDFF5\uFE0F"));
|
||||
}
|
||||
if ((keyFlags[0] & openpgp.enums.keyFlags.sign_data) !== 0) {
|
||||
flags.push(local.createElement("span", { title: 'Signing key' }, "\uD83D\uDD8B"));
|
||||
}
|
||||
if (((keyFlags[0] & openpgp.enums.keyFlags.encrypt_communication) !== 0) ||
|
||||
((keyFlags[0] & openpgp.enums.keyFlags.encrypt_storage) !== 0)) {
|
||||
flags.push(local.createElement("span", { title: 'Encryption key' }, "\uD83D\uDD12"));
|
||||
}
|
||||
if ((keyFlags[0] & openpgp.enums.keyFlags.authentication) !== 0) {
|
||||
flags.push(local.createElement("span", { title: 'Authentication key' }, "\uD83D\uDCB3"));
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
return {
|
||||
setters: [
|
||||
function (local_1) {
|
||||
local = local_1;
|
||||
},
|
||||
function (renderer_1) {
|
||||
renderer = renderer_1;
|
||||
}
|
||||
],
|
||||
execute: function () {
|
||||
openpgp.config.show_version = false;
|
||||
openpgp.config.show_comment = false;
|
||||
proofs = [
|
||||
{
|
||||
matcher: /^https:\/\/gist\.github\.com\/([A-Za-z0-9_-]+)\/([0-9a-f]+)$/,
|
||||
variables: {
|
||||
USERNAME: 1,
|
||||
PROOFID: 2
|
||||
},
|
||||
profile: 'https://github.com/{USERNAME}',
|
||||
proof: 'https://api.github.com/gists/{PROOFID}',
|
||||
username: '{USERNAME}',
|
||||
service: 'github',
|
||||
checks: [{
|
||||
relation: 'eq',
|
||||
proof: ['owner', 'login'],
|
||||
claim: '{USERNAME}'
|
||||
}, {
|
||||
relation: 'eq',
|
||||
proof: ['owner', 'html_url'],
|
||||
claim: 'https://github.com/{USERNAME}'
|
||||
}, {
|
||||
relation: 'contains',
|
||||
proof: ['files', 'openpgp.md', 'content'],
|
||||
claim: '[Verifying my OpenPGP key: openpgp4fpr:{FINGERPRINT}]'
|
||||
}]
|
||||
},
|
||||
{
|
||||
matcher: /^https:\/\/news\.ycombinator\.com\/user\?id=([A-Za-z0-9-]+)$/,
|
||||
variables: {
|
||||
USERNAME: 1,
|
||||
PROFILE: 0
|
||||
},
|
||||
profile: '{PROFILE}',
|
||||
proof: 'https://hacker-news.firebaseio.com/v0/user/{USERNAME}.json',
|
||||
username: '{USERNAME}',
|
||||
service: 'hackernews',
|
||||
checks: [{
|
||||
relation: 'contains',
|
||||
proof: ['about'],
|
||||
claim: '[Verifying my OpenPGP key: openpgp4fpr:{FINGERPRINT}]'
|
||||
}]
|
||||
},
|
||||
{
|
||||
matcher: /^https:\/\/www\.reddit\.com\/user\/([^/]+)\/comments\/([^/]+)\/([^/]+\/)?$/,
|
||||
variables: {
|
||||
USERNAME: 1,
|
||||
PROOF: 2
|
||||
},
|
||||
profile: 'https://www.reddit.com/user/{USERNAME}',
|
||||
proof: 'https://www.reddit.com/user/{USERNAME}/comments/{PROOF}.json',
|
||||
username: '{USERNAME}',
|
||||
service: 'reddit',
|
||||
checks: [{
|
||||
relation: 'contains',
|
||||
proof: [0, 'data', 'children', 0, 'data', 'selftext'],
|
||||
claim: 'Verifying my OpenPGP key: openpgp4fpr:{FINGERPRINT}'
|
||||
}, {
|
||||
relation: 'eq',
|
||||
proof: [0, 'data', 'children', 0, 'data', 'author'],
|
||||
claim: '{USERNAME}'
|
||||
}]
|
||||
},
|
||||
{
|
||||
matcher: /^https:\/\/([^/]+)\/@([A-Za-z0-9_-]+)$/,
|
||||
variables: {
|
||||
INSTANCE: 1,
|
||||
USERNAME: 2,
|
||||
PROFILE: 0
|
||||
},
|
||||
profile: '{PROFILE}',
|
||||
proof: '{PROFILE}',
|
||||
username: '@{USERNAME}@{INSTANCE}',
|
||||
service: 'mastodon',
|
||||
checks: [{
|
||||
relation: 'oneOf',
|
||||
proof: ['attachment', 'value'],
|
||||
claim: '{FINGERPRINT}'
|
||||
}]
|
||||
},
|
||||
{
|
||||
matcher: /^dns:([^?]+)\?type=TXT$/,
|
||||
variables: {
|
||||
DOMAIN: 1
|
||||
},
|
||||
profile: 'https://{DOMAIN}',
|
||||
proof: 'https://dns.google.com/resolve?name={DOMAIN}&type=TXT',
|
||||
username: '{DOMAIN}',
|
||||
service: 'dns',
|
||||
checks: [{
|
||||
relation: 'oneOf',
|
||||
proof: ['Answer', 'data'],
|
||||
claim: '\"openpgp4fpr:{FINGERPRINT}\"'
|
||||
}]
|
||||
}
|
||||
];
|
||||
window.onload = window.onhashchange = function () {
|
||||
lookupKey(location.hash.substring(1));
|
||||
};
|
||||
;
|
||||
document.addEventListener('click', verifyProof);
|
||||
dateFormat = new Intl.DateTimeFormat(undefined, {
|
||||
year: 'numeric', month: 'numeric', day: 'numeric',
|
||||
hour: 'numeric', minute: 'numeric', second: 'numeric',
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
Loading…
Reference in new issue