1. 16
  1.  

  2. 14

    Neither post mentions what the actual bug is; I think it’s this one; that putByte() function calls String.fromCharCode(), and the call does as well. Why does this matter?

    >>> String.fromCharCode(65)
    "A"
    
    >>> String.fromCharCode(String.fromCharCode(65))
    "\u0000"
    

    So that’s not going to be very random 🙃

    If only JavaScript had some sort mechanism to signal unusual situations when the programmer almost certainly made a mistake by passing the wrong type…

    Looks like this bug was already present in the first commit from 2013.

    1. 3

      i love all the TC39 proposals, but i do honestly feel like type annotations are an important feature that needs to be in the language. it’s pretty clear from typescript’s adoption that type defs are something JS devs want, and i don’t think most of us also want to be subservient to microsoft’s whims either.

      1. 3

        Type hints won’t really help here, because you’ll still have those function from the standard library that accept $anything and do $weird_stuff. They can be fixed without type hinting quite easily: if (typeof(arg) != 'number') thow Exception('not a number').

        This is really a core problem that’s not easily fixable with a new feature or whatnot. You really need to break things in a backwards-incompatible way, although that can be guarded with a use stricter similar to the already-existing use strict. They didn’t want to do that “because it would fork the web”, because clearly writing bug-prone software for decades to come is better, and as if the TypeScript thing isn’t a fork either.

        These mistakes compounds and leak in to “modern” JS too; what happens if you try to send a plain ol’ Object with the “modern” fetch API? Seems like that would work no? Nope! It will send [object Object] to the server as POST data. Yes, really. This isn’t a modern API; throw an exception, return an error, don’t send a request, anything else makes more sense than sending [object Object].

        So people use millions upon millions of lines of tools to work around this stuff… TypeScript isn’t wholly bad, but an entire ecosystem to compile, manage, and debug all of it is a lot of overhead, never mind having to know two languages (kind-of). And even with all of that I find the daily dev-experience suboptimal as I can’t just take a few lines of TypeScript and run it in the browser’s REPL. It’s one step forward and two steps back.

        All these other TC39 things are unimportant until this is fixed. “Modern” JavaScript isn’t better; it’s just the old broken JavaScript with more features (which doesn’t necessarily make it better) and layers upon layers on top on things to manage the broken foundations.

        1. 1

          And even with all of that I find the daily dev-experience suboptimal as I can’t just take a few lines of TypeScript and run it in the browser’s REPL.

          Google tried to do this with Dart, like 10 years ago, but they gave up before getting very far. Which is, perhaps, fair since, at that point in time, Dart was a pretty “meh” language itself and people hadn’t really gotten on board the type train yet (CoffeeScript was still cool).

          1. 1

            The last time I used Dart (a long time ago now), I was pretty taken aback by how big the bundle ended up being after you compiled to JS. I think the value add of type-safety was not enough to justify the major drawback of your code taking 4x the time to download. When TypeScript was introduced, the only reason I even looked at it was its promise that it would compile down to JS that is easy to read and not filled with extraneous garbage you’re not using.

            1. 1

              Yeah, TypeScript is definitely the way to go (IMHO) for JS development. The original promise of Dart was that there would be a VM in the browser, so you wouldn’t need to compile to JS at all. That didn’t pan out, though.

              1. 2

                I mean… ;-)

                One day, perhaps!

          2. 1

            Type hints won’t really help here, because you’ll still have those function from the standard library that accept $anything and do $weird_stuff.

            I do get that, and I think if JS ever adopts some level of type-safety it might have to be “except the DOM APIs” for that reason, at least in the very beginning. What I really want is for TypeScript’s type-safety guarantees to apply after I compile, honestly. Having to worry about types seems like a lot of work if you don’t even get those benefits when your code runs in the real world. It feels like training wheels, not a better bicycle.

            1. 1

              If you would add type hints everywhere you’ll still have stuff like 1 + '1' = '11', '' + {} = '[object Object], etc. Your function is constrained to just a number but if you don’t change the automatic casting rules then it’s still a huge footgun. You can’t really fix that with type hints, as it would mean you’d have two different “flavours” of numbers: “regular numbers” and “strict numbers”, which behave different depending on how the function was declared. Copy/pasting code from one function to the other could potentially break it, or make it behave different. That would be even more confusing.

              Actually, if you fix this then a lot of these APIs will probably be fixed automatically as well; the reason these APIs behave weird is because JavaScript in general behaves weird with all its automatic casting.

              And you can do this in a compatible way with 'use strict 2';, 'use stricter';, 'strict me harder';, or whatnot. Perl has been doing this for a long time, and it works quite well.

              Once this is fixed you have a decent (dynamic) type system to work off, and you can start thinking about type hints to constrain what functions can accept.

      2. 2

        I wonder if it’s possible to identify weak keys, the same way we can identify weak passwords. There is no haveibeenpwned for public keys as far as I know, and rules like “at least one upper case, lower case, number, punctuation” don’t make sense for seemingly random public keys either.

        1. 7

          It depends on the weakness and I’m not sure if the details here, but the common approach is to generate the set of weak keys. For example, the Debian vulnerability from way back resulted in a small enough set of possible SSH public keys that it was feasible to generate the entire set and block them all.

          There’s no portable definition of ‘weak’ for something like an SSH key (ignoring things like RSA bugs using the wrong kind of prime), because the weaknesses are usually not in the keys but in the random number generator. More generally, if a key is generated by applying a key-derivation function to a random number, there are two possible ways that this can go wrong: either the KDF is broken or the random number has less entropy than you think it has. The KDF is usually pretty well reviewed and is unlikely to be the source of bugs.

          The random number is really hard to review because cryptographic random number generators incorporate a cryptographic hash function and so will give uniform distributions whether they’re actually secure or not. You can, with some oversimplification, think of a cryptographic random number generator as a hash function that is applied to some set of random things. Unless you exhaustively sample, you won’t notice if the 128-bit random numbers are always in a set of, say, 2^24. To attack them, you need to know the way in which the random number generator was weakened.

          This is even worse when one of the sources of entropy is not properly mixed via something like Yarrow or Fortuna and turns out to be much weaker than expected. For example, consider using the current time in milliseconds as an input. You’ll get a lot of different outputs as you sample the random number generator, but if you know that this is the weakness and you know when the key was generated (not normally secret) to the nearest hour then you know that there are only 3,600,000 possible values for that time. That’s a small enough number to do an exhaustive search on. Even if you only know the year, that only multiplies the effort by a factor of 8,760, so it’s still fairly easy. In fact, if you use millisecond-precision time as your entropy source then it’s quite computationally feasible for an attacker to generate every possible key that you can have generated since the SSH protocol was introduced and compare against any of them: you’re looking at less than 40 bits of entropy.

          1. 2

            This is true, but some classes of non-randomness can be detected without going into the details about the algorithms. See https://en.wikipedia.org/wiki/Diehard_tests and https://en.wikipedia.org/wiki/TestU01

            1. 3

              True, though these don’t make it possible to generate the set of keys that a buggy random number generator would produce and they only detect certain kinds of non-uniformity in the output distribution. I think a random number generator that does sha512(current time in ms + 8 bits of real wrapping counter incremented on every call) would pass all of these tests, yet it would be easy to generate the full set of keys that such a random number generator would output. Actively malicious random number generators use some variant of this: they mix a secret-derived sequence with a small amount of entropy and produce distributions that pass all of these tests but are predictable by someone who knows the sequence.