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

Isaac refactoring #4

Merged
merged 3 commits into from
Sep 13, 2017
Merged

Isaac refactoring #4

merged 3 commits into from
Sep 13, 2017

Conversation

pitdicker
Copy link

You already said you are not likely to merge any ISAAC+ modifications. But I still give it a try, because most changes should be useful.

The first commit is just refactoring.
I have replaced some macro's by functions. This appearently made it possible to remove the unsafe indexing without a performance loss.

Over time the implementation of ISAAC and ISAAC-64 have diverged a bit, now they are as similar as possible (for easier comparison). IsaacRng now does 34% better in the benchmark :-)!

I have tested the code against the previous implementation, and the reference implementation.

The second commit does the modifications for ISAAC+. I can easily remove it if you want. But the problem is, that I do like to sometimes contribute to an existing project, but don't want to commit to a (small) library of myself.

The ISAAC+ algorithm is described in the paper On the pseudo-random generator ISAAC by Jean-Philippe Aumasson. The specification is however not complete. The definition of f'(a,i) is missing. But the text says that in that step we should "perform rotations instead of shifts, so as to get more diffusion from the state bits". So f'(a,i) is f(a,i) with shifts replaced by rotations.

It mentions two other changes: in the rngstep function one addition should be replaced by a XOR. And there should be one extra XOR with a.

Finally the algorithm in the paper shows that in the ind function we should not do shifts, but rotations. But performing a rotation-right of 2 or 10 on an u32, and than only using the last 8 bits, is no different than a shift right. I suppose it makes it easier to describe the new algorithm as 'all shifts are replaced by rotations'.

I found one other (correct) implementation of ISAAC+: konovod/prngs@749c33b

@dhardy
Copy link
Owner

dhardy commented Sep 7, 2017

But the problem is, that I do like to sometimes contribute to an existing project, but don't want to commit to a (small) library of myself.

Ok, fair point that you don't want an extra library to maintain. Possibly I will merge then; it remains to be decided exactly what happens with the RNG implementations (several people would like them all moved to separate crates).

What's your source for the sequences in the "true values" tests?

I think ISAAC+ should be implemented via new types, not modifying the existing ones (first, because there's very little literature review of the algorithm and none of the code yet, so many people will be wary of it and won't want it forced on them; second, because people explicitly using the existing impls might actually want reproducible results).

I will try to review this; links to any external resources used would be great.

@pitdicker
Copy link
Author

pitdicker commented Sep 7, 2017

it remains to be decided exactly what happens with the RNG implementations (several people would like them all moved to separate crates).

I think it would be a good idea to keep a number of good-quality RNG's together in a standard crate. Because otherwise well-known RNG's could be used a lot, while they may not be the best idea. But that is more a discussion for RFC thread.

What's your source for the sequences in the "true values" tests?

I have to admit, this are just the results of this implementation. I don't really see a way to get them from somewhere else. The only other good implementation I could find is not in C, so it is not easy to hook it up with the seeds here.

I think ISAAC+ should be implemented via new types, not modifying the existing ones

I think that is reasonable. One thing that made me unsure about adding it as a new type, was that it duplicates a lot of code. You now not only make sure IsaacRng and Isaac64Rng remain in sync, but also two other types. But maybe just a comment at the top of the file is enough.

I will try to review this; links to any external resources used would be great.

I was thinking about writing some large comments, describing

  • the algorithm,
  • choices made for optimization
  • differences between ISAAC, ISAAC-64 and ISAAC+
    If it would make things easier to review, just say so and I'll add it (in a few days...)

Most useful for me where the following links:
http://burtleburtle.net/bob/rand/isaac.html#ISAAC (description of algorithm and optimisations)
http://burtleburtle.net/bob/rand/isaac.html#Introduction (notes about the different constants in ISAAC-64)
http://burtleburtle.net/bob/c/readable.c (ISAAC reference implementation)
http://burtleburtle.net/bob/c/isaac64.c (ISAAC-64 reference implementation)
http://eprint.iacr.org/2006/438 (Paper with good description of ISAAC algorithm, weaknesses, and ISAAC+)
https://ccodearchive.net/info/isaac.html (good collection of notes / history)
https://github.com/moschles/isaac-gen/wiki (discussing whether ISAAC is secure enough for cryptography)

@dhardy
Copy link
Owner

dhardy commented Sep 7, 2017

I have to admit, this are just the results of this implementation.

That's still much better than nothing, but please write comment in the code. I'll try comparing results with the Crystal impl when I get to this (I'm more focussed on the core traits for now).

Thanks for the links; again, please put some of those in as comments. I'm mainly thinking of anyone looking at the code in the future; it's not always easy finding PRs like this one (especially since this will likely get rebased and moved to a different repo at some point).

@pitdicker
Copy link
Author

You are doing good work designing the core traits 👍 .
It is not something I feel ready to contribute to...

I have changed my thoughts about ISAAC+. It may be okay, but there are just to many little problems, as I've written in rust-lang/rfcs#2106 (comment).

I will add more comments to the normal code.

Paul Dicker added 2 commits September 11, 2017 20:55
For into `isaac` and `isaac64` for easier comparison.
Split of `isaac_word` to generate correct the documentation (TODO).

Nothing is changed here, just split in three files.
This removes the unsafe blocks from the main isaac and isaac64 routines.
Some macro's are replaced by functions.
Over time the implementation of ISAAC and ISAAC-64 have diverged a bit, now they
are as similar as possible (for easier comparison).

The code is tested against the previous implementation, and the reference
implementation. IsaacRng now does 34% better in the benchmark.
@pitdicker
Copy link
Author

pitdicker commented Sep 11, 2017

I have added a little comments, and better documentation.

Also I have split isaac.rs into three files. While working on the code, I found it helpful to have ISAAC and ISAAC64 side-by-side to see the differences. And if it was useful to may, maybe it also is for others in the future.

I split IsaacWordRng to a different file, and changed it to not be a simple re-export. This helps for documentation. I just noticed that part does not work yet, but the rest should be ready for review.

@pitdicker
Copy link
Author

IsaacWordRng should now be ok. I made it a newtype around IsaacRng or Isaac64Rng. This way we can clearly explain its purpose in the documentation. Otherwise it would often have the same documentation as Isaac64Rng, is the platform most documentation is generated on.

let mut m2 = MIDPOINT;
for i in (0..MIDPOINT/4).map(|i| i * 4) {
rngstep(self, !(a ^ (a << 21)), &mut a, &mut b, i + 0, m, m2);
rngstep(self, a ^ (a >> 5 ), &mut a, &mut b, i + 1, m, m2);
Copy link
Author

@pitdicker pitdicker Sep 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here the mix value is completely calculated, instead of passing only a shift to the rngstep* macro like the previous code.
For ISAAC this worked ok, although it needed two different macros for the shift right and shift left. But for ISAAC-64 it was starting to make the code harder to read, because the first step should also invert the mix.

*a = mix + ctx.mem[base + m2];
let y = *a + *b + ind(&ctx.mem, x, 3);
ctx.mem[base + m] = y;
*b = x + ind(&ctx.mem, y, 3 + RAND_SIZE_LEN);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the previous code it was less clear how large the shift was. ind!() in ISAAC was always shifting by 2, and in ISAAC-64 by 3. A further shift was applied by the calling code when calculating b. Now we just always pass the amount to shift to ind().

Copy link
Owner

@dhardy dhardy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a lot cleaner and better documented, thanks @pitdicker!

Any chance you could extend the "true value" tests to go further in the future? I think right now they only test the output of the first buffer fill. It doesn't need to test all intermediate values, so doesn't need a huge block of numbers in the code.

///
/// Where fast random numbers are needed which should still be secure, but where
/// speed is more important than absolute (cryptographic) security (e.g. to
/// initialise hashes in the std library), a generator like ISAAC should be
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should is very assertive language. Unless you're sure about this it might be better to write something like "may be a good choice".

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have copied this line from your RFC ;-)

///
/// In 2006 an improvement to ISAAC was suggested by Jean-Philippe Aumasson,
/// named ISAAC+[3]. But because the specification is not complete, there is no
/// good implementation, and because the suggested bias may not exist, it is not
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reference implementation
There's also the problem of lack of third-party review.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, true. But I tried to not say too much about an algorithm we don't implement.

/// Input: a, b, c, s[256] // state
/// Output: r[256] // results
///
/// mix(a,i) = a ^ a << 13 i = 0 mod 4
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there should be an "if" before i = ...

/// in 1996[1][2].
///
/// Although ISAAC is designed to be cryptographically secure, its design is not
/// founded in cryptographic theory. Therefore it should _not_ be used for
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe better to say is not recommended for? Or are you quoting Jenkins?

/// cryptographic purposes, but this implementation has not be
/// verified as such. Prefer a generator like `OsRng` that defers to
/// the operating system for cases that need high security.
/// ISAAC therefore needs comparatively much memory. 2 * 256 * 4 = 2 kb to hold
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ISAAC therefore needs a lot of memory, relative to non-crypto RNGs.

[As I understand, most crypto RNGs need a lot of memory.]

/// depending on the pointer size of the target architecture.
///
/// In general a random number generator that internally uses the word size of
/// the target architecture is faster than one that doesn't. Choosing
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Until we get to things like x32. And then modern CPUs support so much SIMD that we now have a pretty good u128 type. Leave this as-is for now, but it might be worth thinking about.

@pitdicker
Copy link
Author

That where good comments, thank you. Updated.

@dhardy dhardy merged commit 14d0561 into dhardy:master Sep 13, 2017
@dhardy
Copy link
Owner

dhardy commented Sep 13, 2017

Merged. I created a few conflicts so figured I should solve them. I also removed a few #[inline] attributes (in theory the compiler should work out when to do this itself most of the time) and removed the SeedableRng impl on IsaacWordRng (because that's not reproducible cross platforms).

@pitdicker pitdicker deleted the isaac-rewrite branch October 21, 2017 14:40
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

Successfully merging this pull request may close these issues.

2 participants