SSH MITM at Best Western

I'm currently staying in a Best Western hotel in Eureka, CA, avoiding the Bay Area heat wave, and I noticed something remarkable: the hotel's free WiFi network performs automatic man-in-the-middle interception of all SSH traffic. I've literally never seen this before on public WiFi... Check it out:

$ ssh github.com
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
SHA256:lDE/b9yqZmX2oUniEgQvWsxWeq7wyRTghSYS649tLHk.
Please contact your system administrator.
Add correct host key in /Users/jbrown/.ssh/known_hosts to get rid of this message.
Offending RSA key in /Users/jbrown/.ssh/known_hosts:5
Host key for github.com has changed and you have requested strict checking.
Host key verification failed
read more

Let's Talk About Elastomeric Masks!

As has become abundantly clear recently, COVID-19 isn't going away; in fact, it's striking more and more of my friends and family who had successfully avoided it for years. At this point, we have ample evidence that masks work to drastically reduce the spread of COVID; however, none of the local, state, or federal governments are willing to take the low-cost, high-return step of requiring mask usage in public settings, so most people just don't bother. The fact that I'm usually the only one wearing a mask in any given space, combined with the increased contagiousness of the Omicron family of variants, means that I've been spending the last few months wearing N95 masks a lot of the time. Given that I had an unavoidable plane trip coming up, and spurred by the recent New York Times article on elasticmeric masks, I decided that it was time to investigate reusable (and potentially both more-protective and more-comfortable) options. In general, reusable respirators (often called "elastomeric" because of the rubbery plastic they're made of) offer better filtration and much better seals than disposable masks -- if you wear glasses and have problems with fogging due to a poorly-fit disposable mask, you should strongly consider an elastomeric mask. After some research, I ended up buying three different reusable half-facepiece respirators, and this post has some brief impressions of them.

Just a note up front: I am (of course) not a doctor and can't give you medical advice. As far as I know, all of these respirators are suitable for reducing your risk of catching COVID-19, but you should probably talk to an actual expert. You can find some useful information on the CDC's page about elastomeric respirators.

read more

Reflecting on Photography Gear; Leaving Micro Four Thirds

As far back as I can remember, I've enjoyed photography as a hobby. It's probably because my father's father was a journalist and he never went anywhere without his Leica around his neck --- or maybe it's just because there's something magical at being able to hold the past in your hand and look at it whenever you want.

My grandfather, seen with his Leica Minilux

While it's absolutely true that your camera doesn't matter, I've just recently changed up my camera gear again, and I thought it might be fun to look back at some of the cameras I've used over the last 20 or so years.

We had some cheap 35mm (and APS) film cameras when I was young, but things really took off when we got a Sony Digital Mavica (the very first one, the MVC-FD5 which took digital pictures and record them onto regular 3.5" floppy disks). No more driving to CVS and waiting 3 days for them to develop your photos? Wow! The pictures were awful, but it was a start, and I was hooked on digital photography. A few years later (in 2001), the family upgraded to an HP PhotoSmart (I think model 215, but it's not in the EXIF data) with an unbelievable 1.3MP resolution and a 4MB CompactFlash card that could store so many more photos than a 1.44MB floppy. Just look at this image (whose EXIF data says 2001-01-01, but couldn't been earlier than March 2001):

This is what pre-9/11 America looked like. Really bad exposure

In 2003, there was a brief sidegrade to the still-1.3MP (but much prettier and smaller) Sony CyberShot DSC-U10. It didn't really take better pictures, but because it was smaller and lighter, it could go more places, and like Chase Jarvis says, "the best camera is the one you have with you."

read more

2022 GPG Key Transition

GnuPG Logo

Yet again, it has come time to rotate my PGP/GnuPG private key. My old key (1ED5E5A301C3D109904022893C7775DD37811E62) actually expired a couple of weeks ago, and I've been procrastinating writing up this transition. The new key is 0xC6496DEB3DA8E9B5 (full fingerprint: 24F8AA354990F3F562EC014BC6496DEB3DA8E9B5) You can also find it at https://files.roguelazer.com/roguelazer.gpg. It has also been attached to my keybase.io account and my Github profile. It is cross-signed by the old key.

My signed transition document is below, and can also be found at 2022-05-28-key-transition-statement.txt.asc if you prefer to download it directly.

read more

COVID-19 Finally Hits at Home

After more than two years of successfully dodging it, COVID-19 finally hit my family the week. On Tuesday my wife woke up with a migraine, which isn't unusual for her; on Wednesday morning, she woke up with a low (100.4°F fever) and we belatedly realized that she might be sick. She took an antigen test1 and the TEST line turned blue basically instantly.

Shit.

Quarantine ensued. Isaac (our son) has been home from school2 and we've all been cooped up on the property for the past few days.

So far, Isaac and I have continued to be symptom-free and testing negative on antigen tests. I also got one PCR 3 (so far) which was negative. We've been trying to isolate as much as possible, but Eva and I both have full-time jobs, and Isaac can't go back to day care until this nightmare is over, so there's only so much we can do — we also have a relatively small house which doesn't have any tightly-closing inside doors. Both of us adults have been wearing N95 masks whenever awake, and we have HEPA air purifiers4 running in whatever rooms we happen to be occupying.

Eva is fully-vaccinated and has had mild symptoms (runny nose, low fever, etc), but she still is very strongly positive on antigen tests; hopefully she'll recover in the next few days, and then Isaac and I can get PCRs, and then he can go back to school, and then in a few weeks he can finally get vaccinated5, and then we'll be done with this... at least, for a little while...

In the meantime, I'm left wondering why Eva got sick and I didn't. It's not like she's gone anywhere I haven't. Is my immune system just stronger — did we both get exposed, and I happened to produce some overwhelming immune response? Or did I just happen to dodge whatever breath carried the disease, and every second I spend in this household is another chance of exposure?

We're, of course, extremely lucky to have made it two years without anyone in this household (or our parents or siblings) getting sick, and we're lucky that neither of us is seriously ill, but damn if this isn't annoying.

Some more reading, if you're in a COVID mood:

À bientôt.

1

We have to test basically constantly because of colds from day-care, so we go through 2-4 tests a week. I've been buying the on/go tests from various online retailers, but we've also used FlowFlex, Binax, and iHealth tests. The free federal ones were nice but lasted... a couple of weeks...

2

Because we're not sociopaths and don't want to get his classmates sick. I've had some... distressing... discussions with other parents who don't even seem to be able to think of other peoples' children and just scheme how to get their sick kids back into school, damn the consequences.

3

This was a miserable experience. My primary care provider (One Medical) has no PCR appointments in the East Bay available in the next 5 weeks, and the municipal sites are all shut down. I ended up going to the state-affiliated site (LHI), which doesn't let you reserve PCR tests any more, but if you make an appointment for a rapid test, you can ask for a PCR when you get there, and they'll give it to you if they have supply. We're 27 months into this pandemic... how have we still not figured out testing‽

4

We already had a Coway AP-1512HH(W) and I grabbed a couple of Blueair 411-Autos.

5

Don't even get me started on how insanely frustrating the situation is with vaccines for children under 5. Despite those children being prolific carriers of COVID, the FDA seems determined to not let them get vaccinated. My best guess is that someone at the FDA is in the pocket of Pfizer and that's why they've sat on the highly effective ModeRNA vaccine for the last two months; see also, this tweet thread.

The Long-Overdue Baby Gear Post

As you might recall, my son Isaac was born a couple of years ago. This was obviously an occasion for joy and family happiness (and an end to regular sleep) — but beyond that, it was the beginning of a capitalist glut, a massive influx of new Stuff. So, uh, I figured I'd write about that in case any of my readers ever have a kid and want to know about various baby support items.

In general, we had a few constraints:

  • our house is relatively small (~1200sqft) and has little storage
  • when we were making most of these decisions, my wife and I spent a relatively large amount of time traveling (both via local transit options like BART and AC Transit, and on planes to visit our far-flung families)
  • money/price is... not a chief concern

Some of my go-tos for researching items before buying them were the Wirecutter, Baby Gear Lab, Consumer Reports but, frankly, none of these had quite the same set of priorities as we did. We also went to some local retail stores (shout-out to the now-defunct Tot Tank in Alameda) and of course talked to all of our friends with kids. For non-safety-sensitive things, Berkeley Parents Network has been a good resource for used items.

Anyhow, read on for all the big-ticket items!

read more

New Chair!

Hello dear readers! Like most of you (and as I've written about before), I've been stuck working from home for the last year and a half, out of the little converted covered porch on my house. For the last six months or so, I've been doing so with my butt firmly planted on an HON Basyx pleather-upholstered office chair that I rescued from my office when we moved out. This chair is, uh, not in the best condition1:

old chair

This chair actually originally belonged to Curse, a now-defunct company that made some kind of WoW fansite. They used to be in my company's old office (2016-2018) and left behind all of their furniture when they were acquired by Amazon. I snagged one of these pleather chairs from their board room and had been using it since.

Anyhow, I finally decided to replace the chair a few months ago and kicked off a long search for a better chair. Something you may not have known: it was really damn hard to buy a chair at the height of the pandemic. Every chair in the universe was backordered and no stores would let you actually sit down in chairs to try them out. Starting this spring, though, things started to lighten up a bit, and I was able to go to some stores (including the local Design Within Reach) and try out some overpriced chairs. In the end, I decided to go for a Herman-Miller Cosm, which is kind of a strange chair. It's designed for public spaces and has very little customization. For most people, I think that's a drawback, but I really hate highly-customizable chairs like the Aeron because when I'm in them, I spend all of my time fidgeting with the levers to try to get a slightly more comfortable experience. For Herman-Miller, it came down to the Cosm or the Eames Executive, both of which are very comfortable, low-configuration chairs. I also of course looked at Steelcase, as well as some of the cheap brands at big-box stores. In the end, I decided that the Cosm was the most comfortable option2, and also cost $2000 less than the Eames chairs, so that's what I bought3.

new chair

Now if I could just figure out some way to make it not 90°F in this room when the sun's out and 55° otherwise...

1

Fun fact: those little black dots you can see on the rug under the chair aren't random dirt, they're actually little flakes of the synthetic leather from the chair.

2

The seat is a little short, which kind of sucks, but the "auto-harmonic tilt" back is just right

3

Specifically, with a mid back and adjustable arms. I actually don't use my chair arms very much because I have terrible posture and rest my arms on my desk instead, but the cool-looking wing arms on the Cosm by default are... just awful...

CDPH Digital Vaccine Record

Yesterday, California released their Digital Vaccine Record system for securely verifying residents' COVID-19 vaccination status. I took a look at it and thought I'd write up my findings here. At a high level, the DVR consists of a QR code which contains a cryptographically-signed assertion in JSON Web Token (JWT) format. I'll walk you through how to get one, how to decode it, and what it contains in the rest of this article.

Getting one of the tokens is pretty easy; you just go to the Digital Vaccine Record website and put in your name, date of birth, and the phone number or email address you gave when you got your vaccine. I got mine through CVS, and, for whatever reason, they only provided my phone number to the state, not my email address. The state will then send you an email or SMS containing a short-lived link to a website which will show a very large and information-dense QR Code.

You can scan this QR code with the QR code scanner of your choice and you should get back some text1 like the following:

shc:/56762909524320603460292437404460312229595326546034602925407728043360287028647167452228092861553055647103414321643241110839632106452403713377212638633677424237084126633945392929556404425627596537253338446120605736010645293353127074232853503870255550530362394304317059085244240537...

This very long string is a Smart Health Card token, which is the framework being used by several states. Let's decode it with a little bit of Python!

First, create a virtualenv somewhere handy with python3 -m virtualenv venv. Next, we'll install my forked copy of pyjwt (which adds gzip support, as required for SHC) with ./venv/bin/pip install -e 'git+https://github.com/Roguelazer/pyjwt.git@deflate#egg=pyjwt[crypto]'2.

The following code will verify the signature on your SHC and print out the contents:

import jwt
import json


def decode(code):
    if code.startswith('shc:/'):
        code = code[5:]
    # decode the numeric data into binary data
    if len(code) % 2 != 0:
        raise ValueError('code is not the right length')
    d = []
    for i in range(int(len(code) / 2)):
        d.append(chr(int(code[i * 2] + code[(i * 2) + 1]) + 45))
    d = ''.join(d)
    # download the public keys from the CDPH website. Oddity: the .well-known/
    # directory should be at the top-level, but it isn't!
    jwks_client = jwt.PyJWKClient(
        'https://myvaccinerecord.cdph.ca.gov/creds/.well-known/jwks.json'
    )
    # find the matching signing key based on the header
    signing_key = jwks_client.get_signing_key_from_jwt(d)
    data = jwt.decode(d, signing_key.key, algorithms=['ES256', 'RS256'])
    return data


code = input('SHC code: ')
print(json.dumps(decode(code)))

If it works, you should get something like the following:

{
  "iss": "https://myvaccinerecord.cdph.ca.gov/creds",
  "nbf": 1624036526,
  "vc": {
    "type": [
      "https://smarthealth.cards#health-card",
      "https://smarthealth.cards#immunization",
      "https://smarthealth.cards#covid19"
    ],
    "credentialSubject": {
      "fhirVersion": "4.0.1",
      "fhirBundle": {
        "resourceType": "Bundle",
        "type": "collection",
        "entry": [
          {
            "fullUrl": "resource:0",
            "resource": {
              "resourceType": "Patient",
              "name": [
                {
                  "family": "YOUR LAST NAME",
                  "given": [
                    "YOUR FIRST NAME"
                  ]
                }
              ],
              "birthDate": "YOUR BIRTH DATE"
            }
          },
          {
            "fullUrl": "resource:1",
            "resource": {
              "resourceType": "Immunization",
              "status": "completed",
              "vaccineCode": {
                "coding": [
                  {
                    "system": "http://hl7.org/fhir/sid/cvx",
                    "code": "207"
                  }
                ]
              },
              "patient": {
                "reference": "resource:0"
              },
              "occurrenceDateTime": "2021-04-19",
              "performer": [
                {
                  "actor": {
                    "display": "CVS CORPORATE"
                  }
                }
              ],
              "lotNumber": "036B21A"
            }
          },
          {
            "fullUrl": "resource:2",
            "resource": {
              "resourceType": "Immunization",
              "status": "completed",
              "vaccineCode": {
                "coding": [
                  {
                    "system": "http://hl7.org/fhir/sid/cvx",
                    "code": "207"
                  }
                ]
              },
              "patient": {
                "reference": "resource:0"
              },
              "occurrenceDateTime": "2021-05-18",
              "performer": [
                {
                  "actor": {
                    "display": "CVS CORPORATE"
                  }
                }
              ],
              "lotNumber": " 025C21A"
            }
          }
        ]
      }
    }
  }
}

This is pretty much what you might expect and is in line with, e.g., this very good article from the Mozilla blog. It contains the minimum amount of information required to identify someone (ideally paired with looking at their license or something), it can be easily printed or otherwise stored offline3, it's cryptographically verified using standard protocols and technologies. The only thing that worries me is that it doesn't seem to have a human-readable field to encode what kind of vaccine it was that I received4, so if it turns out that one of the vaccines is ineffective against a variant and folks with that shot need a booster in order to be considered "fully vaccinated", that will require some gymnastics. That's a pretty minor and hypothetical concern, though.

Good on California for coming up with a reasonable and efficient system5! I look forward for using it in the future to be able to go to crowded places and feel confident that I'm not putting my un-vaccinatable son at risk. Hooray!

1

Mine is more than 1600 digits!

2

This line says, basically, install the package at https://github.com/Roguelazer/pyjwt.git from the "deflate" branch, under the name "pyjwt", and with the optional feature "crypto" to enable cryptographically validating tokens

4

I assume this is what the "vaccine.coding.system" and "vaccine.coding.code" fields are supposed to represent, but these are not human-friendly and http://hl7.org/fhir/sid/cvx is a dead link.

5

And, particularly, good on California for doing so without doing anything deeply stupid involving blockchains

More ModeRNA: Vaccinated (part 2)

Yesterday around 10am, I got my second shot of the ModeRNA COVID-19 vaccine (unsurprisingly, I got my first show four weeks ago); at this point I'm in the countdown to be "fully vaccinated"1. So far, the side effects have been pretty mild:

  • I was maybe a little tired yesterday afternoon
  • Yesterday evening I had an elevated temperature of 99.7°F, but no clinical fever
  • This morning my arm feels like I lost a really intense game of Punch Buggy

I know the pandemic is nowhere near over and people are dying by the thousands around the world, but I'm certainly happy that my chances of serious illness are now very close to zilch.

Speaking of the pandemic being nowhere near over, let me just say how bizarre I find last week's CDC guidance on face masks and social distancing... 48% of the US is currently vaccinated, with some states as low as 33%. Even here in Alameda County, only 60% of eligible adults are vaccinated so far. I've been spending the last year watching an endless sequence of men2 who certainly were not vaccinated refuse to take any precautions; now the CDC is saying that I'm all of a sudden supposed to trust that all the newly-unmasked people around me aren't in the 40% who haven't gotten vaccinated? Yeah, right! I know that I, when I am fully-vaccinated, will not be at much risk, but I'd rather not risk that my infant son join the increasing number of pediatric COVID-19 cases. I'm glad that California is being a little more conservative and waiting until June 15th. It's a damn shame that nobody was able to figure out a reliable "vaccine passport" system yet3; Mozilla has a good article on how this could be done that, unfortunately, I don't think any US state will ever bother to implement.

As usual, stay safe out there, readers.

1

"Maxinated"?

2

This has been an odd observation that I've made all throughout the pandemic: at least where I am, it's almost exclusively men who won't wear face masks. My wife and I have a game where we could how many couples we observe while walking where the apparently-female person is wearing a mask and the apparently-male person is not, and we never fail to see at least one such couple.

3

No, the insane "IBM Blockchain"-based New York Excelsior Pass proposal doesn't count.

What's on TV (May 1, 2021)

Just Finished

For All Mankind Season Two. This season had a lot of slow moments, and more of Karen than I really wanted but holy shit did those final two episodes deliver. If you've bought an Apple product in the last couple of years, you get this for free, so you might as well watch it!

Half-Way Through

Babylon 5 Season Three. I'd never seen any B5 and $SPOUSE and I decided to watch it over lockdown. It's pretty fun seeing how much DS9 stole from this show. My main complaint is that I don't really care about any of the human characters and really just want to watch the "Londo and G'Kar screw up the galaxy" show.

Just Started

Mythic Quest Season One. We're always on the hunt for a half-hour comedy to watch during nap-time, and this one's pretty good. Unlike Silicon Valley, this doesn't remind me of my day job too much. Came into it expecting Danny Pudi to be the main draw but Charlotte Nicdao really steals the show. Season Two comes out in a few weeks!