From: Matt Corallo Date: Fri, 12 Jul 2024 12:58:58 +0000 (+0000) Subject: Initial checkin X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=commitdiff_plain;h=88f26a280310502413a556a3eab24306819b638b;p=satsto.me Initial checkin --- 88f26a280310502413a556a3eab24306819b638b diff --git a/dnssec_prover_wasm.js b/dnssec_prover_wasm.js new file mode 100644 index 0000000..6470494 --- /dev/null +++ b/dnssec_prover_wasm.js @@ -0,0 +1,364 @@ +let wasm; + +const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } ); + +if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); }; + +let cachedUint8Memory0 = null; + +function getUint8Memory0() { + if (cachedUint8Memory0 === null || cachedUint8Memory0.byteLength === 0) { + cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer); + } + return cachedUint8Memory0; +} + +function getStringFromWasm0(ptr, len) { + ptr = ptr >>> 0; + return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); +} + +const heap = new Array(128).fill(undefined); + +heap.push(undefined, null, true, false); + +let heap_next = heap.length; + +function addHeapObject(obj) { + if (heap_next === heap.length) heap.push(heap.length + 1); + const idx = heap_next; + heap_next = heap[idx]; + + heap[idx] = obj; + return idx; +} + +let WASM_VECTOR_LEN = 0; + +const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } ); + +const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' + ? function (arg, view) { + return cachedTextEncoder.encodeInto(arg, view); +} + : function (arg, view) { + const buf = cachedTextEncoder.encode(arg); + view.set(buf); + return { + read: arg.length, + written: buf.length + }; +}); + +function passStringToWasm0(arg, malloc, realloc) { + + if (realloc === undefined) { + const buf = cachedTextEncoder.encode(arg); + const ptr = malloc(buf.length, 1) >>> 0; + getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf); + WASM_VECTOR_LEN = buf.length; + return ptr; + } + + let len = arg.length; + let ptr = malloc(len, 1) >>> 0; + + const mem = getUint8Memory0(); + + let offset = 0; + + for (; offset < len; offset++) { + const code = arg.charCodeAt(offset); + if (code > 0x7F) break; + mem[ptr + offset] = code; + } + + if (offset !== len) { + if (offset !== 0) { + arg = arg.slice(offset); + } + ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; + const view = getUint8Memory0().subarray(ptr + offset, ptr + len); + const ret = encodeString(arg, view); + + offset += ret.written; + ptr = realloc(ptr, len, offset, 1) >>> 0; + } + + WASM_VECTOR_LEN = offset; + return ptr; +} +/** +* Builds a proof builder which can generate a proof for records of the given `ty`pe at the given +* `name`. +* +* After calling this [`get_next_query`] should be called to fetch the initial query. +* @param {string} name +* @param {number} ty +* @returns {WASMProofBuilder | undefined} +*/ +export function init_proof_builder(name, ty) { + const ptr0 = passStringToWasm0(name, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.init_proof_builder(ptr0, len0, ty); + return ret === 0 ? undefined : WASMProofBuilder.__wrap(ret); +} + +function _assertClass(instance, klass) { + if (!(instance instanceof klass)) { + throw new Error(`expected instance of ${klass.name}`); + } + return instance.ptr; +} + +function passArray8ToWasm0(arg, malloc) { + const ptr = malloc(arg.length * 1, 1) >>> 0; + getUint8Memory0().set(arg, ptr / 1); + WASM_VECTOR_LEN = arg.length; + return ptr; +} +/** +* Processes a response to a query previously fetched from [`get_next_query`]. +* +* After calling this, [`get_next_query`] should be called until pending queries are exhausted and +* no more pending queries exist, at which point [`get_unverified_proof`] should be called. +* @param {WASMProofBuilder} proof_builder +* @param {Uint8Array} response +*/ +export function process_query_response(proof_builder, response) { + _assertClass(proof_builder, WASMProofBuilder); + const ptr0 = passArray8ToWasm0(response, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + wasm.process_query_response(proof_builder.__wbg_ptr, ptr0, len0); +} + +let cachedInt32Memory0 = null; + +function getInt32Memory0() { + if (cachedInt32Memory0 === null || cachedInt32Memory0.byteLength === 0) { + cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); + } + return cachedInt32Memory0; +} + +function getArrayU8FromWasm0(ptr, len) { + ptr = ptr >>> 0; + return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len); +} +/** +* Gets the next query (if any) that should be sent to the resolver for the given proof builder. +* +* Once the resolver responds [`process_query_response`] should be called with the response. +* @param {WASMProofBuilder} proof_builder +* @returns {Uint8Array | undefined} +*/ +export function get_next_query(proof_builder) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + _assertClass(proof_builder, WASMProofBuilder); + wasm.get_next_query(retptr, proof_builder.__wbg_ptr); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + let v1; + if (r0 !== 0) { + v1 = getArrayU8FromWasm0(r0, r1).slice(); + wasm.__wbindgen_free(r0, r1 * 1, 1); + } + return v1; + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} + +function getObject(idx) { return heap[idx]; } + +function dropObject(idx) { + if (idx < 132) return; + heap[idx] = heap_next; + heap_next = idx; +} + +function takeObject(idx) { + const ret = getObject(idx); + dropObject(idx); + return ret; +} +/** +* Gets the final, unverified, proof once all queries fetched via [`get_next_query`] have +* completed and their responses passed to [`process_query_response`]. +* @param {WASMProofBuilder} proof_builder +* @returns {Uint8Array} +*/ +export function get_unverified_proof(proof_builder) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + _assertClass(proof_builder, WASMProofBuilder); + var ptr0 = proof_builder.__destroy_into_raw(); + wasm.get_unverified_proof(retptr, ptr0); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + var r3 = getInt32Memory0()[retptr / 4 + 3]; + if (r3) { + throw takeObject(r2); + } + var v2 = getArrayU8FromWasm0(r0, r1).slice(); + wasm.__wbindgen_free(r0, r1 * 1, 1); + return v2; + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} + +/** +* Verifies an RFC 9102-formatted proof and returns verified records matching the given name +* (resolving any C/DNAMEs as required). +* @param {Uint8Array} stream +* @param {string} name_to_resolve +* @returns {string} +*/ +export function verify_byte_stream(stream, name_to_resolve) { + let deferred3_0; + let deferred3_1; + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = passArray8ToWasm0(stream, wasm.__wbindgen_malloc); + const len0 = WASM_VECTOR_LEN; + const ptr1 = passStringToWasm0(name_to_resolve, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + wasm.verify_byte_stream(retptr, ptr0, len0, ptr1, len1); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + deferred3_0 = r0; + deferred3_1 = r1; + return getStringFromWasm0(r0, r1); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + wasm.__wbindgen_free(deferred3_0, deferred3_1, 1); + } +} + +const WASMProofBuilderFinalization = (typeof FinalizationRegistry === 'undefined') + ? { register: () => {}, unregister: () => {} } + : new FinalizationRegistry(ptr => wasm.__wbg_wasmproofbuilder_free(ptr >>> 0)); +/** +*/ +export class WASMProofBuilder { + + static __wrap(ptr) { + ptr = ptr >>> 0; + const obj = Object.create(WASMProofBuilder.prototype); + obj.__wbg_ptr = ptr; + WASMProofBuilderFinalization.register(obj, obj.__wbg_ptr, obj); + return obj; + } + + __destroy_into_raw() { + const ptr = this.__wbg_ptr; + this.__wbg_ptr = 0; + WASMProofBuilderFinalization.unregister(this); + return ptr; + } + + free() { + const ptr = this.__destroy_into_raw(); + wasm.__wbg_wasmproofbuilder_free(ptr); + } +} + +async function __wbg_load(module, imports) { + if (typeof Response === 'function' && module instanceof Response) { + if (typeof WebAssembly.instantiateStreaming === 'function') { + try { + return await WebAssembly.instantiateStreaming(module, imports); + + } catch (e) { + if (module.headers.get('Content-Type') != 'application/wasm') { + console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); + + } else { + throw e; + } + } + } + + const bytes = await module.arrayBuffer(); + return await WebAssembly.instantiate(bytes, imports); + + } else { + const instance = await WebAssembly.instantiate(module, imports); + + if (instance instanceof WebAssembly.Instance) { + return { instance, module }; + + } else { + return instance; + } + } +} + +function __wbg_get_imports() { + const imports = {}; + imports.wbg = {}; + imports.wbg.__wbindgen_string_new = function(arg0, arg1) { + const ret = getStringFromWasm0(arg0, arg1); + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_throw = function(arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); + }; + + return imports; +} + +function __wbg_init_memory(imports, maybe_memory) { + +} + +function __wbg_finalize_init(instance, module) { + wasm = instance.exports; + __wbg_init.__wbindgen_wasm_module = module; + cachedInt32Memory0 = null; + cachedUint8Memory0 = null; + + + return wasm; +} + +function initSync(module) { + if (wasm !== undefined) return wasm; + + const imports = __wbg_get_imports(); + + __wbg_init_memory(imports); + + if (!(module instanceof WebAssembly.Module)) { + module = new WebAssembly.Module(module); + } + + const instance = new WebAssembly.Instance(module, imports); + + return __wbg_finalize_init(instance, module); +} + +async function __wbg_init(input) { + if (wasm !== undefined) return wasm; + + if (typeof input === 'undefined') { + input = new URL('dnssec_prover_wasm_bg.wasm', import.meta.url); + } + const imports = __wbg_get_imports(); + + if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) { + input = fetch(input); + } + + __wbg_init_memory(imports); + + const { instance, module } = await __wbg_load(await input, imports); + + return __wbg_finalize_init(instance, module); +} + +export { initSync } +export default __wbg_init; diff --git a/dnssec_prover_wasm_bg.wasm b/dnssec_prover_wasm_bg.wasm new file mode 100644 index 0000000..9cd4df3 Binary files /dev/null and b/dnssec_prover_wasm_bg.wasm differ diff --git a/doh_lookup.js b/doh_lookup.js new file mode 100644 index 0000000..f35a059 --- /dev/null +++ b/doh_lookup.js @@ -0,0 +1,61 @@ +import init from './dnssec_prover_wasm.js'; +import * as wasm from './dnssec_prover_wasm.js'; + +/** +* Asynchronously resolves a given domain and type using the provided DoH endpoint, then verifies +* the returned DNSSEC data and ultimately returns a JSON-encoded list of validated records. +*/ +export async function lookup_doh(domain, ty, doh_endpoint) { + await init(); + + if (!domain.endsWith(".")) domain += "."; + if (ty.toLowerCase() == "txt") { + ty = 16; + } else if (ty.toLowerCase() == "tlsa") { + ty = 52; + } else if (ty.toLowerCase() == "a") { + ty = 1; + } else if (ty.toLowerCase() == "aaaa") { + ty = 28; + } + if (typeof(ty) == "number") { + var builder = wasm.init_proof_builder(domain, ty); + if (builder == null) { + return "{\"error\":\"Bad domain\"}"; + } else { + var queries_pending = 0; + var send_next_query; + send_next_query = async function() { + var query = wasm.get_next_query(builder); + if (query != null) { + queries_pending += 1; + var b64 = btoa(String.fromCodePoint(...query)); + var b64url = b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, ''); + try { + var resp = await fetch(doh_endpoint + "?dns=" + b64url, + {headers: {"accept": "application/dns-message"}}); + if (!resp.ok) { throw "Query returned HTTP " + resp.status; } + var array = await resp.arrayBuffer(); + var buf = new Uint8Array(array); + wasm.process_query_response(builder, buf); + queries_pending -= 1; + } catch (e) { + return "{\"error\":\"DoH Query failed: " + e + "\"}"; + } + return await send_next_query(); + } else if (queries_pending == 0) { + var proof = wasm.get_unverified_proof(builder); + if (proof != null) { + var result = wasm.verify_byte_stream(proof, domain); + return JSON.parse(result); + } else { + return "{\"error\":\"Failed to build proof\"}"; + } + } + } + return await send_next_query(); + } + } else { + return "{\"error\":\"Unsupported Type\"}"; + } +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..9674a71 --- /dev/null +++ b/index.html @@ -0,0 +1,182 @@ + + + + + + + Provable DNS Querying + + +

+ BIP 353 Human Readable Names Resolver +

+

+ BIP 353 defines the way to encode simple human-readable names and map them to Bitcoin payment intructions. +

+ If your wallet doesn't yet resolve BIP 353 names natively, this site will resolve them for you, letting you pay human readable names seamlessly. +

+

+

+
+ ₿ + +
+
+
+ Resolve name using  +
+
+ +

+

+

Note that most BIP 353 addresses rely on at least BOLT 12 or Silent Payments and as both are relatively new, wallet support isn't yet universal. Check that your wallet supports at least one of the two if you are unable to pay.

+

While you're absolutely trusting this site to not provide you with bad code, the code we promise we served you fully validates the name using DNSSEC. Thus, no matter what server you use to resolve the name, the worst they can do is log who you're paying or tell you they're not payable. They can never lie and give you the wrong address!

+ + + + + + + + +