<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <updated>2026-10-16T12:11:49Z</updated>
  <generator>https://njump.me</generator>

  <title>Nostr notes by Terence Eden (Unofficial)</title>
  <author>
    <name>Terence Eden (Unofficial)</name>
  </author>
  <link rel="self" type="application/atom+xml" href="https://njump.me/npub1p0pyxdfjxsfkd2fmpkxw8mxadzpfd6m93j7h2ac30alwv59ys92s4qwwaj.rss" />
  <link href="https://njump.me/npub1p0pyxdfjxsfkd2fmpkxw8mxadzpfd6m93j7h2ac30alwv59ys92s4qwwaj" />
  <id>https://njump.me/npub1p0pyxdfjxsfkd2fmpkxw8mxadzpfd6m93j7h2ac30alwv59ys92s4qwwaj</id>
  <icon>https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f527.png</icon>
  <logo>https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f527.png</logo>




  <entry>
    <id>https://njump.me/nevent1qqsgy2vvswtqse0yndfa4hnwf4sp30ph243g4fw9l5vzcd03s9ztzhszyq9uyse4xg6pxe4f8vxceclvm45g99htvkxt6athz9lhaejs5jq42qgvzl3</id>
    
      <title type="html">Book Review: Quackery - A Brief History of the Worst Ways to Cure ...</title>
    
    <link rel="alternate" href="https://njump.me/nevent1qqsgy2vvswtqse0yndfa4hnwf4sp30ph243g4fw9l5vzcd03s9ztzhszyq9uyse4xg6pxe4f8vxceclvm45g99htvkxt6athz9lhaejs5jq42qgvzl3" />
    <content type="html">
      Book Review: Quackery - A Brief History of the Worst Ways to Cure Everything by Lydia Kang ★★★⯪☆&lt;br/&gt;&lt;br/&gt;This is a charming and funny book all about the various ways humans have promoted fake medical cures in exchange for money.&lt;br/&gt;Despite the grim (and occasionally distressing) subject matter, the tone remains light and poppy throughout. There are lots of snarky asides (especially about the fake medical practitioners) and a real appreciation for the bizarre. It&amp;#39;s fun the whole way through, although sometimes at the expense of people who perhaps didn&amp;#39;t know better than to trust charlatans.&lt;br/&gt;One thing that is constantly brought back is how it can be hard to determine what is patent nonsense and what is a bone fide cure:&lt;br/&gt;Prevent blood clotting: Extract mucus from the intestinal membranes of slaughtered pigs or from the lungs of slaughtered cows. Inject. (heparin)&lt;br/&gt;Is that any more believable than injecting goat testicles to cure impotence?&lt;br/&gt;It also brings to light how squeamish we can be, especially when faced with unusual medical procedures:&lt;br/&gt;We borrow other people’s wombs for surrogacy. And yet, plenty of people squirm at the idea of breastmilk banks. We are a society of contradictions.&lt;br/&gt;The eBook is beautifully formatted. For once, images are in-line with the text! Usually in books of this sort, publishers treat the eBook like a mass-market paperback and stick the illustrations at the end.&lt;br/&gt;It ends rather abruptly, after taking us through light, electricity, colour therapy, animal magnetism, and a couple of dozen other &amp;#34;cure-alls&amp;#34;&lt;br/&gt;Sadly, there are no references or footnotes. While I have no reason to doubt the veracity of the claims, for a book about scientific misinformation and malpractice, I&amp;#39;d&amp;#39;ve expected a way to find out more information.&lt;br/&gt;&lt;br/&gt;&lt;a href=&#34;https://shkspr.mobi/blog/2026/10/book-review-quackery-a-brief-history-of-the-worst-ways-to-cure-everything-by-lydia-kang/&#34;&gt;https://shkspr.mobi/blog/2026/10/book-review-quackery-a-brief-history-of-the-worst-ways-to-cure-everything-by-lydia-kang/&lt;/a&gt;&lt;br/&gt; &lt;img src=&#34;https://shkspr.mobi/blog/wp-content/uploads/2026/08/9781523501854.webp&#34;&gt; &lt;br/&gt;&lt;a href=&#34;https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=74296&amp;amp;HTTP_REFERER=Atom&#34;&gt;https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=74296&amp;amp;HTTP_REFERER=Atom&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;#lydiakang #briefhistory #cureeverything #worstways #bookreview #quackery&lt;br/&gt;&lt;br/&gt;#rss #feeds #linksharing #qwibbit
    </content>
    <updated>2026-10-20T17:59:00Z</updated>
  </entry>

  <entry>
    <id>https://njump.me/nevent1qqsy4esycvvmsg5vq7rrgvvxtqpx6tsgng8x9rvhdu84vwdkankppdgzyq9uyse4xg6pxe4f8vxceclvm45g99htvkxt6athz9lhaejs5jq424084rh</id>
    
      <title type="html">Una Watch TOTP - MFA on your wrist! Let&amp;#39;s get this out of the ...</title>
    
    <link rel="alternate" href="https://njump.me/nevent1qqsy4esycvvmsg5vq7rrgvvxtqpx6tsgng8x9rvhdu84vwdkankppdgzyq9uyse4xg6pxe4f8vxceclvm45g99htvkxt6athz9lhaejs5jq424084rh" />
    <content type="html">
      Una Watch TOTP - MFA on your wrist!&lt;br/&gt;&lt;br/&gt;Let&amp;#39;s get this out of the way first - this is a terrible idea! I want my two-factor authentication codes to be easily accessible to me, so I&amp;#39;ve built a watch app which will display them. That&amp;#39;s really convenient for me - but makes for a terrible security posture. Anyone who steals my watch will be able to see my one-time code. If they&amp;#39;re clever, they can decompile the app and find the secret which powers the code generation.&lt;br/&gt;You should absolutely not do this and, if you ignore my warnings, you should only do this for codes which aren&amp;#39;t critical to your security.&lt;br/&gt;In my defence, an attacker would still need my username and password to make any use of these codes - but please don&amp;#39;t trust your security to my deranged witterings.&lt;br/&gt;What It Looks Like&lt;br/&gt;Prior Art&lt;br/&gt;I&amp;#39;m sure many other people have done this. Indeed, three years ago I built An eInk, Wrist-Mounted, TOTP Generator.&lt;br/&gt;The Service&lt;br/&gt;This is in my Service.cpp - as you can see, it&amp;#39;s pretty simple. This won&amp;#39;t let you add in hundreds of codes - you get one and that&amp;#39;s it. There&amp;#39;s no countdown when a code is about to expire, so I recommend waiting until a new one pops up before you start typing it in.&lt;br/&gt;I&amp;#39;ll be candid, I haven&amp;#39;t used C&#43;&#43; properly since university - and that was in the last century. I&amp;#39;ve mostly just cut things out to see what breaks. Feel free to educate me on the correct idiomatic code.&lt;br/&gt;You will need to set your own secret in here.&lt;br/&gt;// Standard includes.&lt;br/&gt;#include &amp;#34;Service.hpp&amp;#34;&lt;br/&gt;#include &amp;#34;SDK/Messages/CommandMessages.hpp&amp;#34;&lt;br/&gt;#include &amp;#34;SDK/Messages/MessageGuard.hpp&amp;#34;&lt;br/&gt;// Needed for PureTOTP.&lt;br/&gt;#include &amp;lt;iostream&amp;gt;&lt;br/&gt;#include &amp;#34;TOTP.h&amp;#34;&lt;br/&gt;#include &amp;lt;string&amp;gt;&lt;br/&gt;#include &amp;lt;sstream&amp;gt;&lt;br/&gt;// What this Glance can do.&lt;br/&gt;Service::Service(SDK::Kernel &amp;amp;kernel)&lt;br/&gt;: mKernel(kernel)&lt;br/&gt;, mGlanceUI()&lt;br/&gt;, mGlanceTitle()&lt;br/&gt;, mGlanceValue()&lt;br/&gt;{}&lt;br/&gt;void Service::createGuiControls()&lt;br/&gt;{&lt;br/&gt;// Set the main title in the Glance.&lt;br/&gt;mGlanceTitle = mGlanceUI.createText();&lt;br/&gt;mGlanceTitle.pos({ kTitleX, kTitleY }, { kTitleW, kTitleH })&lt;br/&gt;.font(GlanceFont_t::GLANCE_FONT_POPPINS_SEMIBOLD_20)&lt;br/&gt;.color(GlanceColor_t::GLANCE_COLOR_TEAL)&lt;br/&gt;.setText(&amp;#34;HSBC MFA Code&amp;#34;)&lt;br/&gt;.alignment(GlanceAlignH_t::GLANCE_ALIGN_H_CENTER);&lt;br/&gt;// Set the default text for the value.&lt;br/&gt;mGlanceValue = mGlanceUI.createText();&lt;br/&gt;mGlanceValue.pos({ 20, kValueY }, { 200, kValueH })&lt;br/&gt;.font(GlanceFont_t::GLANCE_FONT_POPPINS_SEMIBOLD_20)&lt;br/&gt;.color(GlanceColor_t::GLANCE_COLOR_YELLOW_DARK)&lt;br/&gt;.setText( &amp;#34;Waiting for update&amp;#34; )&lt;br/&gt;.alignment(GlanceAlignH_t::GLANCE_ALIGN_H_CENTER);&lt;br/&gt;}&lt;br/&gt;void Service::glanceUpdate()&lt;br/&gt;{&lt;br/&gt;// Set the Base32 encoded secret (all uppercase).&lt;br/&gt;std::string secret = &amp;#34;QWERTYUIOP&amp;#34;;&lt;br/&gt;// Generate the code based on the Unix timestamp right now.&lt;br/&gt;const std::time_t t = std::time(0);&lt;br/&gt;std::string code = TOTP::GenerateTOTP( 6, 30, TOTP::HMAC_SHA1, secret, t );&lt;br/&gt;std::ostringstream ss;&lt;br/&gt;ss &amp;lt;&amp;lt; t;&lt;br/&gt;// Convert the string to char* and display it as the value.&lt;br/&gt;mGlanceValue.print( code.c_str() );&lt;br/&gt;// Show the Unix time to check the timezone is correct.&lt;br/&gt;// mGlanceTitle.print( ss.str().c_str() );&lt;br/&gt;}&lt;br/&gt;// No service.&lt;br/&gt;Service::~Service(){}&lt;br/&gt;// When this glance is opened.&lt;br/&gt;void Service::run()&lt;br/&gt;{&lt;br/&gt;while (true) {&lt;br/&gt;SDK::MessageBase *msg;&lt;br/&gt;if(!mKernel.comm.getMessage(msg)) {&lt;br/&gt;continue;&lt;br/&gt;}&lt;br/&gt;switch (msg-&amp;gt;getType()) {&lt;br/&gt;// The Glance has started.&lt;br/&gt;case SDK::MessageType::EVENT_GLANCE_START:&lt;br/&gt;if (configGui()) {&lt;br/&gt;createGuiControls();&lt;br/&gt;} else {&lt;br/&gt;mKernel.comm.releaseMessage(msg);&lt;br/&gt;mKernel.sys.exit(0);&lt;br/&gt;}&lt;br/&gt;break;&lt;br/&gt;// The Glance has been stopped.&lt;br/&gt;case SDK::MessageType::COMMAND_APP_STOP:&lt;br/&gt;case SDK::MessageType::EVENT_GLANCE_STOP:&lt;br/&gt;mKernel.comm.releaseMessage(msg);&lt;br/&gt;return;&lt;br/&gt;break;&lt;br/&gt;// Every second, perform an action.&lt;br/&gt;case SDK::MessageType::EVENT_GLANCE_TICK:&lt;br/&gt;onGlanceTick();&lt;br/&gt;break;&lt;br/&gt;default:&lt;br/&gt;break;&lt;br/&gt;}&lt;br/&gt;mKernel.comm.releaseMessage(msg);&lt;br/&gt;}&lt;br/&gt;}&lt;br/&gt;void Service::onGlanceTick()&lt;br/&gt;{&lt;br/&gt;if (mGlanceUI.isInvalid()) {&lt;br/&gt;if (auto upd = SDK::make_msg&amp;lt;SDK::Message::RequestGlanceUpdate&amp;gt;(mKernel)) {&lt;br/&gt;upd-&amp;gt;name = APP_NAME;&lt;br/&gt;upd-&amp;gt;controls = mGlanceUI.data();&lt;br/&gt;upd-&amp;gt;controlsNumber = static_cast&amp;lt;uint32_t&amp;gt;(mGlanceUI.size());&lt;br/&gt;upd.send(100);&lt;br/&gt;}&lt;br/&gt;mGlanceUI.setValid();&lt;br/&gt;}&lt;br/&gt;glanceUpdate();&lt;br/&gt;}&lt;br/&gt;bool Service::configGui()&lt;br/&gt;{&lt;br/&gt;bool status = false;&lt;br/&gt;if (auto gc = SDK::make_msg&amp;lt;SDK::Message::RequestGlanceConfig&amp;gt;(mKernel)) {&lt;br/&gt;if (gc.send(100) &amp;amp;&amp;amp; gc.ok()) {&lt;br/&gt;if (gc-&amp;gt;maxControls &amp;gt;= 3) {&lt;br/&gt;mGlanceUI.setWidth(gc-&amp;gt;width);&lt;br/&gt;mGlanceUI.setHeight(gc-&amp;gt;height);&lt;br/&gt;status = true;&lt;br/&gt;}&lt;br/&gt;}&lt;br/&gt;}&lt;br/&gt;return status;&lt;br/&gt;}&lt;br/&gt;The Library&lt;br/&gt;There are dozens of C&#43;&#43; libraries for RFC 6238 generation. I ended up using PureTOTP as it didn&amp;#39;t require any external dependencies and was only two files. As I only needed SHA1 TOTP codes, I stripped out a bunch of functionality which was unnecessary for me. Similarly, I only wanted to generate 6 digit codes with a 30 second period - so took out anything which did more than that.&lt;br/&gt;I&amp;#39;ve tidied up the code and comments where I can. The original was MIT Licenced - so this is as well.&lt;br/&gt;There are two parts to this. First, the header&lt;br/&gt;// TOTP.h&lt;br/&gt;//&lt;br/&gt;#pragma once&lt;br/&gt;#include &amp;lt;string&amp;gt;&lt;br/&gt;class TOTP&lt;br/&gt;{&lt;br/&gt;public:&lt;br/&gt;const static int HMAC_SHA1 = 20; // Length of HMAC.&lt;br/&gt;static std::string GenerateTOTP(const std::string&amp;amp; secret);&lt;br/&gt;static std::string GenerateTOTP(int digit, int refreshSeconds, int algorithm, const std::string&amp;amp; secret, int specTime);&lt;br/&gt;TOTP(int digit, int refreshSeconds, int algorithm, const std::string&amp;amp; secret): digit(digit), refreshSeconds(refreshSeconds), algorithm(algorithm), secret(secret) {}&lt;br/&gt;TOTP(const std::string&amp;amp; secret): digit(6), refreshSeconds(30), algorithm(HMAC_SHA1), secret(secret) {}&lt;br/&gt;std::string currentTOTP();&lt;br/&gt;private:&lt;br/&gt;int digit, refreshSeconds, algorithm;&lt;br/&gt;std::string secret;&lt;br/&gt;};&lt;br/&gt;Next is the standalone library which does the TOTP generation, TOTP.cpp&lt;br/&gt;// TOTP.cpp&lt;br/&gt;//&lt;br/&gt;#include &amp;#34;TOTP.h&amp;#34;&lt;br/&gt;#include &amp;lt;vector&amp;gt;&lt;br/&gt;#include &amp;lt;stack&amp;gt;&lt;br/&gt;#include &amp;lt;queue&amp;gt;&lt;br/&gt;#include &amp;lt;sstream&amp;gt;&lt;br/&gt;#include &amp;lt;iomanip&amp;gt;&lt;br/&gt;#include &amp;lt;cstdint&amp;gt;&lt;br/&gt;#include &amp;lt;cstring&amp;gt;&lt;br/&gt;#include &amp;lt;array&amp;gt;&lt;br/&gt;#include &amp;lt;stdexcept&amp;gt;&lt;br/&gt;#include &amp;lt;algorithm&amp;gt;&lt;br/&gt;using namespace std;&lt;br/&gt;struct JEVP_MD&lt;br/&gt;{&lt;br/&gt;enum Kind {&lt;br/&gt;SHA1&lt;br/&gt;};&lt;br/&gt;Kind kind;&lt;br/&gt;int md_size;&lt;br/&gt;int block_size;&lt;br/&gt;};&lt;br/&gt;static inline uint32_t rotl32(uint32_t x, uint32_t n) { return (x &amp;lt;&amp;lt; n) | (x &amp;gt;&amp;gt; (32 - n)); }&lt;br/&gt;static inline uint32_t rotr32(uint32_t x, uint32_t n) { return (x &amp;gt;&amp;gt; n) | (x &amp;lt;&amp;lt; (32 - n)); }&lt;br/&gt;static inline uint64_t rotr64(uint64_t x, uint64_t n) { return (x &amp;gt;&amp;gt; n) | (x &amp;lt;&amp;lt; (64 - n)); }&lt;br/&gt;static uint32_t load_be32(const unsigned char* p)&lt;br/&gt;{&lt;br/&gt;return (uint32_t(p[0]) &amp;lt;&amp;lt; 24) |&lt;br/&gt;(uint32_t(p[1]) &amp;lt;&amp;lt; 16) |&lt;br/&gt;(uint32_t(p[2]) &amp;lt;&amp;lt; 8) |&lt;br/&gt;uint32_t(p[3]);&lt;br/&gt;}&lt;br/&gt;static void store_be32(uint32_t v, unsigned char* p)&lt;br/&gt;{&lt;br/&gt;p[0] = static_cast&amp;lt;unsigned char&amp;gt;((v &amp;gt;&amp;gt; 24) &amp;amp; 0xFF);&lt;br/&gt;p[1] = static_cast&amp;lt;unsigned char&amp;gt;((v &amp;gt;&amp;gt; 16) &amp;amp; 0xFF);&lt;br/&gt;p[2] = static_cast&amp;lt;unsigned char&amp;gt;((v &amp;gt;&amp;gt; 8) &amp;amp; 0xFF);&lt;br/&gt;p[3] = static_cast&amp;lt;unsigned char&amp;gt;( v &amp;amp; 0xFF);&lt;br/&gt;}&lt;br/&gt;static void store_be64(uint64_t v, unsigned char* p)&lt;br/&gt;{&lt;br/&gt;p[0] = static_cast&amp;lt;unsigned char&amp;gt;((v &amp;gt;&amp;gt; 56) &amp;amp; 0xFF);&lt;br/&gt;p[1] = static_cast&amp;lt;unsigned char&amp;gt;((v &amp;gt;&amp;gt; 48) &amp;amp; 0xFF);&lt;br/&gt;p[2] = static_cast&amp;lt;unsigned char&amp;gt;((v &amp;gt;&amp;gt; 40) &amp;amp; 0xFF);&lt;br/&gt;p[3] = static_cast&amp;lt;unsigned char&amp;gt;((v &amp;gt;&amp;gt; 32) &amp;amp; 0xFF);&lt;br/&gt;p[4] = static_cast&amp;lt;unsigned char&amp;gt;((v &amp;gt;&amp;gt; 24) &amp;amp; 0xFF);&lt;br/&gt;p[5] = static_cast&amp;lt;unsigned char&amp;gt;((v &amp;gt;&amp;gt; 16) &amp;amp; 0xFF);&lt;br/&gt;p[6] = static_cast&amp;lt;unsigned char&amp;gt;((v &amp;gt;&amp;gt; 8) &amp;amp; 0xFF);&lt;br/&gt;p[7] = static_cast&amp;lt;unsigned char&amp;gt;( v &amp;amp; 0xFF);&lt;br/&gt;}&lt;br/&gt;static vector&amp;lt;unsigned char&amp;gt; sha1_hash(const unsigned char* data, size_t len)&lt;br/&gt;{&lt;br/&gt;vector&amp;lt;unsigned char&amp;gt; msg(data, data &#43; len);&lt;br/&gt;uint64_t bit_len = static_cast&amp;lt;uint64_t&amp;gt;(len) * 8;&lt;br/&gt;msg.push_back(0x80);&lt;br/&gt;while ((msg.size() % 64) != 56) msg.push_back(0x00);&lt;br/&gt;unsigned char lenbuf[8];&lt;br/&gt;store_be64(bit_len, lenbuf);&lt;br/&gt;msg.insert(msg.end(), lenbuf, lenbuf &#43; 8);&lt;br/&gt;uint32_t h0 = 0x67452301;&lt;br/&gt;uint32_t h1 = 0xEFCDAB89;&lt;br/&gt;uint32_t h2 = 0x98BADCFE;&lt;br/&gt;uint32_t h3 = 0x10325476;&lt;br/&gt;uint32_t h4 = 0xC3D2E1F0;&lt;br/&gt;for (size_t chunk = 0; chunk &amp;lt; msg.size(); chunk &#43;= 64)&lt;br/&gt;{&lt;br/&gt;uint32_t w[80];&lt;br/&gt;for (int i = 0; i &amp;lt; 16; &#43;&#43;i)&lt;br/&gt;w[i] = load_be32(&amp;amp;msg[chunk &#43; i * 4]);&lt;br/&gt;for (int i = 16; i &amp;lt; 80; &#43;&#43;i)&lt;br/&gt;w[i] = rotl32(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);&lt;br/&gt;uint32_t a = h0, b = h1, c = h2, d = h3, e = h4;&lt;br/&gt;for (int i = 0; i &amp;lt; 80; &#43;&#43;i)&lt;br/&gt;{&lt;br/&gt;uint32_t f, k;&lt;br/&gt;if (i &amp;lt; 20)&lt;br/&gt;{&lt;br/&gt;f = (b &amp;amp; c) | ((~b) &amp;amp; d);&lt;br/&gt;k = 0x5A827999;&lt;br/&gt;}&lt;br/&gt;else if (i &amp;lt; 40)&lt;br/&gt;{&lt;br/&gt;f = b ^ c ^ d;&lt;br/&gt;k = 0x6ED9EBA1;&lt;br/&gt;}&lt;br/&gt;else if (i &amp;lt; 60)&lt;br/&gt;{&lt;br/&gt;f = (b &amp;amp; c) | (b &amp;amp; d) | (c &amp;amp; d);&lt;br/&gt;k = 0x8F1BBCDC;&lt;br/&gt;}&lt;br/&gt;else&lt;br/&gt;{&lt;br/&gt;f = b ^ c ^ d;&lt;br/&gt;k = 0xCA62C1D6;&lt;br/&gt;}&lt;br/&gt;uint32_t temp = rotl32(a, 5) &#43; f &#43; e &#43; k &#43; w[i];&lt;br/&gt;e = d;&lt;br/&gt;d = c;&lt;br/&gt;c = rotl32(b, 30);&lt;br/&gt;b = a;&lt;br/&gt;a = temp;&lt;br/&gt;}&lt;br/&gt;h0 &#43;= a;&lt;br/&gt;h1 &#43;= b;&lt;br/&gt;h2 &#43;= c;&lt;br/&gt;h3 &#43;= d;&lt;br/&gt;h4 &#43;= e;&lt;br/&gt;}&lt;br/&gt;vector&amp;lt;unsigned char&amp;gt; out(20);&lt;br/&gt;store_be32(h0, &amp;amp;out[ 0]);&lt;br/&gt;store_be32(h1, &amp;amp;out[ 4]);&lt;br/&gt;store_be32(h2, &amp;amp;out[ 8]);&lt;br/&gt;store_be32(h3, &amp;amp;out[12]);&lt;br/&gt;store_be32(h4, &amp;amp;out[16]);&lt;br/&gt;return out;&lt;br/&gt;}&lt;br/&gt;static const JEVP_MD g_sha1 = {JEVP_MD::SHA1, 20, 64};&lt;br/&gt;const JEVP_MD* JEVP_sha1() { return &amp;amp;g_sha1; }&lt;br/&gt;unsigned char* JHMAC(const JEVP_MD* JEVP_MD, const void* key, int key_len, const unsigned char* data, size_t data_len, unsigned char* md, unsigned int* md_len)&lt;br/&gt;{&lt;br/&gt;if (!JEVP_MD || !key || key_len &amp;lt; 0 || !data)&lt;br/&gt;return nullptr;&lt;br/&gt;static thread_local unsigned char static_md[64];&lt;br/&gt;if (!md)&lt;br/&gt;md = static_md;&lt;br/&gt;vector&amp;lt;unsigned char&amp;gt; key_block(JEVP_MD-&amp;gt;block_size, 0);&lt;br/&gt;const unsigned char* key_bytes = static_cast&amp;lt;const unsigned char*&amp;gt;(key);&lt;br/&gt;vector&amp;lt;unsigned char&amp;gt; real_key;&lt;br/&gt;if (key_len &amp;gt; JEVP_MD-&amp;gt;block_size)&lt;br/&gt;{&lt;br/&gt;real_key = sha1_hash(key_bytes, static_cast&amp;lt;size_t&amp;gt;(key_len));&lt;br/&gt;std::copy(real_key.begin(), real_key.end(), key_block.begin());&lt;br/&gt;}&lt;br/&gt;else&lt;br/&gt;{&lt;br/&gt;std::copy(key_bytes, key_bytes &#43; key_len, key_block.begin());&lt;br/&gt;}&lt;br/&gt;vector&amp;lt;unsigned char&amp;gt; ipad(JEVP_MD-&amp;gt;block_size);&lt;br/&gt;vector&amp;lt;unsigned char&amp;gt; opad(JEVP_MD-&amp;gt;block_size);&lt;br/&gt;for (int i = 0; i &amp;lt; JEVP_MD-&amp;gt;block_size; &#43;&#43;i)&lt;br/&gt;{&lt;br/&gt;ipad[i] = static_cast&amp;lt;unsigned char&amp;gt;(key_block[i] ^ 0x36);&lt;br/&gt;opad[i] = static_cast&amp;lt;unsigned char&amp;gt;(key_block[i] ^ 0x5C);&lt;br/&gt;}&lt;br/&gt;vector&amp;lt;unsigned char&amp;gt; inner;&lt;br/&gt;inner.reserve(JEVP_MD-&amp;gt;block_size &#43; data_len);&lt;br/&gt;inner.insert(inner.end(), ipad.begin(), ipad.end());&lt;br/&gt;inner.insert(inner.end(), data, data &#43; data_len);&lt;br/&gt;vector&amp;lt;unsigned char&amp;gt; inner_hash = sha1_hash(inner.data(), inner.size());&lt;br/&gt;vector&amp;lt;unsigned char&amp;gt; outer;&lt;br/&gt;outer.reserve(JEVP_MD-&amp;gt;block_size &#43; inner_hash.size());&lt;br/&gt;outer.insert(outer.end(), opad.begin(), opad.end());&lt;br/&gt;outer.insert(outer.end(), inner_hash.begin(), inner_hash.end());&lt;br/&gt;vector&amp;lt;unsigned char&amp;gt; final_hash = sha1_hash(outer.data(), outer.size());&lt;br/&gt;std::memcpy(md, final_hash.data(), final_hash.size());&lt;br/&gt;if (md_len) *md_len = static_cast&amp;lt;unsigned int&amp;gt;(final_hash.size());&lt;br/&gt;return md;&lt;br/&gt;}&lt;br/&gt;// Base32 decoding table.&lt;br/&gt;/* clang-format off */&lt;br/&gt;const int base32_reverse_map[128] =&lt;br/&gt;{&lt;br/&gt;255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 15&lt;br/&gt;255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 31&lt;br/&gt;255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 47&lt;br/&gt;// (49) 2 3 4 5 6 7 (56)&lt;br/&gt;255, 255, 26, 27, 28, 29, 30, 31, 255, 255, 255, 255, 255, 255, 255, 255, // 63&lt;br/&gt;// (64) A B C D E F G H I J K L M N O&lt;br/&gt;255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 79&lt;br/&gt;// P Q R S T U V W X Y Z (91)&lt;br/&gt;15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, // 95&lt;br/&gt;// (96) A B C D E F G H I J K L M N O // lower case instead&lt;br/&gt;255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 111&lt;br/&gt;// P Q R S T U V W X Y Z (123) // lower case instead&lt;br/&gt;15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255 // 127&lt;br/&gt;};&lt;br/&gt;/* clang-format on */&lt;br/&gt;// Base32 decoding function.&lt;br/&gt;vector&amp;lt;unsigned char&amp;gt; base32_decode(const string&amp;amp; base32)&lt;br/&gt;{&lt;br/&gt;queue&amp;lt;int&amp;gt; q_rev;&lt;br/&gt;for (const char&amp;amp; c : base32)&lt;br/&gt;{&lt;br/&gt;if (c &amp;lt;= 49 || (c &amp;gt;= 56 &amp;amp;&amp;amp; c &amp;lt;= 64) || (c &amp;gt;= 91 &amp;amp;&amp;amp; c &amp;lt;= 96) || c &amp;gt;= 123)&lt;br/&gt;{ // Illegal lower case will be treat as upper case thanks to base32_reverse_map.&lt;br/&gt;if (c == &amp;#39;=&amp;#39;) break;&lt;br/&gt;}&lt;br/&gt;else&lt;br/&gt;{&lt;br/&gt;q_rev.push(base32_reverse_map[(int)c]); // Get integers of base32.&lt;br/&gt;}&lt;br/&gt;}&lt;br/&gt;queue&amp;lt;bool&amp;gt; q_bin;&lt;br/&gt;while (!q_rev.empty()) // Transform the integers into bits and queue them up.&lt;br/&gt;{&lt;br/&gt;int now = q_rev.front(); // Get a integer from queue.&lt;br/&gt;q_rev.pop();&lt;br/&gt;stack&amp;lt;bool&amp;gt; sb;&lt;br/&gt;for (int i = 0; i &amp;lt; 5; i&#43;&#43;) // Push the bits of this integer into stack&lt;br/&gt;{ // 5 bits into 1 group&lt;br/&gt;sb.push(now &amp;amp; 1); // Push the last bit of this integer into stack.&lt;br/&gt;now &amp;gt;&amp;gt;= 1; // Flush the last bit.&lt;br/&gt;}&lt;br/&gt;while (!sb.empty()) // Flush the bits of this integer into binary queue.&lt;br/&gt;{&lt;br/&gt;q_bin.push(sb.top()); // Pop bits from stack into binary queue.&lt;br/&gt;sb.pop();&lt;br/&gt;}&lt;br/&gt;}&lt;br/&gt;vector&amp;lt;unsigned char&amp;gt; uc;&lt;br/&gt;while (!q_bin.empty()) // Transform the bits into bytes.&lt;br/&gt;{&lt;br/&gt;int thehex = 0;&lt;br/&gt;for (int i = 0; i &amp;lt; 8; i&#43;&#43;) // Read a byte from the bits queue.&lt;br/&gt;{&lt;br/&gt;if (q_bin.empty())&lt;br/&gt;{ // If the bits queue is empty now,&lt;br/&gt;thehex &amp;lt;&amp;lt;= 8 - i; // set 0 on the last bits of a byte.&lt;br/&gt;break;&lt;br/&gt;}&lt;br/&gt;thehex &amp;lt;&amp;lt;= 1; // Make the space for the new bit.&lt;br/&gt;thehex |= (int)q_bin.front(); // Push this bit into byte.&lt;br/&gt;q_bin.pop();&lt;br/&gt;}&lt;br/&gt;uc.push_back((unsigned char)thehex);&lt;br/&gt;}&lt;br/&gt;if (uc.back() == 0) uc.pop_back(); // TOTP may abandon last zero.&lt;br/&gt;if (uc.size() % 2 != 0) uc.pop_back(); // TOTP may abandon odd res.&lt;br/&gt;return uc;&lt;br/&gt;}&lt;br/&gt;// Get Unix Time.&lt;br/&gt;time_t getUnixTime()&lt;br/&gt;{&lt;br/&gt;return time(0);&lt;br/&gt;}&lt;br/&gt;// Calculate the TOTP time signature.&lt;br/&gt;time_t getTimeSign(int refreshPeriod, time_t specUnixTime)&lt;br/&gt;{&lt;br/&gt;time_t outTime = specUnixTime / refreshPeriod;&lt;br/&gt;return outTime;&lt;br/&gt;}&lt;br/&gt;// Transform time_t aka &amp;lt;long long&amp;gt; to bytes.&lt;br/&gt;vector&amp;lt;unsigned char&amp;gt; llong2bytes(time_t tt)&lt;br/&gt;{&lt;br/&gt;uint64_t value = static_cast&amp;lt;uint64_t&amp;gt;(tt);&lt;br/&gt;vector&amp;lt;unsigned char&amp;gt; bytes(8);&lt;br/&gt;for (int i = 7; i &amp;gt;= 0; --i)&lt;br/&gt;{&lt;br/&gt;bytes[i] = static_cast&amp;lt;unsigned char&amp;gt;(value &amp;amp; 0xFF);&lt;br/&gt;value &amp;gt;&amp;gt;= 8;&lt;br/&gt;}&lt;br/&gt;return bytes;&lt;br/&gt;}&lt;br/&gt;// Fast Exponentiation algorithm.&lt;br/&gt;unsigned fastpow(unsigned base, unsigned index)&lt;br/&gt;{&lt;br/&gt;if (index == 0) return 1;&lt;br/&gt;unsigned t = 1;&lt;br/&gt;while (index != 0)&lt;br/&gt;{&lt;br/&gt;if (index &amp;amp; 1) t *= base;&lt;br/&gt;index &amp;gt;&amp;gt;= 1;&lt;br/&gt;base *= base;&lt;br/&gt;}&lt;br/&gt;return t;&lt;br/&gt;}&lt;br/&gt;// Calculate TOTP according to RFC standard.&lt;br/&gt;std::string hash_to_TOTP(const unsigned char* hmac, unsigned int length, int digit)&lt;br/&gt;{&lt;br/&gt;int offset = hmac[length - 1] &amp;amp; 0x0F;&lt;br/&gt;uint32_t bin_code = ((hmac[offset] &amp;amp; 0x7F) &amp;lt;&amp;lt; 24) |&lt;br/&gt;((hmac[offset &#43; 1] &amp;amp; 0xFF) &amp;lt;&amp;lt; 16) |&lt;br/&gt;((hmac[offset &#43; 2] &amp;amp; 0xFF) &amp;lt;&amp;lt; 8) |&lt;br/&gt;( hmac[offset &#43; 3] &amp;amp; 0xFF);&lt;br/&gt;uint32_t mod = 1;&lt;br/&gt;for (int i = 0; i &amp;lt; digit; &#43;&#43;i) mod *= 10;&lt;br/&gt;uint32_t otp = bin_code % mod;&lt;br/&gt;std::ostringstream oss;&lt;br/&gt;oss &amp;lt;&amp;lt; std::setw(digit) &amp;lt;&amp;lt; std::setfill(&amp;#39;0&amp;#39;) &amp;lt;&amp;lt; otp;&lt;br/&gt;return oss.str();&lt;br/&gt;}&lt;br/&gt;std::string TOTP::GenerateTOTP(const std::string&amp;amp; secret)&lt;br/&gt;{&lt;br/&gt;return GenerateTOTP(6, 30, HMAC_SHA1, secret, getUnixTime());&lt;br/&gt;}&lt;br/&gt;std::string TOTP::GenerateTOTP(int digit, int refreshSeconds, int algorithm, const std::string&amp;amp; secret, int specTime)&lt;br/&gt;{&lt;br/&gt;vector&amp;lt;unsigned char&amp;gt; h_msg = llong2bytes(getTimeSign(refreshSeconds, specTime)); // Get current time signature and transform time_t aka &amp;lt;long long&amp;gt; to bytes.&lt;br/&gt;vector&amp;lt;unsigned char&amp;gt; h_key = base32_decode(secret); // Transform base32 encoded secret key to decoded bytes.&lt;br/&gt;unsigned char* out_hmac = new unsigned char[algorithm]; // Perform openssl function to calculate HMAC-SHA1 hash.&lt;br/&gt;unsigned int out_hmac_length;&lt;br/&gt;const JEVP_MD* emd;&lt;br/&gt;emd = JEVP_sha1();&lt;br/&gt;JHMAC(emd, h_key.data(), (int)h_key.size(), h_msg.data(), (int)h_msg.size(), out_hmac, &amp;amp;out_hmac_length);&lt;br/&gt;string fine = hash_to_TOTP(out_hmac, out_hmac_length, digit);&lt;br/&gt;delete[] out_hmac;&lt;br/&gt;return fine; // Calculate TOTP according to RFC standard.&lt;br/&gt;}&lt;br/&gt;std::string TOTP::currentTOTP()&lt;br/&gt;{&lt;br/&gt;return GenerateTOTP(digit, refreshSeconds, algorithm, secret, getUnixTime());&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;&lt;a href=&#34;https://shkspr.mobi/blog/2026/10/una-watch-totp-mfa-on-your-wrist/&#34;&gt;https://shkspr.mobi/blog/2026/10/una-watch-totp-mfa-on-your-wrist/&lt;/a&gt;&lt;br/&gt; &lt;img src=&#34;https://shkspr.mobi/blog/wp-content/uploads/2026/09/MFA.webp&#34;&gt; &lt;br/&gt; &lt;img src=&#34;https://shkspr.mobi/blog/wp-content/uploads/2023/07/Wrist-TOTP.jpg&#34;&gt; &lt;br/&gt;&lt;a href=&#34;https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=76104&amp;amp;HTTP_REFERER=Atom&#34;&gt;https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=76104&amp;amp;HTTP_REFERER=Atom&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;#rss #feeds #linksharing #qwibbit
    </content>
    <updated>2026-10-18T15:19:01Z</updated>
  </entry>

  <entry>
    <id>https://njump.me/nevent1qqsgj85cjjxrjh40sukxpxnyedylclwyw8jr5qcmrc3at9wy389wtggzyq9uyse4xg6pxe4f8vxceclvm45g99htvkxt6athz9lhaejs5jq42vapmun</id>
    
      <title type="html">Book Review: Are You *Really* The Doctor? by Matthew Hutchinson ...</title>
    
    <link rel="alternate" href="https://njump.me/nevent1qqsgj85cjjxrjh40sukxpxnyedylclwyw8jr5qcmrc3at9wy389wtggzyq9uyse4xg6pxe4f8vxceclvm45g99htvkxt6athz9lhaejs5jq42vapmun" />
    <content type="html">
      Book Review: Are You *Really* The Doctor? by Matthew Hutchinson ★★★⯪☆&lt;br/&gt;&lt;br/&gt;A few months ago, I went to see Dr Hutchinson record his stand-up comedy special for the BBC - it&amp;#39;s about his experiences working in the NHS as a Black doctor. As the title of the book implies, some of the public can be a mite suspicious that anyone with even a hint of melanin can rise beyond being a porter or, at a push, a nurse.&lt;br/&gt;Much like Adam Kay&amp;#39;s &amp;#34;This Is Going To Hurt&amp;#34;, this is part biography, part rant about the NHS, and part humorous look at what it takes to be a modern doctor. Hutchinson&amp;#39;s book has rather less gallows humour and rather more racism.&lt;br/&gt;The problems of racial inequality in the NHS aren&amp;#39;t just focussed on patients; they&amp;#39;re also reflected in the way staff are treated. Rather than just recounting all the abuse he&amp;#39;s been subject to, the book takes a good look at the actual statistics. Of course, this makes for slightly grim reading at times, so the frequent injections of humour are most welcome.&lt;br/&gt;The UK quite often seems in the shadow of larger cultures. As the book points out:&lt;br/&gt;This same issue is understudied in the UK – to the point where the absence of research is glaring. While we are clearly not just a little America, we aren’t so different that we can be confident we’re not replicating the same here.&lt;br/&gt;Because of that, at times the book does feel a little bit like the &amp;#34;we have [racism] at home&amp;#34; meme.&lt;br/&gt;There&amp;#39;s an almost relentlessly cheery irreverence to some of the issues - but what else can you do? Sometimes it takes a good dose of comedy to cut through the bleakness. There&amp;#39;s a slight streak of ageism within the book - especially when it comes to older doctors trying to use new technology. Although, to be fair, that was also my experience when working at NHSX. There are some people within the system who are desperate for it to remain preserved in aspic as even minor improvements are a threat to their position in the hierarchy.&lt;br/&gt;A minor grumble, the problem I have with books like this - and with The Secret Barrister - is that a lot of the moaning about how hard it is to become a professional doctor / lawyer just generates a giant shrug from me. Every piece of medical media in the last 50 years talks about how bone-crunchingly hard it is. All I can think of is &amp;#34;none of this should be coming as a surprise to you!&amp;#34; Should it be easier (and less sleep depriving) to be a doctor? Sure! But given it isn&amp;#39;t, perhaps dial down on the clichés of how uniquely awful your chosen career is.&lt;br/&gt;The book isn&amp;#39;t just a list of complaints, of course, everything is backed up with statistics (although unreferenced). Thankfully, the book ends with some practical suggestions of how the NHS can improve. It would certainly be interesting to read a version of this from 2046 to see how much has changed - given the improvements already bearing fruit, I&amp;#39;d hope that the UK health service would become much less racist over time.&lt;br/&gt;&lt;br/&gt;&lt;a href=&#34;https://shkspr.mobi/blog/2026/10/book-review-are-you-really-the-doctor-by-matthew-hutchinson/&#34;&gt;https://shkspr.mobi/blog/2026/10/book-review-are-you-really-the-doctor-by-matthew-hutchinson/&lt;/a&gt;&lt;br/&gt; &lt;img src=&#34;https://shkspr.mobi/blog/wp-content/uploads/2026/10/are-you-really-the-doctor.avif&#34;&gt; &lt;br/&gt;&lt;a href=&#34;https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=76754&amp;amp;HTTP_REFERER=Atom&#34;&gt;https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=76754&amp;amp;HTTP_REFERER=Atom&lt;/a&gt;&lt;br/&gt;&lt;br/&gt;#book #hutchinson #doctor #nhs #adamkay #bbc&lt;br/&gt;&lt;br/&gt;#rss #feeds #linksharing #qwibbit
    </content>
    <updated>2026-10-17T15:30:05Z</updated>
  </entry>

  <entry>
    <id>https://njump.me/nevent1qqs0q7hha5jvwhnuc86ugv4hjf89nhysp6qkjurx5d6atu7y7xgjerczyq9uyse4xg6pxe4f8vxceclvm45g99htvkxt6athz9lhaejs5jq425l6dja</id>
    
      <title type="html">Concert Review: London Voices - Video Games Go Choral ...</title>
    
    <link rel="alternate" href="https://njump.me/nevent1qqs0q7hha5jvwhnuc86ugv4hjf89nhysp6qkjurx5d6atu7y7xgjerczyq9uyse4xg6pxe4f8vxceclvm45g99htvkxt6athz9lhaejs5jq425l6dja" />
    <content type="html">
      Concert Review: London Voices - Video Games Go Choral ★★★★☆&lt;br/&gt;&lt;br/&gt;London&amp;#39;s musical entertainment scene has something for everyone - from long-running blockbuster shows to no-name bands in dingy basements. Somewhere in the middle is monophonic plainchant versions of the Halo theme sung in a church.&lt;br/&gt;The acoustics at St Martin in the Field are echoey. That makes it rather hard to hear the amplified announcement to turn off our phones, but makes an a cappella chorus absolutely soar. The choir of London Voices have sung on just about every movie you&amp;#39;ve watched and game you&amp;#39;ve played - so they&amp;#39;re no stranger to more modern compositions.&lt;br/&gt;The programme strictly alternated between songs from popular games are some more traditional verses. As a tone-deaf musical ignoramus, I found some of the pieces hard to distinguish from each other and, perhaps, just a little repetitive. Delightfully, the choir is generous with its solos. Far too often these events have one main star while everyone else glares daggers at them. Here it seemed quite the opposite, with a range of voices brought to the forefront.&lt;br/&gt;Parts of the programme are a little odd. While Pilentze Pee is an incredible song and sung extremely well, I don&amp;#39;t think there&amp;#39;s much to tie it to The Wind Waker. Of course, the highlight was a cheeky rendition of &amp;#34;Still Alive&amp;#34; from Portal. An excellent arrangement sung with joy and gusto.&lt;br/&gt;An eclectic and interesting event, marred only by the uncomfortable pews of the church. Truly we must suffer for other people&amp;#39;s art!&lt;br/&gt;&lt;br/&gt;&lt;a href=&#34;https://shkspr.mobi/blog/2026/10/concert-review-london-voices-video-games-go-choral/&#34;&gt;https://shkspr.mobi/blog/2026/10/concert-review-london-voices-video-games-go-choral/&lt;/a&gt;&lt;br/&gt; &lt;img src=&#34;https://shkspr.mobi/blog/wp-content/uploads/2026/10/Video_Games_Go_Choral.webp&#34;&gt; &lt;br/&gt;&lt;a href=&#34;https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=76517&amp;amp;HTTP_REFERER=Atom&#34;&gt;https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=76517&amp;amp;HTTP_REFERER=Atom&lt;/a&gt;
    </content>
    <updated>2026-10-16T07:38:15Z</updated>
  </entry>

</feed>