Tuesday, February 28, 2017

February Cycling

Up to 14625.1, 0, 128.1 -- 147.6 miles, which is better than January, since I did manage to average more than one day a week commuting by bike, and the weather has at times felt positively spring-like. No rides just for pure pleasure, as yet, but definitely more inclination to go by bike for business.

Saturday, February 25, 2017

Powershell Transcript cmdlets and secondary runspaces gotcha

So, I had some fun this past week, with a piece of code that, stripped to its essentials, looked like


which yields

Stop-Transcript : An error occurred stopping transcription: The host is not currently transcribing.
At line:1 char:1
+ Stop-Transcript
+ ~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [Stop-Transcript], PSInvalidOperationException
    + FullyQualifiedErrorId : InvalidOperation,Microsoft.PowerShell.Commands.StopTranscriptCommand

with a transcript (again, stripped to its essentials) of

Transcript started, output file is ...
PS>$pool = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspacePool(1,1)
PS>$pool.Open()
PS># do stuff...
PS>$pool.Close()
**********************
Windows PowerShell transcript end

with nothing after the Close() showing up.

It turns out that, however you create a runspace, even if using an instance of a custom PSHost subclass and explicitly minting one through CreateRunspace(), the runspace will still get attached to an internal PSHost subtype, which couples it to a whole web of other internal and/or sealed types, eventually linking it to the transcription state of the overall PowerShell session. And when a runspace closes, it closes all open transcripts attached to it.

WTF FAIL!

Fortunately, there is one public API available to us that can sever this link, and one that makes a perverse sort of sense, after you've run through all the plumbing:


which finishes with

Transcript stopped, output file is...

and a transcript that looks like

Transcript started, output file is ...

PS>$pool = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspacePool(1,1)
PS>$save = [System.Management.Automation.Runspaces.Runspace]::DefaultRunspace
PS>try {
    [System.Management.Automation.Runspaces.Runspace]::DefaultRunspace = $null
    $pool.Open()
    # do stuff...
    $pool.Close()
    # do more stuff...
}
finally {
    [System.Management.Automation.Runspaces.Runspace]::DefaultRunspace = $save
}
PS>Stop-Transcript
**********************
Windows PowerShell transcript end

because it turns out that, via a long chain of indirections, it is the -- fortunately thread-static -- global default runspace which is the thing that contaminates our intended-to-be-isolated worker environment.

Now, it would be understandable if runspace construction were to directly use the default runspace as a prototype, but it's nothing so obvious. It actually comes in via the UI object that is tenuously attached to the runspace reaching out to the default runspace. That's not so good, and speaks of excessive internal coupling.

Checking metrics on the assembly System.Management.Automation 3.0.0.0, we can see that it is indeed highly internally coupled, with a relational cohesion of 7.22, which is a level that is not so much coherent as positively incestuous. So while I didn't spot any other obvious booby-traps waiting to be sprung, I'm sure there are others that will rise up and bite the occasional edge-case.


Sunday, February 05, 2017

SHA256.Create() and "Works on my machine" FIPS-compliance

I've just had an interesting run-in with a little-advertised and not backwards-compatible feature in .net 4.6 and up, and how it affects FIPS-compliance, for those of us who have to worry about such things.

You see, in the recent .net versions (unlike the older ones), the selection of the default implementation toggles on whether the machine has HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\FipsAlgorithmPolicy\[Enabled] set non-zero or not (or the equivalent group policy), and will select a FIPS-certified implementation for SHA256 (and SHA384 and SHA512), and, interestingly only for those flavours of SHA-2, using exactly the same criterion as is used to make the non-compliant implementations raise an exception on construction. So, assuming no app or machine level overrides, the matrix looks like this:

SHA###.Create()
no FIPS enforcementFIPS enforced
.net 4.6 and up

(Dev machine)
SHA###Managed

OK
SHA###Cng

"Works on my machine."
.net 4.5.2 or earlier

(Slow-moving customer environment)
SHA###Managed

OK
SHA###Managed

KABOOM!

The end-stop selection of algorithm defaults (where not overridden by a specific [class].Create()) in .net 4.5.2 are drawn from mscorlib alone, many FIPS-compliant, with the notable exceptions being the "new" (SHA-2, or AES which is present only by the original name of Rijndael), or old-and-deprecated (like MD5) algorithms. As in most cases, [DerivedClass].Create() is just a synonym for [BaseAlgorithm].Create(), you can get a false sense of security here -- SHA256CryptoServiceProvider.Create() will equally spit out an instance of SHA256Managed in three of the cases above, and SHA256Cng in the fourth ("works on my machine").

TL;DR -- if you have to worry about FIPS, don't use [Algorithm].Create(), but select the one you mean by calling its constructor explicitly.


Wednesday, February 01, 2017

January Cycling

Up to 14477.7, 0, 127.9 -- 108.7 + 0 + 1.8 = 109.5 miles in a chilly month, with a lot of days where I might have cycled blocked out by other things, so I only ended up commuting once a week, with one shopping trip and one expedition for fresh air on the New Year Bank Holiday. That may be well less than I did last year, but this time I'm not feeling ground down by the cycle-work-cycle-work-cook-eat-sleep routine I was caught in back then.