Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Why does Math.random open non-overt channels? #10

Open
erights opened this issue Mar 27, 2016 · 6 comments
Open

Why does Math.random open non-overt channels? #10

erights opened this issue Mar 27, 2016 · 6 comments

Comments

@erights
Copy link
Collaborator

erights commented Mar 27, 2016

If we specified that Math.random is seeded by an adequate source of entropy and that it itself is a cryptographically strong random number generator, then it would be, as far as anyone (ignorant of the encapsulated seed) can tell, perfectly non-deterministic, which, amusingly, also does not enable any side channels or covert channels.

However, because someone sometime in the distant past used Math.random in the inner loop of some benchmark, all browsers made it as fast as possible while being just good enough for most statistical purposes. There have actually been exploits caused by the ability of one caller of Math.random to infer how many times someone else called it between two calls by itself. (Does anyone have a link?)

Rather than ask browser makers to provide a strong Math.random in the SES realm, it is safer to just remove it. It is impossible to write a black box conformance test that tests whether a random number generator is producing good enough randomness. There are already distinct APIs that provide good randomness anyway, though these are currently provided only by the host environment. Rather than fix Math.random, we should move some of this work into the language standard.

In any case, even though a strong enough Math.random would not open non-overt channels, it would prevent reproducibility and so hurt SES's benefits for testing and debugging.

@benjamingr
Copy link

I think removing Math.random() downright like that would be confusing for developers trying to use frozen-realms.

It definitely can't stay as is, because it would give the user internal information about the system.

The problem we need to solve isn't "Math.random is predictable" it's "Math.random leaks information about external state".

Wouldn't requiring that Math.random inside a frozen realm be completely isolated from the regular Math.random be sufficient?

@erights
Copy link
Collaborator Author

erights commented Mar 31, 2016

I think removing Math.random() downright like that would be confusing for developers trying to use frozen-realms.

That's the reason for the polyfill example. Most developers will, I expect, see only lightweight realms spawned for a polyfilled frozen realms with Date and Math restored to their normal function.

It definitely can't stay as is, because it would give the user internal information about the system.

The problem we need to solve isn't "Math.random is predictable" it's "Math.random leaks information about external state".

Wouldn't requiring that Math.random inside a frozen realm be completely isolated from the regular Math.random be sufficient?

I think this misunderstands which "external" we are concerned about. Two objects, Alice and Bob, that share the same frozen primordials and nothing else should not thereby be able to communicate. If Math.random is predicable enough, then we enable the following scenario:

Alice says:

const x = Math.random();

In a separate interleaved turn later Bob says:

if (secretBit) { Math.random(); }

In a separate interleaved turn later Alice says:

const y = Math.random();
if (y === expectedSuccessor(x)) {
  // secretBit was zero
} else if (y === expectedSuccessor(expectedSuccessor(x))) {
  // secretBit was probably one
} else {
  // What me worry?
}

@benjamingr
Copy link

I think this misunderstands which "external" we are concerned about. Two objects, Alice and Bob, that share the same frozen primordials and nothing else should not thereby be able to communicate.

This is exactly what I meant by "external", our actual requirement here is that the above scenario is not possible - not that Math.random is not defined.

We only need implementations to provide independent Math.random for every new spawned safe realm. So that in:

Alice:

 if(attempt) Math.random();

Bob:

var x = Math.random();

Bob can't deduce anything regardless of whether attempt was true in Alice's realm. One way to solve it is a cryptographically strong pseudorandom number generator - another is that implementations use two independent cryptographically weak ones.

@benjamingr
Copy link

Oh I see the miscommunication, when I said:

Wouldn't requiring that Math.random inside a frozen realm be completely isolated from the regular Math.random be sufficient?

I really should have said:

Wouldn't requiring that Math.random inside a frozen realm be completely isolated from the regular Math.random and other frozen realms be sufficient?

@erights
Copy link
Collaborator Author

erights commented Apr 1, 2016

If Alice and Bob do not need their own globals, they might have simply been the result of evaluating

const alice = Realm.TheFrozenRealm.eval(aliceSrc);
const bob = Realm.TheFrozenRealm.eval(bobSrc);

(I am using the API as written in this proposal at this time, though it is already obsolete based on feedback from the TC39. The API change does not affect the point illustrated by the above code.)

See the mobile code example in the proposal for a concrete case when evaluating in the root realm is fine and there's no need to spawn separate child realms.

@benjamingr
Copy link

Oh, if Math.random is actually shared - then it has to be cryptographically strong or not execute.

I wonder if Math.random can be patched through Zones to act independent - if each different zone gets a different Math.random and zones are set when evaluating foreign code - then technically we meet the requirement without removing Math.random - no?

const randoms = new WeakMap();
Math.random = () {
    if(!randoms.has(Zone.current)) randoms.put(Zone.current, getIndependentGenerator());
    return randoms.get(Zone.current)();
};

This assumes Zone.current is a valid map key.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants