<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://www.shh.sh/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.shh.sh/" rel="alternate" type="text/html" /><updated>2025-12-13T16:11:08+00:00</updated><id>https://www.shh.sh/feed.xml</id><title type="html">the life and times of @shh</title><subtitle></subtitle><author><name>@shh</name></author><entry><title type="html">Streaming with Full-Body Tracking in VR</title><link href="https://www.shh.sh/2020/09/06/full-body-tracking.html" rel="alternate" type="text/html" title="Streaming with Full-Body Tracking in VR" /><published>2020-09-06T00:00:00+00:00</published><updated>2020-09-06T00:00:00+00:00</updated><id>https://www.shh.sh/2020/09/06/full-body-tracking</id><content type="html" xml:base="https://www.shh.sh/2020/09/06/full-body-tracking.html"><![CDATA[<p><img src="/images/2020-09-06-full-body-tracking/preview.gif" alt="Motion Capture of Miku playing Beat Saber" /></p>

<h2 id="introduction">Introduction</h2>

<p>I’ve been a huge fan of <a href="https://en.wikipedia.org/wiki/Virtual_reality">VR</a> ever since we saw some of the first few consumer products hit the market in around 2015/2016, and so back then I got myself a HTC Vive headset and controllers and upgraded aspects of my gaming PC to accommodate the more strenuous requirements at the time. The game <a href="https://store.steampowered.com/app/620980/Beat_Saber/">Beat Saber</a> has particularly piqued my interest (in addition to a few other discoveries such as <a href="https://store.steampowered.com/app/418650/Space_Pirate_Trainer/">Space Pirate Trainer</a>, <a href="https://store.steampowered.com/app/322500/SUPERHOT/">SUPERHOT</a>, and <a href="https://store.steampowered.com/app/1079800/Pistol_Whip/">Pistol Whip</a>). I also like occasionally streaming my ventures on <a href="https://www.twitch.tv/worldwise001">Twitch</a>. However there are some significant differences when trying to stream from the perspective of a typical keyboard+mouse/controller non-VR game, versus from the perspective of a controller/tracker VR game.</p>

<h3 id="current-vr-setup">Current VR Setup</h3>

<p>The setup I’ve had for some time is an original HTC Vive Headset from around 2016, complete with the 2016 era controllers. It comes with two lightboxes, which are used to track the position of the headset and controllers with a remarkable amount of accuracy and smoothness.</p>

<p>The VR headset itself supports a resolution of 2160×1200, which is about 1080×1200 per eye. To support this sort of resolution, you generally needed a pretty beefy graphics card at the time, so here mine is supported by a GTX 1080.</p>

<p><img src="/images/2020-09-06-full-body-tracking/lightbox1.png" alt="Lightbox 1" /></p>

<p><img src="/images/2020-09-06-full-body-tracking/lightbox2.png" alt="Lightbox 2" /></p>

<p><img src="/images/2020-09-06-full-body-tracking/headset.png" alt="HTC Vive Headset and Controllers" /></p>

<p><img src="/images/2020-09-06-full-body-tracking/computer.png" alt="Gaming Desktop" /></p>

<h3 id="streaming-vr-vs-non-vr">Streaming VR vs non-VR</h3>

<p>All the VR games generally have a non-VR window that is displayed on the desktop monitor. You can easily capture that output using some popular off-the-shelf software, such as OBS. The view is somewhat indicative of what’s visible in the headset, usually from one eye, but of course unless the game supports adjusting the FOV, the default FOV has a hard time showing everything that’s going on. There’s also the problem of, unlike controlled in a mouse/keyboard/controller situation, human bodies actually sometimes have very jerky movement. As the wearer of the headset you don’t notice since your eyes naturally locks onto targets while you move, however for external viewers it can be very disorienting.</p>

<p><img src="/images/2020-09-06-full-body-tracking/spt-1p.gif" alt="Desktop Capture of Space Pirate Trainer" /></p>

<p><img src="/images/2020-09-06-full-body-tracking/beatsaber-1p.gif" alt="Desktop Capture of Beat Saber" /></p>

<p>Compare that to this of Halo: Reach where you can get a better field perspective of what’s going on.</p>

<p><img src="/images/2020-09-06-full-body-tracking/halo-reach.gif" alt="Desktop Capture of Halo: Reach" /></p>

<h2 id="htc-vive-trackers-setup">HTC Vive Trackers Setup</h2>

<p>You’ll want three trackers, two for your feet and one for your waist. This is the typical setup for most Full-Body tracking situations. The trackers themselves only come with a camera mount for attachment, so it’s up to you to figure out how to attach them to your person. I went ahead and bought the <a href="https://www.amazon.com/gp/product/B07DFBR6Y3">TrackBelt straps</a> from Amazon and they work well enough. Note: despite the picture, the straps DO NOT come with trackers! Each Vive Tracker currently retails for $99 USD.</p>

<p>It’s fairly easy to set up a single HTC Vive Tracker. It’s harder to set up 3. Kudos to <a href="https://www.reddit.com/r/Vive/comments/67jaqd/how_to_pair_3_extra_trackers/dgs3au8/">this reddit user</a> for figuring out the careful set of steps, reposted here:</p>

<ul>
  <li>Plug in Tracker A Dongle</li>
  <li>Plug Tracker A into a wired USB hub that is not occupied by the Tracker A Dongle</li>
  <li>See SteamVR find Tracker A (it will be lit green).</li>
  <li>Unplug Tracker A’s wired USB cable.</li>
  <li>Tracker A will now be gray, but you can right-click on it</li>
  <li>Right click on Tracker A in SteamVR, select “pair Tracker”</li>
  <li>Hold button on Tracker A for 4 seconds until blue light is flashing</li>
  <li>Tracker will pair (sometimes can take 2-3 tries, be patient)</li>
  <li>Label the Tracker correctly in Steam (as left foot, right foot, or waist!)</li>
  <li>Turn off Tracker A (right click in SteamVR and power off Tracker. If you do it by holding the button down for six seconds ‘till it powers off you may force it to un-pair because it will cycle through the pairing process again)</li>
  <li>UNPLUG TRACKER A DONGLE</li>
  <li>Plug in Tracker B Dongle and repeat all steps with Trackers B, and then C</li>
  <li>Plug in all 3 Tracker Dongles, Turn on both controllers, and then power up all 3 Trackers. You should see everything lit up.</li>
</ul>

<p>If you have lost track of Tracker A, B, etc. I recommend labeling your dongles and your trackers:</p>

<p><img src="/images/2020-09-06-full-body-tracking/dongles.png" alt="Labeled Dongles" /></p>

<p><img src="/images/2020-09-06-full-body-tracking/trackers.png" alt="Labeled Trackers" /></p>

<p>R = Right foot, L = Left food, W = Waist.</p>

<p>You’ll of course want to correctly label each tracker as you add them, since otherwise it’s basically impossible to distinguish which from which.</p>

<p><img src="/images/2020-09-06-full-body-tracking/steam-tracker-config.png" alt="Tracker configuration/labeling in Steam VR" /></p>

<h2 id="setting-up-liv">Setting up LIV</h2>

<p>I spent a lot of time trying to get avatar setup with Beat Saber back in April 2020. At the time I was armed with only a Kinect, which provided emulated Vive trackers to try and make the feet and waist visible to Beat Saber Custom Avatar. All the results at the time were extremely poor; the avatar would twist itself/get mangled, which was just not a good look. I had to start thinking about moving soon after, so I dropped that project.</p>

<p>Since then, I got the HTC Vive Trackers and attempted the whole setup process again. I couldn’t figure out how to get it working with Custom Avatar out of the box, and also by that time I had spent hours trying to get them configured and pairing properly on the computer. Through some more googling, I discovered a piece of software called <a href="https://liv.tv/">LIV</a>. While I was fully expecting to have to watch some more YouTube or jump on a Discord to figure out how to install the software, I was pleasantly surprised that you could just <a href="https://store.steampowered.com/app/755540/LIV/">download the software on Steam</a>. So that’s what I did.</p>

<p>The <a href="https://guide.liv.tv/What-is-LIV-and-what-do-we-do-a1a2ba8313454e28aca16c989283c334">guide</a> is fairly straightforward regarding setup. You have <a href="https://guide.liv.tv/Install-LIV-Driver-b7e903e78867413285e6692e9251350a">install the LIV driver</a> which provides more virtual trackers into SteamVR, and typically you also need to <a href="https://guide.liv.tv/Create-Avatar-Virtual-Camera-Profile-c09654baa68a45ffbbc8ad69460ccbd8">start the compositor</a> any time you want to stream. The compositor provides a nice window separate from the game window and separate from the Steam VR mirror display. For this reason I recommend disabling the Steam VR mirror display, and to reduce the resolution of the native game mirroring if you don’t want to stress out your video card.</p>

<p><img src="/images/2020-09-06-full-body-tracking/steam-vr-no-room.gif" alt="Disable Steam Room VR Mirroring" /></p>

<p>To pull up the LIV menu, you need to stare at and point at the LIV icon floating on the ground.</p>

<p><img src="/images/2020-09-06-full-body-tracking/liv-config-0.gif" alt="Pulling up LIV Config in Headset" /></p>

<p>Adding and configuring an avatar is very easy with LIV. I recommend for each new play session to re-calibrate your avatar to the trackers, since you’re likely putting on the trackers slightly differently each time, and otherwise it will distort your avatar to viewers.</p>

<p>If you don’t want to use the stock standard boring avatars, you should go look for <a href="https://bsaber.com/avatars/">some online</a>. Note that not all avatars support Full-Body Tracking, and actually if you are more savvy with 3D modeling software (e.g. Blender), there are a few guides out there, such as <a href="https://bs.assistant.moe/Avatars/">this one</a>. But if you’re not into that, I recommend adding <code class="language-plaintext highlighter-rouge">tag:FBT</code> and <code class="language-plaintext highlighter-rouge">tag:Full Body Tracking</code> to refine your search results. I am currently using <a href="https://modelsaber.com/Avatars/?id=1564625718&amp;pc">Sour Miku Black v2</a>.</p>

<p><img src="/images/2020-09-06-full-body-tracking/liv-calibrate-tpose.gif" alt="Calibrating T-Pose of LIV Avatar" /></p>

<p>If you are wanting to fiddle with the LIV camera settings I recommend using a gamepad or controller.</p>

<p><img src="/images/2020-09-06-full-body-tracking/liv-config-1.gif" alt="Configuring LIV Camera Part 1" /></p>

<p><img src="/images/2020-09-06-full-body-tracking/liv-config-2.gif" alt="Configuring LIV Camera Part 2" /></p>

<p><img src="/images/2020-09-06-full-body-tracking/liv-config-3.gif" alt="Configuring LIV Camera Part 3" /></p>

<p>When you’re done, you can close the LIV menu by staring and pointing at the ground again.</p>

<p><img src="/images/2020-09-06-full-body-tracking/liv-config-4.gif" alt="Dismissing the LIV Config in Headset" /></p>

<h2 id="streaming-with-streamlabs-obs">Streaming with StreamLabs OBS</h2>

<p>Now when it comes to connecting all the pieces together to actually stream your VR content, you’ll need to probably get an account on a streaming website such as <a href="https://twitch.tv">Twitch</a> or <a href="https://youtube.com">YouTube</a>. You’ll also need streaming software; I used to use <a href="https://obsproject.com/">OBS</a> but more recently I’ve switched to <a href="https://streamlabs.com/">StreamLabs</a> which has roots in OBS. If you connect your Twitch account to StreamLabs, it will set up the output stream more or less automatically; alternatively if you have a prior OBS profile it can import those settings. If all else fails, you can refer to the <a href="https://stream.twitch.tv/">Twitch Streaming Configuration Suggestions</a>.</p>

<p>Within OBS or StreamLabs, you have the opportunity to set up different Scenes; this more or less shows different kinds of views on the stream, and is commonly used with Transitions for cases where you have a custom Scene for intermissions, breaks, etc. from gameplay. I am not a serious streamer, so I just use it to organize different game streaming “profiles” depending on what kind of game I want to stream. Within a Scene you can configure a number of different Sources, that can range from captured window output to custom media to custom browser-based UI widgets.</p>

<p><img src="/images/2020-09-06-full-body-tracking/streamlabs.png" alt="Sample StreamLabs view" /></p>

<p>Speaking of widgets…</p>

<h3 id="setting-up-beat-saber-visual-stats-in-stream">Setting up Beat Saber Visual Stats in Stream</h3>

<p><img src="/images/2020-09-06-full-body-tracking/beatsaber-stats.gif" alt="Cropped visual of Beat Saber stats during play" /></p>

<p>This one is wholly optional and has nothing to do with Full-Body Tracking but I found it to be a really nice indicator on the stream of specifically the song and difficulty level that I was currently playing. It’s primarily powered by <a href="https://github.com/opl-/beatsaber-http-status">Beat Saber HTTP Status</a> which exposes Beat Saber stats on a local websocket, and then from there you can host a UI that listens periodically to that port. It’s specifically very nice for browser/HTTP-based UI widgets that can be added to your favorite capture software.</p>

<p>Interestingly I vaguely remembered this mod being available on ModAssistant, but I couldn’t find it. However it seems at the time of this writing, the most recent version of HTTP Status supports one patch version behind the current one, so I suspect it’s not shown for those reasons. So instead you can install it manually. If that doesn’t work out of the box, it’s fairly trivial to use <code class="language-plaintext highlighter-rouge">strings</code> and <code class="language-plaintext highlighter-rouge">xxd</code> to find the Beat Saber version hardcoded in the DLL, and change it slightly to support the most recent version.</p>

<p>On its own, this mod doesn’t do anything, so you’ll need to find something that reads from the websocket and provides an overlay. For that I just cloned <a href="https://github.com/Reselim/beat-saber-overlay">reselim’s Beat Saber Overlay</a>, and published it via github pages. I then created a browser UI widget and pointed it to the appropriate URL, which for me is https://worldwise001.github.io/beat-saber-overlay/.</p>

<p><img src="/images/2020-09-06-full-body-tracking/beatsaber-stats.png" alt="Screenshot of StreamLabs Widget Configuration for Beat Saber Stats" /></p>

<h2 id="final-thoughts">Final Thoughts</h2>

<p>Overall this project happened over several hours of research distributed over several days; originally I embarked on this quest in April, and it took time for (a) the Kinects to arrive for testing, (b) the Vive Trackers to arrive for testing, and (c) for me to move to a different country and set up VR in a better space. But I am fairly pleased with the final results, and so I hope this post will help connect the dots of some of the articles that were scattered around the internet, or at the very least provide some entertainment value documenting my different roadblocks along the way.</p>

<p>Good luck and happy VRing!</p>

<p><img src="/images/2020-09-06-full-body-tracking/miku.gif" alt="Motion Capture of Miku Waving" /></p>

<hr />

<h2 id="postscript-things-that-didnt-work">PostScript: Things that didn’t work</h2>

<p>The first thought I had was trying to figure out how to get a third-person camera view working with Beat Saber. I had seen some folks demonstrate that capability on YouTube in the past, so I knew it was possible, and I figured it was through modding of some kind since modding is fairly popular in that community.</p>

<h3 id="modassistant">ModAssistant</h3>

<p>There are a few different guides to aid first-time modders, including the <a href="https://bsmg.wiki/pc-modding.html">BSMG Wiki</a> and <a href="https://bsaber.com/installing-the-mod-guide-necessary-for-any-custom-songs/">Beast Saber</a>; I used some combination of these guides. While many claim that you can technically set everything up manually, what you probably want to use is a piece of software called <a href="https://github.com/Assistant/ModAssistant">ModAssistant</a>. I was remarkably surprised at how easy this was to set up. You download the <a href="https://github.com/Assistant/ModAssistant/releases">latest release</a>, and simply run the executable. It doesn’t actually install anything, so keep that executable aside from your Downloads folder if you don’t want to lose it.</p>

<p><img src="/images/2020-09-06-full-body-tracking/modassistant.png" alt="ModAssistant Startup Screen" /></p>

<p>I love some of the user-friendliness of this windows. Note the Game Version and the ModAssistant version tracked in the lower left. You have to click “I Agree”, which will install the basic scaffolding (probably by overwriting certain DLLs/binaries). If nothing seems to be happening, you can click on the “Mods” icon on the left hand side. That brings up something similar to the following window:</p>

<p><img src="/images/2020-09-06-full-body-tracking/modassistant1.png" alt="ModAssistant Mods Screen" /></p>

<p>Perhaps non-intuitively, you want to click “Install or Update” which will install all the mods indicated by the Checked Boxes. It should then show the green installed version numbers. As you can tell there are some core libraries that are generally required for all the other mods; digging into the source code it seems like it provides a bunch of useful convenience methods for modders.</p>

<p>An important caveat is that the Beat Saber releases change semi-frequently, and this seems to be a frequent source of breakage. I don’t remember the last time I had to handle this, but if you are fine with wiping away your mods and starting fresh, you can do the following:</p>

<ul>
  <li>Open your Steam library, and uninstall Beat Saber. Note that this will not remove the modded content.</li>
  <li>Navigate to your Beat Saber install directory and nuke the contents. This is usually <code class="language-plaintext highlighter-rouge">&lt;your steam install folder&gt;/steamapps/common/Beat Saber</code>.</li>
  <li>Open up Steam again and isntall Beat Saber.</li>
  <li>Repeat the above steps to reinstall mods.</li>
</ul>

<h3 id="setting-up-cameraplus">Setting up CameraPlus</h3>

<p>CameraPlus is a Beat Saber mod to configure custom cameras (specifically for popular third-person cameras) to view the player in Beat Saber. It commonly is also used in tandem with Custom Avatars. Setting up CameraPlus is only partially intuitive. It’s easy to install through <a href="https://github.com/Assistant/ModAssistant">ModAssistant</a>, as shown in the screenshot above, but it does take some finagling to get the view working as you intended. Some of the gotchas I ran into include:</p>

<ul>
  <li>The currently maintained source for CameraPlus is by <a href="https://github.com/Snow1226/CameraPlus">Snow1226</a> not <a href="https://github.com/xyonico/CameraPlus">xyonico</a> (the original author). Digging into commit history seems to show a change in maintainership a few times, but it’s good to see that this is still actively being maintained for bugfixes and features.</li>
  <li>CameraPlus uses the existing Desktop window screen to render its view. It turns out if you enable Smooth Camera, that overwrites the CameraPlus settings so you’re stuck with still a first-person view. However infuriatingly, you don’t notice until you record yourself in-game, since in the menu screens, CameraPlus appears to work perfectly.</li>
  <li>Configuring the camera involves <a href="https://www.youtube.com/watch?v=RpYoMiKJygQ">watching a YouTube video</a>. If you are discovering CameraPlus through ModAssistant and bad Google SEO, you will not easily find any of these things.</li>
</ul>

<p>Once you have CameraPlus install, the way to configure it is from the Desktop Window of Beat Saber, not from within the VR headset. If you right-click on the window, the CameraPlus menu interface pops up and you can do things such as add/remove cameras, and switch between First-Person and Third-Person view.</p>

<p><img src="/images/2020-09-06-full-body-tracking/cameraplus-menu.gif" alt="CameraPlus Menu and Changing Perspectives" /></p>

<p>If you’re not happy with the Third-Person camera view, you can put on the VR headset and move it around with your pointer. Hold the trigger to grab it, let go of the trigger to let it go.</p>

<p><img src="/images/2020-09-06-full-body-tracking/cameraplus-camera-adjust.gif" alt="CameraPlus Adjusting the Camera" /></p>

<p>At the end of the day, I didn’t end up using CameraPlus for my streaming since I found LIV to be slightly easier to set up. But more on that later.</p>

<h3 id="setting-up-custom-avatars">Setting up Custom Avatars</h3>

<p>The <a href="https://github.com/nicoco007/BeatSaberCustomAvatars">Beat Saber Custom Avatars</a> mod is intended to pair with <a href="https://github.com/Snow1226/CameraPlus">CameraPlus</a> to provide a representation of your person holding the sabers in-game. Unfortunately for some reason this isn’t installed through <a href="https://github.com/Assistant/ModAssistant">ModAssistant</a>, so you have to follow the instructions yourself. Note you probably don’t need to manually install <a href="https://github.com/nicoco007/DynamicOpenVR/releases">DynamicOpenVR</a>, since that is provided by ModAssistant. However you do need to download and install (read: extract the contents) of <a href="https://github.com/nicoco007/BeatSaberCustomAvatars/releases">Custom Avatars</a> in your Beat Saber install directory, i.e. usually <code class="language-plaintext highlighter-rouge">&lt;your steam install folder&gt;/steamapps/common/Beat Saber</code>.</p>

<p>At the time of this writing, there’s actually <a href="https://github.com/nicoco007/BeatSaberCustomAvatars/issues/58#issuecomment-676755378">a bug</a> with Custom Avatars, so check the thread and download the pre-release rc2 version to see if it fixes the bug for you (it did for me).</p>

<p>Once the files are in, the left screen upon startup now has a Mods Tab with an Avatars section you can configure. From there you can choose the appropriate avatar you want to “wear”. Note that the menu show Full-Body Tracking support, however the legs move really weird when we pick a Full-Body Avatar. That’s because this is only using trackers on the headset and the controllers aka hands, and so the avatar “guesses” where your feet might want to be and move them appropriately.</p>

<p><img src="/images/2020-09-06-full-body-tracking/beatsaber-custom-avatar-config.gif" alt="Configuring Beat Saber Custom Avatar" /></p>

<p>There’s one problem with using the Template Avatar and that’s this:</p>

<p><img src="/images/2020-09-06-full-body-tracking/beatsaber-boring.gif" alt="Beat Saber playthrough using Template Avatar" /></p>

<h3 id="emulating-vive-trackers-with-kinect">Emulating Vive Trackers with Kinect</h3>

<p><em>Note: this does not use LIV, and sets up virtual vive trackers using a Kinect. It probably is not what you want!</em></p>

<p>TBD, but it’s gnarly.</p>]]></content><author><name>@shh</name></author><category term="guide" /><category term="long" /><category term="tech" /><category term="vr" /><category term="gaming" /><summary type="html"><![CDATA[I've been a huge fan of VR ever since we saw some of the first few consumer products hit the market in around 2015/2016, and so back then I got myself a HTC Vive headset and controllers and upgraded aspects of my gaming PC to accommodate the more strenuous requirements at the time.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.shh.sh/images/2020-09-06-full-body-tracking/preview.gif" /><media:content medium="image" url="https://www.shh.sh/images/2020-09-06-full-body-tracking/preview.gif" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">The Curious Case of Python, Catalina, Snowflake, and Abort Trap 6</title><link href="https://www.shh.sh/2020/01/04/python-abort-trap-6.html" rel="alternate" type="text/html" title="The Curious Case of Python, Catalina, Snowflake, and Abort Trap 6" /><published>2020-01-04T00:00:00+00:00</published><updated>2020-01-04T00:00:00+00:00</updated><id>https://www.shh.sh/2020/01/04/python-abort-trap-6</id><content type="html" xml:base="https://www.shh.sh/2020/01/04/python-abort-trap-6.html"><![CDATA[<p><img src="/images/2020-01-04-python-abort-trap-6/abort-trap-6.png" alt="Python Quit Unexpectedly Dialog - Details" /></p>

<p>Some time ago I had upgraded my work laptop from Mojave to Catalina. The result of that, as a lot of you probably know, is that due to the fact that it <a href="https://en.wikipedia.org/wiki/MacOS_Catalina">now only supports 64-bit applicatons</a> this meant a lot of apps/libraries stopped working, complaining of mismatched dylibs. As a veteran Linux user, I had seen something similar many many times with mismatched .so’s, and knew the standard approach was to just reinstall everything, because with an OS version upgrade, the standard Mac OS libraries probably changed versions/ABIs causing linkage errors/other inconsistencies. Typically that meant just doing <code class="language-plaintext highlighter-rouge">brew upgrade</code> and stepping out for a coffee.</p>

<h3 id="python-still-complains">Python still complains</h3>

<p>A few python things remained persistently problematic, which is why I had these scripts handy ready to blow out various parts of the environment:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#!/bin/bash

pushd ~/Library/Caches/pip &amp;&amp; rm -rf http wheels &amp;&amp; popd
pushd /usr/local/lib/python3.7/site-packages &amp;&amp; rm -rf `find . -name __pycache__` &amp;&amp; popd
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#!/bin/sh

rm -rf dist/ *.egg-info/ build/ venv/ .pytest_cache/ .mypy_cache/
rm -rf `find . -name __pycache__`

python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
</code></pre></div></div>

<p>Despite all that, I still remained flummoxed by the fact that one service I was working on locally that was using <a href="https://github.com/snowflakedb/snowflake-connector-python">snowflake-connector-python</a> was still quitting unexpectedly with <code class="language-plaintext highlighter-rouge">Abort trap: 6</code>.</p>

<h3 id="what-is-abort-trap-6">What is <code class="language-plaintext highlighter-rouge">Abort trap: 6</code>?</h3>

<p>Mac uses <a href="https://en.wikipedia.org/wiki/Trap_%28computing%29">this definition</a> of trap; this message appears when the <code class="language-plaintext highlighter-rouge">abort()</code> function in the C standard library is called. It is akin to <a href="https://en.wikipedia.org/wiki/Signal_%28IPC%29">SIGABRT</a>. There are <a href="https://en.wikipedia.org/wiki/Abort_%28computing%29">various reasons</a> to do an <code class="language-plaintext highlighter-rouge">abort()</code> over doing a standard application shutdown, but typically it’s more controlled than a segfault, but deeper into library/system internals and thus out of control of the developer. An <code class="language-plaintext highlighter-rouge">abort()</code> in the Linux kernel for instance will cause a kernel panic.</p>

<h3 id="getting-detailed-information">Getting Detailed Information</h3>

<p>Weirdly enough, when <code class="language-plaintext highlighter-rouge">abort()</code> is called in an application in Mac OS X, a very helpful dialog titled “$program quit unexpectedly” pops up, and it’s only through there that you can see detailed error messages, despite the fact that the application may have been called on the command line.</p>

<p><img src="/images/2020-01-04-python-abort-trap-6/python-quit-unexpectedly.png" alt="Python Quit Unexpectedly Dialog" /></p>

<p>Either way, clicking on “Report” allowed me to dig in more into the messages to see what was the problem.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Crashed Thread:        2

Exception Type:        EXC_CRASH (SIGABRT)
Exception Codes:       0x0000000000000000, 0x0000000000000000
Exception Note:        EXC_CORPSE_NOTIFY

Application Specific Information:
/usr/lib/libcrypto.dylib
abort() called
Invalid dylib load. Clients should not load the unversioned libcrypto dylib as it does not have a stable ABI.
</code></pre></div></div>

<p>Huh. That’s odd.</p>

<h3 id="initial-googling">Initial Googling</h3>

<p>It turns out that I wasn’t the only one encountering this error; <a href="https://forums.developer.apple.com/thread/119429">many other folks</a> had encountered this, and despite being shown the <a href="https://forums.developer.apple.com/thread/119429#380112">proper fix</a>, some folks had posted a <a href="https://forums.developer.apple.com/thread/119429#386843">workaround</a> that consisted of overwriting the library with a symlink to a pinned version. I decided that ultimately that was pretty tacky and not a good long-term solution, since inevitably there was no consistent version to link to, folks would eventually run into the problem again.</p>

<p>I went to go report this as a bug to snowflake, but discovered, in fact, someone had <a href="https://github.com/snowflakedb/snowflake-connector-python/issues/235">already beat me to it</a>:</p>

<p><img src="/images/2020-01-04-python-abort-trap-6/github-issue.png" alt="Screenshot of snowflake connector issue on Github" /></p>

<p>Someone traced the potential cause to something in the oscrypto library <a href="https://github.com/snowflakedb/snowflake-connector-python/issues/235#issuecomment-554058505">loading openssl</a>, causing it to crash, but that’s where my trail ended, since no one could figure out a workaround to that, especially since oscrypto was created as a <a href="https://github.com/wbond/asn1crypto/blob/master/changelog.md#100">split from asn1crypto</a>.</p>

<p>Since by this time I had realized that there was no easy good fix, and that this was actively blocking my work, I decided that I might as well investigate how to fix this once and for all.</p>

<h3 id="tracing-the-source-of-the-bug">Tracing the source of the bug</h3>

<p>The first step to fixing was of course, to identify the offending line of code in the dependency libraries that would trigger it. I use <a href="https://www.jetbrains.com/pycharm/">PyCharm </a> as my development IDE at work, and some of its great features include both a way to click through to see original code definitions, and a really powerful graphical <a href="https://en.wikipedia.org/wiki/Debugger">debugger</a> that is very similar in usability to <a href="https://www.jetbrains.com/idea/">IntelliJ</a>’s debugger.</p>

<p>However the debugger is not useful if I can’t figure out where to set possible breakpoints.</p>

<p>I tried tracing through from <code class="language-plaintext highlighter-rouge">snowflake.connector.connect()</code> but soon got bogged down since it runs through the requests library as well. I took a step away for lunch/coffee and when I came back, I realized based on the hint above that it was probably a problem in <code class="language-plaintext highlighter-rouge">oscrypto</code>, I looked for where <code class="language-plaintext highlighter-rouge">oscrypto</code> was referenced in the snowflake library and added two breakpoints; one where it was imported, and one where the function was being used.</p>

<p><a href="https://github.com/snowflakedb/snowflake-connector-python/blob/f2f3f998f04666ed5e3bf2e0067856c459cc47c4/ocsp_asn1crypto.py#L20"><img src="/images/2020-01-04-python-abort-trap-6/breakpoint-1.png" alt="First breakpoint" /></a></p>

<p><a href="https://github.com/snowflakedb/snowflake-connector-python/blob/f2f3f998f04666ed5e3bf2e0067856c459cc47c4/ocsp_asn1crypto.py#L317"><img src="/images/2020-01-04-python-abort-trap-6/breakpoint-2.png" alt="Second breakpoint" /></a></p>

<p>Cool, let’s try running the debugger and see if dig further.</p>

<p><a href="/images/2020-01-04-python-abort-trap-6/early-crash.mov"><img src="/images/2020-01-04-python-abort-trap-6/early-crash.gif" alt="GIF/Video of crash after clicking through one breakpoint" /></a></p>

<p>Woah that’s weird! It crashed on the import?</p>

<h3 id="digging-into-the-import">Digging into the import</h3>

<p>After verifying that it was that one import line (by adding a third breakpoint and seeing if the next import succeeded at all), I decided to dig in further to see how that import was causing problems.</p>

<p><a href="/images/2020-01-04-python-abort-trap-6/add-breakpoints.mov"><img src="/images/2020-01-04-python-abort-trap-6/add-breakpoints.gif" alt="GIF/Video of setting breakpoints" /></a></p>

<p>I then stumbled upon how the libcrypto libraries were being determined on Mac, and how they were being loaded, in a file called <a href="https://github.com/wbond/oscrypto/blob/a9f577499244e8c8645065be0f495de77447f0cd/oscrypto/_openssl/_libcrypto_cffi.py#L25"><code class="language-plaintext highlighter-rouge">_libcrypto_cffi.py</code></a>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>libcrypto_path = _backend_config().get('libcrypto_path')
if libcrypto_path is None:
    libcrypto_path = find_library('crypto')
if not libcrypto_path:
    raise LibraryNotFoundError('The library libcrypto could not be found')

try:
    vffi = FFI()
    vffi.cdef("const char *SSLeay_version(int type);")
    version_string = vffi.string(vffi.dlopen(libcrypto_path).SSLeay_version(0)).decode('utf-8')
except (AttributeError):
    vffi = FFI()
    vffi.cdef("const char *OpenSSL_version(int type);")
    version_string = vffi.string(vffi.dlopen(libcrypto_path).OpenSSL_version(0)).decode('utf-8')

is_libressl = 'LibreSSL' in version_string
</code></pre></div></div>

<p>Aha! This might be where we’re running into problems! Let’s try the debugger and see if we can isolate the crash:</p>

<p><a href="/images/2020-01-04-python-abort-trap-6/late-crash.mov"><img src="/images/2020-01-04-python-abort-trap-6/late-crash.gif" alt="GIF/Video of isolating the crash" /></a></p>

<p>Bingo! We isolated the location of the problem!</p>

<h3 id="filing-and-fixing-the-bug">Filing and fixing the bug</h3>

<p><img src="/images/2020-01-04-python-abort-trap-6/github-issue-2.png" alt="Screenshot of oscrypto issue on Github" /></p>

<p>Now that I found where the issue was, I decided to <a href="https://github.com/wbond/oscrypto/issues/35">file an issue</a> with the <a href="https://github.com/wbond/oscrypto">oscrypto</a> project on github. But filing the bug probably was not going to mean it was going to get fixed overnight, so I decided I better investigate to see if I can find make a fix.</p>

<p>Since this seemed to be a Catalina-specific issue, and Apple recommended that we pin to specific versions of the libcrypto dylibs, it seemed like the obvious fix would be to do just that. So I proposed the <a href="https://github.com/wbond/oscrypto/pull/36/files">following change</a>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># if we are on catalina, we want to strongly version libcrypto since unversioned libcrypto has a non-stable ABI
if sys.platform == 'darwin' and platform.mac_ver()[0].startswith('10.15') and \
        libcrypto_path.endswith('libcrypto.dylib'):
    # libcrypto.42.dylib is in libressl-2.6 which as a OpenSSL 1.0.1-compatible API
    libcrypto_path = libcrypto_path.replace('libcrypto.dylib', 'libcrypto.42.dylib')
</code></pre></div></div>

<p>and</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># if we are on catalina, we want to strongly version libssl since unversioned libcrypto has a non-stable ABI
if sys.platform == 'darwin' and platform.mac_ver()[0].startswith('10.15') and libssl_path.endswith('libssl.dylib'):
    # libssl.44.dylib is in libressl-2.6 which as a OpenSSL 1.0.1-compatible API
    libssl_path = libssl_path.replace('libssl.dylib', 'libssl.44.dylib')
</code></pre></div></div>

<p>Why <code class="language-plaintext highlighter-rouge">42</code> for crypto vs <code class="language-plaintext highlighter-rouge">44</code> for ssl? As per <a href="https://github.com/wbond/oscrypto/issues/35#issuecomment-556870568">this discussion</a>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ for i in `ls libcrypto.*.dylib`; do echo -n $i:; strings $i | grep libressl | head -n1 | cut -d'/' -f9; echo; done
...
libcrypto.42.dylib:libressl-2.6
...
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ for i in `ls libssl.*.dylib`; do echo -n $i:; strings $i | grep libressl | head -n1 | cut -d'/' -f9; echo; done
...
libssl.44.dylib:libressl-2.6
...
</code></pre></div></div>

<p>We didn’t have a problem with <code class="language-plaintext highlighter-rouge">libssl</code> yet, but better safe than sorry!</p>

<h3 id="testing-the-fix">Testing the fix</h3>

<p>This was a little bit tricky, due to the fact that this was in a library pulled as a dependency. So to test this, I cloned a copy of the repo down, added my fix, and installed from that local copy by installing it as an egg:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip install file:///Users/shh/Development/github/oscrypto#egg=oscrypto
</code></pre></div></div>

<p>Running the debugger again in fact confirms that the fix works!</p>

<h3 id="merge-and-release">Merge and release</h3>

<p>As of this writing, I am happy to say that the fix was merged shortly after I <a href="https://github.com/wbond/oscrypto/pull/36">made the PR</a>, and <a href="https://github.com/wbond/oscrypto/issues/35#issuecomment-570789336">released in v1.1.1</a> of oscrypto! Future users should no longer be running into this problem :).</p>

<p>Thank you for reading!</p>]]></content><author><name>@shh</name></author><category term="tech" /><category term="long" /><category term="python" /><category term="debug" /><category term="mac" /><summary type="html"><![CDATA[Some time ago I had upgraded my work laptop from Mojave to Catalina. The result of that, as a lot of you probably know, is that due to the fact that it now only supports 64-bit applicatons this meant a lot of apps/libraries stopped working, complaining of mismatched dylibs.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.shh.sh/images/2020-01-04-python-abort-trap-6/abort-trap-6.png" /><media:content medium="image" url="https://www.shh.sh/images/2020-01-04-python-abort-trap-6/abort-trap-6.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Starting a career in tech</title><link href="https://www.shh.sh/2019/12/14/career-tech-start.html" rel="alternate" type="text/html" title="Starting a career in tech" /><published>2019-12-14T00:00:00+00:00</published><updated>2019-12-14T00:00:00+00:00</updated><id>https://www.shh.sh/2019/12/14/career-tech-start</id><content type="html" xml:base="https://www.shh.sh/2019/12/14/career-tech-start.html"><![CDATA[<p>I’ve given this sort of pep talk to a bunch of newly joined womeng at Square, to the point where I’ve just decided to sit down and write all that stuff in one document. I’ll add a disclaimer that this is very engineering-focused, and may not apply to all other companies, but I think whether or not some of these things apply is indicative of the maturity level of HR/promotion processes at a company. With that said, let’s dive in.</p>

<p>The typical (but not only) background is for folks to come straight from an undergraduate program. Often this is their first full-time job, or first full-time job in tech, so it is critical to set expectations and guidance from the very start:</p>

<h3 id="know-your-current-level">Know your current level</h3>

<p>What level were you hired at? This should be easy to figure out through your HR portal. Sometimes this level is public to the whole company, sometimes it’s not. There is nothing preventing you from discussing your level with others. Your level defines your salary band and official title, which may or may not be also present in said HR portal.</p>

<p>Knowing where you are in your salary band is a generally good indicator of what the company initially expects of you. Lower in the band probably means you’re early in the level; mid-point means you’re performing on average; high in the band means they probably expect you to level up soon.</p>

<p>Titles are superfluous within a company, but are important for gaining a different job outside of the company. Titles may also be affected with ladders/tiers, and so if there is a slight difference between your title and your coworker’s title, you are likely on different ladders. Asking HR what these mean is always a good idea.</p>

<h3 id="find-your-career-ladder-document">Find your career ladder document</h3>

<p>A mature company should have career ladder documents and expectations for each level clearly posted, e.g. like <a href="https://developer.squareup.com/blog/squares-growth-framework-for-engineers-and-engineering-managers/">Square</a> <a href="https://docs.google.com/spreadsheets/d/12h50IYqd7fsO7tJ0l1OuHYbz5vN2d24a8EIDFhu2AZQ/edit">has</a>. Once you know your level, it’s important to take a look at what the expectations are at your <em>current</em> level, and then what the expectations are at your <em>next</em> level. Many tech companies declare that they will promote you once you have demonstrated that you are performing at the next level. The way to meet those next level expectations is to then seek out opportunities that will allow you to demonstrate those skills.</p>

<h3 id="promotions-arent-automatic">Promotions aren’t automatic</h3>

<p>I think the most intimidating thing for folks who’ve never gone through a promotion process before, is figuring out when the right time to ask for a promotion is. If you are conflict-averse but a hard worker, you may happen to have a really good manager who just files a promotion for you with very little work on your part. Unfortunately this may make it harder to figure out when the right time for a promotion is later on!</p>

<p>Unsurprisingly, different companies will have different paths for the employee who seeks a promotion. Some require the employee to put together the packet and present it to the manager who will forward it on. Others require the manager to put together the packet and forward it on. Yet even others might be extremely informal about the process. In all these cases, there’s generally a review panel, whether org-specific or company-wide, that will discuss review all the packets and approve who gets a promotion.</p>

<p>There are multiple reasons why, in a steady-state, you may not “automatically” get a promotion. Your manager may be overworked, distracted, new, dealing with other situations and is simply forgetting about how promotions are important. It may be your manager hasn’t seen enough evidence from you and doesn’t feel confident about your case. It may be there are other more critically needed promotion packets that demand their attention.</p>

<p>In any of these cases, even if your manager is fully supportive of your case for promotion, you should assume that your manager doesn’t have 100% visibility to your work. Doing some upfront work whether it be familiarizing yourself with the promotion process, <a href="https://developer.squareup.com/blog/you-are-your-own-best-hype-person">making a hype doc</a>, or otherwise documenting clear evidence that supports traits for level+1, will all make your manager extremely grateful during the promo period.</p>

<h3 id="speaking-about-visibility">Speaking about visibility…</h3>

<p>I mentioned that there may be multiple reasons why your manager doesn’t feel they have seen enough evidence. A large chunk of that is visibility of not just accomplishments, but also problems of the employee. A good manager wants to know about both wins and struggles from the employee so that they can figure out how to make the situation better/provide advice.</p>

<p>One of the analogies I’ve made to several managers about their work is that they’re effectively playing a Real-Time Strategy game, e.g. say Starcraft. Their day-to-day is to figure out where the logistical and management problems are, solve those, to ensure they have efficient output. They are less concerned about the fact that they are out of Vespene gas, and more concerned about why they are out of it, what is the rate of Vespene gas usage, why was it drained suddenly, and how can they ensure that rate of use is consistent so they don’t run out again. In a game it’s easy to get visibility because lots of things show rates and metrics; in the real world, they have to rely on their reports being effective communicators of potential problems before they become actual problems.</p>

<p>I should add also that whenever I’ve made this analogy, a lot of managers chuckle and strongly agree.</p>

<h3 id="speaking-about-problems">Speaking about problems…</h3>

<p>Complaining about potential problems is okay; understanding, dissecting, and explaining how problems are affecting your work/timeline/time estimates is even better. The technique to becoming a senior engineer is not just solving harder/more complex problems; it’s being able to understand tradeoffs, potential risks to the project, and outlining possible solution avenues, ahead of time of roadblocks. This is something that only comes with practice and diligence. Coupled with effective communication, and now your manager feels confident enough to defer to you and be hands-off because you have control over the situation.</p>

<h3 id="becoming-senior-is-becoming-your-own-manager">Becoming senior is becoming your own manager</h3>

<p>There’s a reason why senior engineers often transition to full-time management, and it’s because of the assumption that they have been already doing this really well for their projects. (Whether they are actually ready to go into management is another question for another day). There is the implicit expectation that senior engineers are also good at project management, and this is often recorded as explicit line items in leveling documents, such as leading a team, facilitating discussion, estimating risk, being able to break down a large task into smaller tasks, etc. You will likely not succeed in a senior+ capacity if you do not take on rudimentary project management skills.</p>

<h3 id="write-things-down">Write things down</h3>

<p>It is hard to do things like, promotion evidence, project planning, root-cause analysis, trade-off analysis, etc. if things aren’t written down. The higher you go, the more complex problems become, and the easier it becomes to forget decisions made in Slack or verbally. Becoming an effective worker in tech really involves becoming an effective communicator, at the very least in a written medium. (There’s also a good reason to become an effective oral communicator, but I argue doing it in written form should be done first…. even though I’m personally not actually good at this). When things are written down, you spend less time rehashing decisions, and less time spinning wheels, and more time actually executing.</p>

<h3 id="starting-out">Starting out</h3>

<p>It may seem extremely daunting to figure out how to get to senior+ when you have just graduated from school. Start simple and easy; comment on design documents and code review by asking questions (asking questions is a good exercise in forces the author to re-explain, and if you can’t understand what they’re writing, you’re probably not the only one); question assumptions, especially to dig out historical decisions, thinking, or libraries; trust your manager/team when they give you a larger-ish project, and remember that executing a project is never a singular effort, but a team one, so leverage your team. Finally feel free to propose crazy ideas; sometimes people haven’t literally thought about that option, because the older you get, the crustier you get.</p>

<p>Once you get into the flow of things, I guarantee things will start falling into place.</p>

<h3 id="final-thoughts">Final thoughts</h3>

<p>Just about everyone feels terrified in their first year at a workplace. This is a normal feeling. It will go away as you build confidence. Take note of other new hires that are senior, and watch as they make the same mistakes as you. The best senior hires are those who are honest upfront about how they don’t know everything, but are willing to learn with you and listen to you.</p>

<p>Good luck!!! and onward!!!</p>]]></content><author><name>@shh</name></author><summary type="html"><![CDATA[I've given this sort of pep talk to a bunch of newly joined womeng at Square, to the point where I've just decided to sit down and write all that stuff in one document. I'll add a disclaimer that this is very engineering-focused, and may not apply to all other companies, but I think whether or not some of these things apply is indicative of the maturity level of HR/promotion processes at a company. With that said, let's dive in.]]></summary></entry><entry><title type="html">Security/Privacy/Tech resources</title><link href="https://www.shh.sh/2019/09/30/introsec.html" rel="alternate" type="text/html" title="Security/Privacy/Tech resources" /><published>2019-09-30T00:00:00+00:00</published><updated>2019-09-30T00:00:00+00:00</updated><id>https://www.shh.sh/2019/09/30/introsec</id><content type="html" xml:base="https://www.shh.sh/2019/09/30/introsec.html"><![CDATA[<p>At this point, I’ve read or watched or listened to a bunch of these, and generally these are all work by great people done in an engaging fashion. Unlike other practitioners, I tend to veer towards more hardware/systems/infra type problems instead of web/frontend problems, so if you’re looking for stuff on the latter, sadly you’re less likely to find it here. I do hope though that some of these do end up interesting to you even if it’s not in your direct field of interest, and hopefully you’ll learn something new! If you think there deserves to be a special mention here or there, or if you find a milkshake duck that I’ve not caught, please feel free to send me an email or DM me on twitter (@worldwise001) and I’ll do my best to respond as soon as I can.</p>

<p>Resources:</p>
<ul>
  <li>CTFs:
    <ul>
      <li><a href="https://cryptopals.org">cryptopals.org</a> - Cryptography CTF, you don’t need a math background</li>
      <li><a href="https://microcorruption.com">microcorruption.com</a> - Embedded Security CTF</li>
      <li><a href="https://squarectf.com">squarectf.com</a> - our yearly small CTF competition</li>
    </ul>
  </li>
  <li>Conferences (don’t attend, just look for the talks on youtube, or read papers):
    <ul>
      <li>Industry:
        <ul>
          <li><a href="https://bsidessf.org">BSidesSF</a></li>
          <li><a href="https://www.usenix.org/conferences/byname/1046">USENIX PEPR - Privacy Engineering, Practice and Respect</a></li>
          <li><a href="https://www.usenix.org/enigma">USENIX Enigma</a></li>
          <li><a href="https://www.usenix.org/srecon">USENIX SRECon</a></li>
          <li><a href="https://www.thestrangeloop.com/">StrangeLoop</a></li>
          <li><a href="https://defcon.org">DefCon</a></li>
          <li><a href="https://blackhat.com">Black Hat</a></li>
        </ul>
      </li>
      <li>Academic:
        <ul>
          <li><a href="https://www.ieee-security.org/TC/SP2022/past.html">IEEE S&amp;P</a></li>
          <li><a href="https://www.usenix.org/conferences/byname/108">USENIX Security</a></li>
          <li><a href="https://www.usenix.org/conference/soups2021">SOUPS</a></li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Podcasts or mailing lists
    <ul>
      <li><a href="https://tldrsec.com/">tl;dr sec</a></li>
      <li><a href="https://securitycryptographywhatever.buzzsprout.com/">Security, Cryptography, Whatever</a></li>
      <li><a href="https://oxide.computer/podcasts">On The Metal</a></li>
    </ul>
  </li>
  <li>Youtubers
    <ul>
      <li><a href="https://www.curiousmarc.com/">Curious Marc</a></li>
      <li><a href="https://www.youtube.com/c/lockpickinglawyer">Lockpicking Lawyer</a></li>
      <li><a href="https://eater.net/8bit/">Ben Eater - build an 8-bit computer from scratch</a></li>
      <li><a href="https://www.3blue1brown.com/">3blue1brown - animated math</a></li>
      <li><a href="https://www.youtube.com/playlist?list=PLuYLhuXt4HrSNZYhP9QRN9rwv4z9nKPba">jan misali video essays</a></li>
    </ul>
  </li>
  <li>People:
    <ul>
      <li>Twitter has a lot of security/privacy professionals on it
        <ul>
          <li>(I have a list probably, it needs to be organized)</li>
        </ul>
      </li>
      <li>James Mickens deserves his own section:
        <ul>
          <li><a href="https://www.usenix.org/system/files/1401_08-12_mickens.pdf">This World of Ours</a> - article</li>
          <li><a href="https://vimeo.com/135347162">Not Even Close: The State of Computer Security (with slides)</a> - video</li>
          <li><a href="https://www.usenix.org/conference/usenixsecurity18/presentation/mickens">Q: Why Do Keynote Speakers Keep Suggesting That Improving Security Is Possible?
 A: Because Keynote Speakers Make Bad Life Decisions and Are Poor Role Models</a> - video</li>
        </ul>
      </li>
      <li>Speaking of USENIX, I really enjoyed Alex Stamos’ keynote in 2019:
        <ul>
          <li><a href="https://www.usenix.org/conference/usenixsecurity19/presentation/stamos">Tackling the Trust and Safety Crisis</a></li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Wobsites:
    <ul>
      <li><a href="https://www.reddit.com/r/netsec/wiki/start">/r/netsec wiki</a></li>
      <li><a href="https://www.alchemistowl.org/pocorgtfo/">PoC GTFO</a> has a small collection of newsletters that do explain pretty well how to make and break things</li>
      <li><a href="http://www-inst.eecs.berkeley.edu/~cs161/fa08/papers/stack_smashing.pdf">Smashing The Stack For Fun And Profit</a> Classic buffer overflow howto.</li>
    </ul>
  </li>
  <li>Selected papers I enjoyed or think are foundational:
    <ul>
      <li>(there are more, I haven’t collected or organized them yet)</li>
      <li><a href="https://crysp.uwaterloo.ca/courses/pet/S13/readinglist.html">CrySP CS858 old paper reading list</a></li>
      <li><a href="https://crysp.uwaterloo.ca/courses/pet/S13/cache/solove.pdf">“I’ve got nothing to hide” and other misunderstandings of privacy</a></li>
    </ul>
  </li>
</ul>]]></content><author><name>@shh</name></author><category term="links" /><category term="tech" /><category term="security" /><category term="privacy" /><summary type="html"><![CDATA[I frequently get asked for things to read/watch/listen to. Check out this list, maybe you'll learn something.]]></summary></entry><entry><title type="html">Using your YubiKey to enable secure software development</title><link href="https://www.shh.sh/2019/07/26/yubikey.html" rel="alternate" type="text/html" title="Using your YubiKey to enable secure software development" /><published>2019-07-26T00:00:00+00:00</published><updated>2019-07-26T00:00:00+00:00</updated><id>https://www.shh.sh/2019/07/26/yubikey</id><content type="html" xml:base="https://www.shh.sh/2019/07/26/yubikey.html"><![CDATA[<p>YubiKeys are great. They’re a good <a href="https://en.wikipedia.org/wiki/Multi-factor_authentication">2FA</a> mechanism, support <a href="https://en.wikipedia.org/wiki/Time-based_One-time_Password_algorithm">TOTP</a>, and can be used as a rudimentary <a href="https://en.wikipedia.org/wiki/Smart_card">smartcard</a>. They’re also about $40-50.</p>

<p>This guide is to help you set up a YubiKey to sign git commits and also SSH into servers using key-based auth. It’s intended for software developers who want marginally more security and peace of mind while doing development.</p>

<p>If you want a more advanced/detailed howto, with references, checkout <a href="https://github.com/drduh/YubiKey-Guide">this guide instead</a> (not mine).</p>

<h3 id="prerequisites">Prerequisites</h3>

<p>I assume you know the following:</p>
<ul>
  <li>vaguely what PGP/GPG is, and some understanding of its web of trust model</li>
  <li>what a yubikey is</li>
  <li>what SSH keys are w.r.t. client authentication</li>
</ul>

<p>I assume you have the following:</p>
<ul>
  <li>a YubiKey 4 or better, that has never been configured with GPG</li>
  <li>a computer with a newish version of GPG 2</li>
  <li>servers that still allow RSA keys (some folks already are into ED25519 keys only)</li>
  <li>an existing GPG secring, ideally with a 2048 or 4096-bit RSA private key</li>
  <li>the command <code class="language-plaintext highlighter-rouge">ykman</code> (on mac, you can use homebrew and <code class="language-plaintext highlighter-rouge">brew install ykman</code>)</li>
</ul>

<h3 id="resetting-gpg-on-the-yubikey">Resetting GPG on the YubiKey</h3>

<p><a href="https://support.yubico.com/support/solutions/articles/15000006421-resetting-the-openpgp-applet-on-the-yubikey">(source 1)</a> <a href="https://developers.yubico.com/PGP/Card_edit.html">(source 2)</a></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ ykman openpgp reset
WARNING! This will delete all stored OpenPGP keys and data and restore factory settings? [y/N]: y
Resetting OpenPGP data, don't remove your YubiKey...
Success! All data has been cleared and default PINs are set.
PIN:         123456
Reset code:  NOT SET
Admin PIN:   12345678
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ gpg --card-edit

Reader ...........: Yubico Yubikey 4 OTP U2F CCID
Application ID ...:
Version ..........: 2.1
Manufacturer .....: Yubico
Serial number ....:
Name of cardholder: [not set]
Language prefs ...: [not set]
Sex ..............: unspecified
URL of public key : [not set]
Login data .......: [not set]
Signature PIN ....: not forced
Key attributes ...: rsa2048 rsa2048 rsa2048
Max. PIN lengths .: 127 127 127
PIN retry counter : 3 0 3
Signature counter : 0
Signature key ....: [none]
Encryption key....: [none]
Authentication key: [none]
General key info..: [none]

gpg/card&gt; admin
Admin commands are allowed

gpg/card&gt; passwd
gpg: OpenPGP card no.  detected

1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit

Your selection? 1
PIN changed.     

1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit

Your selection? 3
PIN changed.     

1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit

Your selection? q
</code></pre></div></div>

<h3 id="back-up-your-gpg-key">BACK UP YOUR GPG KEY</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ gpg --export-secret-keys 8A2BD353 &gt; 8A2BD353.asc
</code></pre></div></div>

<h3 id="loading-your-gpg-onto-the-yubikey">Loading your GPG onto the YubiKey</h3>

<p><a href="https://support.yubico.com/support/solutions/articles/15000006420-using-your-yubikey-with-openpgp">(source 1)</a></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ gpg --edit-key 8A2BD353
gpg (GnuPG) 2.2.12; Copyright (C) 2018 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Secret key is available.

gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
sec  rsa4096/BDE438068A2BD353
     created: 2013-09-11  expires: never       usage: SC  
     trust: ultimate      validity: ultimate
ssb  rsa4096/422DAD542FC162F9
     created: 2013-09-11  expires: never       usage: E
ssb  rsa4096/1EA455BF07A988D2
     created: 2019-07-23  expires: never       usage: A   
[ultimate] (1). Sarah Harvey &lt;-@shh.sh&gt;
[ultimate] (2)  Sarah Harvey &lt;-------@cs.uwaterloo.ca&gt;
[ultimate] (3)  Sarah Harvey &lt;------------@gmail.com&gt;
[ultimate] (4)  Sarah Harvey &lt;-------@csclub.uwaterloo.ca&gt;
[ultimate] (5)  [jpeg image of size 13232]
[ultimate] (6)  Sarah Harvey &lt;---@squareup.com&gt;

gpg&gt; keytocard
Really move the primary key? (y/N) y
Please select where to store the key:
   (1) Signature key
   (3) Authentication key
Your selection? 1

gpg&gt; key 1

sec  rsa4096/BDE438068A2BD353
     created: 2013-09-11  expires: never       usage: SC  
     trust: ultimate      validity: ultimate
ssb* rsa4096/422DAD542FC162F9
     created: 2013-09-11  expires: never       usage: E
ssb  rsa4096/1EA455BF07A988D2
     created: 2019-07-23  expires: never       usage: A   

gpg&gt; keytocard
Please select where to store the key:
   (2) Encryption key
Your selection? 2

gpg&gt; key 1

gpg&gt; key 2

sec  rsa4096/BDE438068A2BD353
     created: 2013-09-11  expires: never       usage: SC  
     trust: ultimate      validity: ultimate
ssb  rsa4096/422DAD542FC162F9
     created: 2013-09-11  expires: never       usage: E
ssb* rsa4096/1EA455BF07A988D2
     created: 2019-07-23  expires: never       usage: A   

gpg&gt; keytocard
Please select where to store the key:
   (3) Authentication key
Your selection? 3

sec  rsa4096/BDE438068A2BD353
     created: 2013-09-11  expires: never       usage: SC  
     trust: ultimate      validity: ultimate
ssb  rsa4096/422DAD542FC162F9
     created: 2013-09-11  expires: never       usage: E
ssb* rsa4096/1EA455BF07A988D2
     created: 2019-07-23  expires: never       usage: A   

gpg&gt; quit
Save changes? (y/N) y
</code></pre></div></div>

<h3 id="setting-up-git-with-gpg-signed-commits">Setting up Git with GPG-signed commits</h3>

<p><a href="https://help.github.com/en/articles/signing-commits">(source 1)</a> <a href="https://gist.github.com/troyfontaine/18c9146295168ee9ca2b30c00bd1b41e">(source 2)</a></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ git config --global commit.gpgsign true
</code></pre></div></div>

<p>For good measure, kill gpg-agent and restart your terminal:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ killall gpg-agent # forces gpg-agent to restart
</code></pre></div></div>

<h3 id="setting-up-ssh-with-gpg">Setting up SSH with GPG</h3>

<p><a href="https://www.linode.com/docs/security/authentication/gpg-key-for-ssh-authentication/">(source 1)</a> <a href="https://github.com/robbyrussell/oh-my-zsh/pull/6140/files">(source 2)</a></p>

<p>Add the following to <code class="language-plaintext highlighter-rouge">~/.bash_profile</code> (mac-specific):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>GPG_AGENT_SOCKET="$HOME/.gnupg/S.gpg-agent.ssh"
if [ ! -S $GPG_AGENT_SOCKET ]; then
  gpg-agent --use-standard-socket --daemon &gt;/dev/null 2&gt;&amp;1
  export GPG_TTY=$(tty)
fi
unset SSH_AGENT_PID
export SSH_AUTH_SOCK=$GPG_AGENT_SOCKET
</code></pre></div></div>

<p>Add the following to <code class="language-plaintext highlighter-rouge">~/.gnupg/gpg-agent.conf</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>default-cache-ttl 600
max-cache-ttl 7200
enable-ssh-support
</code></pre></div></div>

<p>For good measure, kill gpg-agent and restart your terminal:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ killall gpg-agent
</code></pre></div></div>

<p>Extract your new SSH pubkey from your yubikey:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ ssh-add -L &gt; ~/.ssh/id_yubikey.pub
</code></pre></div></div>

<p>Add the contents of that pubkey to your remote server’s <code class="language-plaintext highlighter-rouge">~/.ssh/authorized_keys</code>.</p>]]></content><author><name>@shh</name></author><category term="guide" /><category term="tech" /><category term="yubikey" /><summary type="html"><![CDATA[This guide is to help you set up a YubiKey to sign git commits and also SSH into servers using key-based auth. It's intended for software developers who want marginally more security and peace of mind while doing development.]]></summary></entry><entry><title type="html">Baby ML</title><link href="https://www.shh.sh/2019/04/04/baby-ml.html" rel="alternate" type="text/html" title="Baby ML" /><published>2019-04-04T00:00:00+00:00</published><updated>2019-04-04T00:00:00+00:00</updated><id>https://www.shh.sh/2019/04/04/baby-ml</id><content type="html" xml:base="https://www.shh.sh/2019/04/04/baby-ml.html"><![CDATA[<p>Sometimes you, like me, forget how to actually use basic machine learning, so here is a scikit-learn starter kit for text in python.</p>

<p>Note that even just 1MB of text data has the potential to generate about 300MB of nonsparse vectors, so saving the vectors directly here doesn’t work for very large text datasets. It’s more recommended to save the actual model in sparse mode (which I will save for another snippet/guide).</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>from nltk import bigrams
from sklearn.linear_model import LogisticRegression
import json


def vectorize_line(header, line):
    unfiltered = ['{}{}'.format(i[0], i[1]) for i in list(bigrams(line.strip()))]
    return [unfiltered.count(h) for h in header]


def vectorize_files(true_file, false_file):
    header_set = []

    true_list = []
    with open(true_file, 'r') as fp:
        for line in fp:
            true_list.append(['{}{}'.format(i[0], i[1]) for i in list(bigrams(line.strip()))])
            header_set.extend([i for i in true_list[-1]])
            header_set = list(set(header_set))

    false_list = []
    with open(false_file, 'r') as fp:
        for line in fp:
            false_list.append(['{}{}'.format(i[0], i[1]) for i in list(bigrams(line.strip()))])
            header_set.extend([i for i in false_list[-1]])
            header_set = list(set(header_set))

    header = list(header_set)

    true_vector = []
    for e in true_list:
        true_vector.append([e.count(h) for h in header])

    false_vector = []
    for e in false_list:
        false_vector.append([e.count(h) for h in header])

    X = true_vector + false_vector
    y = [1] * len(true_vector) + [0] * len(false_vector)
    return header, X, y


def load(vectors_file):
    with open(vectors_file, 'r') as fp:
        return json.load(fp)


def save(vectors_file, header, X, y):
    with open(vectors_file, 'w') as fp:
        json.dump({'h': header, 'X': X, 'y': y}, fp)


def run():
    #header, X, y = vectorize_files('emails.txt', 'not-emails.txt')
    #save('vectors.json', header, X, y)

    vectors = load('vectors.json')
    h = vectors['h']
    X = vectors['X']
    y = vectors['y']
    clf = LogisticRegression(random_state=0, solver='lbfgs', multi_class='multinomial').fit(X, y)
    print(clf.predict([vectorize_line(h, 'noreply@gmail.com')]))


if __name__ == '__main__':
    run()

</code></pre></div></div>]]></content><author><name>@shh</name></author><category term="guide" /><category term="tech" /><category term="ml" /><category term="code" /><summary type="html"><![CDATA[Sometimes you, like me, forget how to actually use basic machine learning. so here is a scikit-learn starter kit for text in python.]]></summary></entry><entry><title type="html">Git in 5 seconds</title><link href="https://www.shh.sh/2019/03/08/git.html" rel="alternate" type="text/html" title="Git in 5 seconds" /><published>2019-03-08T00:00:00+00:00</published><updated>2019-03-08T00:00:00+00:00</updated><id>https://www.shh.sh/2019/03/08/git</id><content type="html" xml:base="https://www.shh.sh/2019/03/08/git.html"><![CDATA[<p>You may also be interested in <a href="https://jvns.ca/about/">Julia Evans’</a> <a href="https://jvns.ca/blog/2018/10/27/new-zine--oh-shit--git-/">zine on git</a></p>

<h3 id="shortcut-command-installation">Shortcut command installation</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gem install grb
gem install gpr
gem install gap
</code></pre></div></div>

<h3 id="checking-out-a-repo">Checking out a repo</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone &lt;repo&gt;
cd &lt;repo&gt;
</code></pre></div></div>

<h3 id="updating-main">Updating main</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git fetch origin main
</code></pre></div></div>

<h3 id="creating-a-new-branch">Creating a new branch</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>grb new &lt;user&gt;/&lt;project&gt;/&lt;feature/ticket/whatever you want&gt;
</code></pre></div></div>

<p>_<do your="" thing="">_</do></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git add &lt;files&gt;
</code></pre></div></div>

<p>OR</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gap
</code></pre></div></div>

<p>OR</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git add --patch
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git commit -m &lt;message&gt;
git push
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git scmp
</code></pre></div></div>

<p>OR</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gpr
</code></pre></div></div>

<p><em>&lt;follow instructions (add some reviewers) to create a pull request to main&gt;</em></p>

<h3 id="on-commits-and-amending">On commits and amending</h3>

<p>In general, smaller commits (and PRs) are better. Descriptive commit messages are great; if you’re still working on something, it’s fine to go back and change the commit messages later.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git commit --amend
</code></pre></div></div>

<h3 id="rebasing-from-main-on-your-branch">Rebasing from main on your branch</h3>
<p>(do this before merging on stale PRs!)</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git rebase -i origin main
git push -f
</code></pre></div></div>

<h3 id="rebasing-to-squashamenddelete-commits">Rebasing to squash/amend/delete commits</h3>

<p>You can squash, reorder, split, or even change the message of some commits. This is great if you had a bunch of nit commits that could all be squashed, or if you want to split a large commit into several smaller ones. The instructions are mostly straightforward…</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git rebase -i HEAD~&lt;number of commits&gt;
</code></pre></div></div>

<h3 id="changing-branches">Changing branches</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git checkout &lt;branch&gt;
</code></pre></div></div>

<h3 id="holding-onto-temp-changes-without-explicit-commits">Holding onto temp changes without explicit commits</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git stash
</code></pre></div></div>

<p>Note that this will stash any file changes, including ones that are unstaged. However it will not stash any untracked files.</p>

<h3 id="branch-history">Branch history</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git log
</code></pre></div></div>

<h3 id="copying-a-commit-from-elsewhere">Copying a commit from elsewhere</h3>
<p>This is known as cherry-picking.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git cherry-pick &lt;commit&gt;
</code></pre></div></div>]]></content><author><name>@shh</name></author><category term="guide" /><category term="git" /><category term="tech" /><category term="short" /><summary type="html"><![CDATA[You may also be interested in Julia Evans’ zine on git]]></summary></entry></feed>