erincandescent ⎔

erincandescent ⎔

@erincandescent.net

⎔ immigrant | software engineer in card payments | I wrote the initial draft of ActivityPub and now spend a lot of thing thinking about how we design social systems with better dynamics | long-form: https://blog.erincandescent.net/

391 followers150 following323 posts

I have been doing some work on my blog recently to make it play better with the ATProto ecosystem, and. I had a lot of dreams back in 2014 as to the sorts of social applications you could build with ActivityPub and very few of them really materialised. In 2026 they have materialised, on ATProto. …

blog.erincandescent.netI have been doing some work on my blog recently to make it play better with the ATProto ecosystem, and.I have been doing some work on my blog recently to make it play better with the ATProto ecosystem, and.

Had a headache all morning and felt a bit grim. Eventually walked into the kitchen and saw the collection of caffeinated beverages on the shelf. Only at this point did it click that "wait. Maybe it's caffeine withdrawal"

Me: why is one of my services taking 3 whole minutes to pick up its background jobs ...me a few minutes later, on the database server_ apk add chrony && rc-update add chronyd default && rc-service chronyd start fucking timesync man.

Can't believe the definition of UNIX Time was so unfixably bad we as a society decided we had to abolish UTC instead (The CGPM has voted to abolish the leap second)

erincandescent ⎔ reposted

"$organization accepting LLM-generated PR descriptions silently lowers expectations" actually just statistical error. average committer produces 0 decent PR descriptions per year. Systems Georg, who lives in emacs and cares deeply about the sociotechnical whole, is an outlier adn should not have bee

erincandescent ⎔ reposted

Please stop hashing the public key first (or: ML-DSA's µ annoys me)

blog.erincandescent.netPlease stop hashing the public key first (or: ML-DSA's µ annoys me)A goal one might have when designing a signing algorithm is to prevent signing a message one has not seen. Signing a message one hasn't seen might sound difficult at first, but most classical signature algorithms, as commonly used, sign a hash of the message, rather than the message itself. This kind of arrangement is necessitated by the fact that the underlying algorithms are only capable of signing messages of a limited size. You might not have seen the message, but you might have seen the hash of it, or another signature which encodes said hash. In many protocols, this lets you produce a valid signature on a message you've never seen. That's rarely a problem in practice, but it can be surprising, and it's a pitfall for the unsuspecting protocol designer. More recent algorithms take steps to prevent this: * Ed25519 signs `SHA-512(R || PubK || M)` where R is functionally a random curve point, PubK is the public key, M is the message, and `||` is the concatenation operator * ML-DSA signs `SHAKE-256(SHAKE-256(PubK, 64) || 0x00 || len(ctx) || ctx || m, 64)` (where ctx is a 0 to 255 byte context string) This can complicate your signing code, particularly when dealing with HSMs or smart cards, but it doesn't require majorly reworking your signing process. Unfortunately, on the verification side things are less rosy. ## The challenge As a general rule of thumb, we should avoid touching the body of a signed message until we've verified the signature. Also as a general rule of thumb, cryptographic protocols where there are multiple signers put the signer's identity within said body (X.509, most JWT and CWT usages, SAML, I'm looking at all of you, but you're not the only ones). This means the verification process ends up being 1. Gingerly parse the message 2. Dig through as much message as necessary to identify the signing key 3. Actually verify the message 4. Actually consume the message Often (JWT, CWT and SAML again), there's either no key identifier present in the signed message, or it's permitted for multiple signing keys to share a key identifier†. This means that, if the issuer has multiple keys, you may have to do multiple trial-verifications to work out if the message is actually valid. One nice property that all of these protocols have (except XML Digital Signatures, but _nobody_ thinks XML Digital Signatures were a good idea) is that they carry the signing method in a header. So, with a traditional signing algorithm, you have the option of 1. Parsing the header 2. Identify algorithm and hence the hash algorithm 3. Hash the message 4. Gingerly parse the message 5. Dig through as much message as necessary to identify the signing key 6. Actually verify the message 7. Actually consume the message This comes with two advantages: 1. After step 3, you can throw away the raw message, reducing memory usage 2. In step 6, you pay for the cost of verifying a signature for every attempted key rather than also the cost of re-hashing the message. In most cases messages are small and this cost is also small, but every little helps. Unfortunately, it has the slight flaw that it doesn't work with either Ed25519 or ML-DSA as defined. For ML-DSA, it would have been quite easy to re-arrange matters. If we instead computed `SHAKE-256(0x00 || len(ctx) || ctx || m || SHAKE-256(PubK, 64), 64)` (i.e. we put the public key hash in _at the end_), then we can hold our SHAKE-256 context around and copy it for each trial verification. We'd end up keeping just over 200 bytes of SHAKE-256 context around (instead of 64 bytes of output), but that's not too bad. Unfortunately, that's not the choice that was made and we'll just have to deal with it. Ed25519 is slightly more challenging. If we reversed the hash computation to be `SHA-512(M || R || PubK)` instead, it would be vulnerable to length extension attacks (for any `SHA-512(X)` you can extend it into `SHA-512(X || some predictable garbage || Y)` for any arbitrary Y). Fixing that when Ed25519 was published in 2011 would be somewhat tricky, though the easiest option without truncating the hash is just to sign `SHA-512(SHA-512("ed25519 domain separation" || M || R || PubK))`. Since we don't have a time machine, it's all a bit academic. In 2026 you can just avoid the problem by using SHA-3, BLAKE2, a truncated SHA-2 variant, or any other modern hash function which isn't vulnerable to length extension attacks. In the meantime I'll just sigh and hope we can reduce the number of signatures we pass around, given they're really becoming rather large and expensive. * * * † You may be thinking it somewhat defeats the purpose of having a key identifier if they aren't unique. I don't disagree!

Please stop hashing the public key first (or: ML-DSA's µ annoys me)

blog.erincandescent.netPlease stop hashing the public key first (or: ML-DSA's µ annoys me)A goal one might have when designing a signing algorithm is to prevent signing a message one has not seen. Signing a message one hasn't seen might sound difficult at first, but most classical signature algorithms, as commonly used, sign a hash of the message, rather than the message itself. This kind of arrangement is necessitated by the fact that the underlying algorithms are only capable of signing messages of a limited size. You might not have seen the message, but you might have seen the hash of it, or another signature which encodes said hash. In many protocols, this lets you produce a valid signature on a message you've never seen. That's rarely a problem in practice, but it can be surprising, and it's a pitfall for the unsuspecting protocol designer. More recent algorithms take steps to prevent this: * Ed25519 signs `SHA-512(R || PubK || M)` where R is functionally a random curve point, PubK is the public key, M is the message, and `||` is the concatenation operator * ML-DSA signs `SHAKE-256(SHAKE-256(PubK, 64) || 0x00 || len(ctx) || ctx || m, 64)` (where ctx is a 0 to 255 byte context string) This can complicate your signing code, particularly when dealing with HSMs or smart cards, but it doesn't require majorly reworking your signing process. Unfortunately, on the verification side things are less rosy. ## The challenge As a general rule of thumb, we should avoid touching the body of a signed message until we've verified the signature. Also as a general rule of thumb, cryptographic protocols where there are multiple signers put the signer's identity within said body (X.509, most JWT and CWT usages, SAML, I'm looking at all of you, but you're not the only ones). This means the verification process ends up being 1. Gingerly parse the message 2. Dig through as much message as necessary to identify the signing key 3. Actually verify the message 4. Actually consume the message Often (JWT, CWT and SAML again), there's either no key identifier present in the signed message, or it's permitted for multiple signing keys to share a key identifier†. This means that, if the issuer has multiple keys, you may have to do multiple trial-verifications to work out if the message is actually valid. One nice property that all of these protocols have (except XML Digital Signatures, but _nobody_ thinks XML Digital Signatures were a good idea) is that they carry the signing method in a header. So, with a traditional signing algorithm, you have the option of 1. Parsing the header 2. Identify algorithm and hence the hash algorithm 3. Hash the message 4. Gingerly parse the message 5. Dig through as much message as necessary to identify the signing key 6. Actually verify the message 7. Actually consume the message This comes with two advantages: 1. After step 3, you can throw away the raw message, reducing memory usage 2. In step 6, you pay for the cost of verifying a signature for every attempted key rather than also the cost of re-hashing the message. In most cases messages are small and this cost is also small, but every little helps. Unfortunately, it has the slight flaw that it doesn't work with either Ed25519 or ML-DSA as defined. For ML-DSA, it would have been quite easy to re-arrange matters. If we instead computed `SHAKE-256(0x00 || len(ctx) || ctx || m || SHAKE-256(PubK, 64), 64)` (i.e. we put the public key hash in _at the end_), then we can hold our SHAKE-256 context around and copy it for each trial verification. We'd end up keeping just over 200 bytes of SHAKE-256 context around (instead of 64 bytes of output), but that's not too bad. Unfortunately, that's not the choice that was made and we'll just have to deal with it. Ed25519 is slightly more challenging. If we reversed the hash computation to be `SHA-512(M || R || PubK)` instead, it would be vulnerable to length extension attacks (for any `SHA-512(X)` you can extend it into `SHA-512(X || some predictable garbage || Y)` for any arbitrary Y). Fixing that when Ed25519 was published in 2011 would be somewhat tricky, though the easiest option without truncating the hash is just to sign `SHA-512(SHA-512("ed25519 domain separation" || M || R || PubK))`. Since we don't have a time machine, it's all a bit academic. In 2026 you can just avoid the problem by using SHA-3, BLAKE2, a truncated SHA-2 variant, or any other modern hash function which isn't vulnerable to length extension attacks. In the meantime I'll just sigh and hope we can reduce the number of signatures we pass around, given they're really becoming rather large and expensive. * * * † You may be thinking it somewhat defeats the purpose of having a key identifier if they aren't unique. I don't disagree!

In the continuing tale of networking hauntedness, I've had some mysteriously slow downloads for months, that finally annoyed me enough to go properly debugging 😭 Turns out that fiber that looked plugged into the SFP wasn't and I had 1-5% packet loss on a primary link on our LAN

For extra pain this _didn't_ show up as more than a handful of receive errors because most of the packets getting yeeted had such a bad SNR it didn't recognise them as packets

erincandescent ⎔ reposted

I just don't understand how people can eulogize Swartz and simultaneously conclude that the world needs *more* intellectual property enforcement

Darren Dahly@statsepi.bsky.social

They hounded Aaron Swartz to death for downloading papers from jstor but society is now supposed to just accept mass theft because this thing can write that TPS report *for* you. en.wikipedia.org/wiki/Aaron_S...

"Mistral said Le Chonk - officially named Mistral Large 4 - will be made fully public on October 27." How is this real text from a real Reuters article?! 😭https://www.reuters.com/world/china/mistral-ceo-says-new-ai-model-beats-chinese-ones-some-areas-2026-10-06/

Prediction: A track created substantially with Generative AI will wedge itself solidly inside our collective heads and refuse to be evicted in the next 12 months. Think something "Baby shark"-esque

Spent the morning debugging a confusing networking problem (ARPs from VM not being bridged onto the network). Turns out: NIC driver's haunted. Newer kernel exorcises the bug.

Ok, I skipped the bacon, but this still slaps. One thing to work on: the garlic bread does have structural integrity problems, it really wasn't designed to be split in the middle, I wonder if I can find a better one

kira ☆@kira.ws

polish invention of national importance: the czosndog. a hot dog whose bun is an entire garlic baguette. czosnek = garlic. split it, sausage in, cheese on top, bake. bacon, chilli and spring onion if you respect yourself. born on wykop in 2020, beloved since

erincandescent ⎔ reposted

Replying to @esqueer.net

let’s maybe not?.. the destruction is very much real but using AI-generated imagery instead of real satellite photos only makes it easier to deny

erincandescent ⎔ reposted

as someone who has been really critical of a lot of the initial breathless "teehee, weird sex" reportage, i really appreciate this thread for nailing the actual fucked up immoral thing that is happening; you CANNOT consent if your employment or job success are on the line

Faine Greenwood@faineg.com

Women trying to succeed in Silicon Valley AI and effective altruist spaces are reporting feeling serious social pressure to attend sex parties - where people have reported being sexually assaulted, like below. Events like Aella’s “Slutcon,” which is happening again this weekend.

erincandescent ⎔ reposted

Jaws Slayer 🎃@jesawyer.bsky.social

I’ve been seeing a lot of new characters on billboards in the SweetJames Multiverse and I don’t like it. I want only the original, somewhat unsettling, Sweet James.

I picked up Silent Hill: Townfall on a whim and while it's not without flaws, it nails the feeling of 90s working class Britain (when it's not just nailing the feeling of desperation required of a horror game)

erincandescent ⎔ reposted

It's really refreshing seeing shitey normal working class life depicted like this in a game, and a Silent Hill game no less. Pebble-dashed council flats, three-bar fires, Post Office counters, concrete shopping precincts. This must be how posh people felt playing Everybody's Gone to the Rapture.

Andy Kelly@ultrabrilliant.xyz

Seeing a five quid powercard in a Silent Hill game is mangling my mind more than any of the faceless shambling horrors.

Short story idea: an astronomer many billions of years into the future muses over the fact that they know other galaxies once existed from many records and photos, but all evidence is now gone from the night's sky

If your multi-page essay on why LLMs can't be conscious fundamentally boils down the idea that man can't create a soul, then it's not a scientific or philosophical argument but in fact a religious one. I expect better epistemics.

erincandescent ⎔ reposted

Polybius Biotech's Eastnor Spaceport has been taken over. Leaked security footage from the facility's final hours, shows the rebel forces… throwing a sick party.

It has taken all of a couple of months for work to go from "everyone gets a Claude license!" to "Sonnet is blocked and maybe tomorrow we'll block Opus too". Incredible levels of own-goal scoring from team Anthropic lately.

blog.erincandescent.netOn finding bugs in Ceph(X)On the 28th August 2026, the Ceph project released fixes for CVE-2025-30156 and CVE-2026-39944. I first reported these to the project on the 20th December 2024; they were independently rediscovered in the meantime. These are high risk bugs that are also _extremely_ difficult to remediate, and require coordination across a lot of projects (not just Ceph itself, but the Linux kernel, Rook, and other related projects). They also required implementing methods of doing cluster key rotations. On the one hand, I understand why it took so long to fix these; on the other hand, it is still disappointing that the resolution timeline took over 18 months. Nontheless, my thanks to the Ceph team for their handling of this. These were human findings. Today, I expect you could throw an LLM at the Ceph codebase and these sorts of bugs would fall out quite quickly, but that wasn't the case in 2024. ## Background Ceph RGW has functionality which integrates with OpenStacks KeyStone, and also emulates the AWS STS API. I was investigating how these worked, looking at the possibility of doing an integration. As I was doing this, I noticed that the same `CryptoKeyHandler` class was used for to hold keys for both HMAC-SHA256 MACs and AES-CBC encryption. This piqued my curiosity; mixing keys with different algorithms like this is a code smell. In the end, the same keys are never reused with both, so while this is suboptimal design it isn't a security problem as written. However, this lead me to look at the AES-CBC encryption code, where I discovered that fixed keys were reused with hardcoded nonces. AES-CBC is not an authenticated-encryption mode (meaning it does not protect against message alteration, though it does hinder it), and also degrades in security significantly when nonces are reused. As I discovered, this use of AES-CBC was the _only_ protection applied to messages protected with CephX, and to RGW session tokens. As the CLYSO researchers who also found this bug demonstrated, it is possible to elevate privileges on vulnerable RGW installs with simple bitflips. This is sadly not a complicated issue to find; any junior cryptographic engineer should have several klaxons going off in their head at the sight of a hardcoded nonce without good justification. I was not searching through the Ceph codebase for a critical security vulnerability; I just happened to stumble upon it. ## Takeaways Ceph is an old project; the design of CephX likely predates the wide dissemination of the AEAD construct. We should pay attention to other codebases of similar vintage which also sometimes had to hand-roll their cryptographic protocols out of necessity.