public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH][RFC] Allow net devices to contribute to /dev/random
@ 2001-09-25 23:36 Robert Love
  2001-09-26  0:20 ` David Wagner
  0 siblings, 1 reply; 22+ messages in thread
From: Robert Love @ 2001-09-25 23:36 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-kernel

Updated versions of my netdev-random patch are out.  The patchset is two
piece: one containing the core code and two containing the updated
drivers.

2.4.9-ac15:
http://tech9.net/rml/linux/patch-rml-2.4.9-ac15-netdev-random-1
http://tech9.net/rml/linux/patch-rml-2.4.9-ac15-netdev-random-2

2.4.10:
http://tech9.net/rml/linux/patch-rml-2.4.10-netdev-random-1
http://tech9.net/rml/linux/patch-rml-2.4.10-netdev-random-2

ChangeLog and more information:
http://tech9.net/rml/linux/

Quick summary:  This patch enables a new configure option that allows
users to allow whether network devices can contribute to /dev/random. 
Normally block devices and the keyboard contribute, although very few
network devices do.  This patch makes a user-configurable policy out of
the issue: either allow them all or disallow them all.  Some users, such
as those on a headless or diskless system, have little or no entropy. 
This patch will give them that entropy.  Summarizing the discussion on
the issue, as long as SHA-1 is secure or your network traffic is secure,
this is safe.  For those who don't want the option, leave the setting
disabled and no NIC will contribute.

How it works:  defines a new flag for each architecture,
SA_SAMPLE_NET_RANDOM which defines to 0 or SA_SAMPLE_RANDOM depending on
the value of the configure statement.

All architectures and all network devices are supported.  The lastest
patch fixes a few typos and the like.

[You can ignore further if you just wanted the newest patch]

Now, why this I ask for comments.  An alternative approach to this is to
not have a configure setting but instead have a /proc interface.  When
disabled, interrupts will not contribute, and when enabled, they will.

The code is something like this:

define SA_SAMPLE_NET_RANDOM to be our new SA_SAMPLE_RANDOM for NICs
(like with the normal patch).

let random_netdev_contribute be 0 or 1 set from the /proc interface.

in setup_irq and handle_IRQ_event() we change:

if (status & SA_SAMPLE_RANDOM)

to

if ((status & SA_SAMPLE_RANDOM) ||
	   ((status & SA_SAMPLE_NET_RANDOM) && random_netdev_contrib))

That's about it.  Most of the code I have is for the proc interface.

One problem, and one concern.

The problem: setup_irq is called on device setup.  this means that
in-kernel drivers and modules loaded before the /proc interface is set
will have the wrong value registered in setup_irq.  I am not too sure
what this entails

Ie, if random_netdev_contrib=0 when we call setup_irq, we won't call
rand_initialize_irq() but then if random_netdev_contrib is set to 1, we
will all of a sudden start calling add_interrupt_randomness()!  You can
see the reverse of this, too, where we will initialize it but not call
add.

Changing the proc entry on the fly and/or loading/unloading modules just
adds to this mess.

I just don't think this will work cleanly.

Finally, my concern is that the if statement is not the cleanest.  We
have to check for the normal SA_SAMPLE_RANDOM flag, and then we need to
check for the other possibility of the NET version of the flag. If it
exists, we need to see if random_netdev_contrib is set.  Not very
clean.  A cleaner design, anyone?

I am happy to just leave the patch as is, and right now I am thinking I
will do just that.

-- 
Robert M. Love
rml at ufl.edu
rml at tech9.net


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH][RFC] Allow net devices to contribute to /dev/random
  2001-09-25 23:36 [PATCH][RFC] Allow net devices to contribute to /dev/random Robert Love
@ 2001-09-26  0:20 ` David Wagner
  2001-09-26  0:52   ` Robert Love
  2001-10-01  9:52   ` Florian Weimer
  0 siblings, 2 replies; 22+ messages in thread
From: David Wagner @ 2001-09-26  0:20 UTC (permalink / raw)
  To: linux-kernel

I'm worried about the language in the configuration documentation:
  +  Some people, however, feel that network devices should not contribute to
  +  /dev/random because an external attacker could observe incoming packets
  +  in an attempt to learn the entropy pool's state. Note this is completely
  +  theoretical. 
Incrementing the entropy counter based on externally observable
values is dangerous.  Calling this risk 'completely theoretical'
is, I believe, a misrepresentation, unless I've missed something.

A failed RNG is one of the most likely -- and most devastating
-- failure modes possible for modern cryptosystems, and as such
it pays to be careful here.  Are you proposing this for inclusion
in the mainline Linux kernel?  If so, I'm concerned that this patch
could put security at risk.  What am I missing?

Here is my reasoning.  I'd like to quote drivers/char/random.c:
  * add_interrupt_randomness() uses the inter-interrupt timing as random
  * inputs to the entropy pool.  Note that not all interrupts are good
  * sources of randomness!  For example, the timer interrupts is not a
  * good choice, because the periodicity of the interrupts is too
  * regular, and hence predictable to an attacker.  Disk interrupts are
  * a better measure, since the timing of the disk interrupts are more
  * unpredictable.
  * 
  * All of these routines try to estimate how many bits of randomness a
  * particular randomness source.  They do this by keeping track of the
  * first and second order deltas of the event timings.
This suggests that add_interrupt_randomness() should not be called
on all interrupts without discrimination.  Would it be fair to say
that your patch enables randomness collection on almost all interrupts?
If so, why is this safe to do?

I hope you agree that making this configurable does not obviate our
responsibility to make sure that the default configuration is reasonable.
This stuff is subtle, and changing it is not something I'd recommend
without a careful analysis of the impact on security.  Moreover, the
comments about 'completely theoretical' leave me concerned that any
analysis that has been done on this patch may be based on misconceptions
about cryptographic security.  Can you tell us anything about what
security analysis you've done so far?

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH][RFC] Allow net devices to contribute to /dev/random
  2001-09-26  0:20 ` David Wagner
@ 2001-09-26  0:52   ` Robert Love
  2001-09-26  1:36     ` David Wagner
                       ` (3 more replies)
  2001-10-01  9:52   ` Florian Weimer
  1 sibling, 4 replies; 22+ messages in thread
From: Robert Love @ 2001-09-26  0:52 UTC (permalink / raw)
  To: David Wagner; +Cc: linux-kernel

On Tue, 2001-09-25 at 20:20, David Wagner wrote:
> I'm worried about the language in the configuration documentation:
>   +  Some people, however, feel that network devices should not contribute to
>   +  /dev/random because an external attacker could observe incoming packets
>   +  in an attempt to learn the entropy pool's state. Note this is completely
>   +  theoretical. 
> Incrementing the entropy counter based on externally observable
> values is dangerous.  Calling this risk 'completely theoretical'
> is, I believe, a misrepresentation, unless I've missed something.

First, I agree the wording is too `kind' and I will change it.

However, the actual end result -- that the output of /dev/random can be
predicted -- is theoretical, because of the fact the output is one-way
hashed.  I do not want to get into another full discussion of this, but
a thread exists about a month back that discussed it.

To summarize: the patch is a risk if and only if: SHA-1 is cracked, an
attacker can observe the state of network packets, and it contributes
enough to the pool that enough of its state is learned.

Some diskless machines really have _zero_ entropy.  This patch does them
good.  If you don't want net devices to contribute, don't enable them.

Hell, without this patch, some network devices do contribute!  It
actually standardizes the whole thing.

> A failed RNG is one of the most likely -- and most devastating
> -- failure modes possible for modern cryptosystems, and as such
> it pays to be careful here.  Are you proposing this for inclusion
> in the mainline Linux kernel?  If so, I'm concerned that this patch
> could put security at risk.  What am I missing?

I would like to see it in the kernel, for 2.5.

I agree a poor RNG is a risk.  I also put forth that this patch is
configurable and that WITHOUT it you actually have a few NIC drivers
that contribute to /dev/random!

If you are on a diskless system and need some seed for whatever, say SSH
or just something dinky, this patch may be a requirement.  Even on my
workstation, sometimes SSH blocks for some seconds waiting for
/dev/random to fill up.

> Here is my reasoning.  I'd like to quote drivers/char/random.c:
>   * add_interrupt_randomness() uses the inter-interrupt timing as random
>   * inputs to the entropy pool.  Note that not all interrupts are good
>   * sources of randomness!  For example, the timer interrupts is not a
>   * good choice, because the periodicity of the interrupts is too
>   * regular, and hence predictable to an attacker.  Disk interrupts are
>   * a better measure, since the timing of the disk interrupts are more
>   * unpredictable.
>   * 
>   * All of these routines try to estimate how many bits of randomness a
>   * particular randomness source.  They do this by keeping track of the
>   * first and second order deltas of the event timings.

Obviously the timer interrupt would be the worst idea ever.  Its the
same value (HZ) on almost all versions of Linux (Alpha being on example
where it is not the same).

> This suggests that add_interrupt_randomness() should not be called
> on all interrupts without discrimination.  Would it be fair to say
> that your patch enables randomness collection on almost all interrupts?
> If so, why is this safe to do?

No, it enables them on the interrupt for your NIC, if you enable the
config setting.  If you don't enable the config, then your NIC won't
contribute.

If you don't use the patch, then it is unknown what your NIC will do
without looking at the source.  A few drivers currently do set
SA_SAMPLE_RANDOM.

> I hope you agree that making this configurable does not obviate our
> responsibility to make sure that the default configuration is reasonable.

The default is off.

> This stuff is subtle, and changing it is not something I'd recommend
> without a careful analysis of the impact on security.  Moreover, the
> comments about 'completely theoretical' leave me concerned that any
> analysis that has been done on this patch may be based on misconceptions
> about cryptographic security.  Can you tell us anything about what
> security analysis you've done so far?

This patch has been around, and a consensus has been reached.  I also
understand the mathematics here.

Again:
Yes, network traffic can be observed (or even influenced) and this could
allow an attacker to learn something of the state of your entropy pool. 
Note this would be hard due to clock skews and the such, but yes it is
entirely possible.

Yes, if your pool has little other entropy, the attacker now knows of a
good deal of the state of the pool.

Yes, assuming SHA-1 is at risk, the attacker can know for SURE the
output of /dev/random.

Yes, this is completely theoretical.  Is this a risk? Sure. But it
defaults to off.  Sitting on my home network and knowing the strength of
SHA-1, I don't fear any of this.  Or maybe I am desperate for entropy
since I have no disks or user interaction on this system.  The patch was
written out of a discussion for it, even.

Thus, the patch defaults to off.  By all means, keep it off.  I agree
the wording should be made more intimidating.  However, without this
patch, your NIC could be contributing -- the patch actually institutes a
policy.

That is all this is.  Having a clearly defined policy, and letting it be
configurable.

-- 
Robert M. Love
rml at ufl.edu
rml at tech9.net


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH][RFC] Allow net devices to contribute to /dev/random
  2001-09-26  0:52   ` Robert Love
@ 2001-09-26  1:36     ` David Wagner
  2001-09-26 22:55       ` Gordon Oliver
  2001-09-26 15:49     ` dean gaudet
                       ` (2 subsequent siblings)
  3 siblings, 1 reply; 22+ messages in thread
From: David Wagner @ 2001-09-26  1:36 UTC (permalink / raw)
  To: linux-kernel

Ok.  Since the default is 'off', I agree that this patch is unlikely
to lead to cryptographic failures in the 'off' setting.  At worst,
/dev/random will block forever, or more likely, block longer than it
otherwise would.  There is still a question in my mind about whether, by
turning off all network collection (and thus doing less entropy collection
than today's kernels do), this patch might be making the situation worse
for the average user.  However, I agree that at least this is fail-safe,
not fail-open, which is a useful property.

I certainly agree that insufficient entropy is indeed an important
problem for some installations.  However, I must admit that am not yet
convinced that this patch is the right solution for those installations:
it seems that the cure might well be worse than the disease in some cases.
I'll give some further discussion about my concerns below.  But before
I do, let me be clear about the worst case: With this patch set to
'on', the system might silently fail open and become insecure, but with
this patch in the default 'off' setting, at worst it just freezes, if
I understand correctly.  In other words, the reservations about being
too generous with the entropy counter only apply to the non-default 'on'
setting, so the rest of my comments only apply to this limited setting
where the user has already been warned.


Robert Love  wrote:
>On Tue, 2001-09-25 at 20:20, David Wagner wrote:
>> Incrementing the entropy counter based on externally observable
>> values is dangerous.
>
>However, the actual end result -- that the output of /dev/random can be
>predicted -- is theoretical, because of the fact the output is one-way
>hashed.

I didn't follow this part.  One concern is that, if we are too
generous with the entropy count, _all_ of the inputs to the randomness
pool might be known or predictable.  In this situation, the SHA
hash doesn't help at all.  I don't know how likely it is that this
situation will arise -- once we get 160 bits of good randomness
into the pool, we're safe as long as SHA is secure -- but the
whole reason that /dev/random exists is to be extremely conservative
about security.  Being overly generous with the entropy count invalidates
the warranty on /dev/random.  And for those who don't need an extremely
conservative solution, there's no reason to use /dev/random in the
first place: just grab 160 good bits and use /dev/urandom from then on.

>I do not want to get into another full discussion of this, but
>a thread exists about a month back that discussed it.

Yes, I followed that thread, and would not have posted my note about
this revision of the patch if I did not still have some questions.

>To summarize: the patch is a risk if and only if: SHA-1 is cracked, an
>attacker can observe the state of network packets, and it contributes
>enough to the pool that enough of its state is learned.

Well, maybe I could suggest that this statement should be extended
a bit.  Your statement is absolutely correct if we have gotten 160
good bits of true randomness into the pool at some point since booting,
but it is not quite correct if all of the inputs to /dev/random since
booting were predictable.

Thus, it might be more accurate to say that /dev/random is at risk
if SHA-1 is cracked, OR if the attacker can observe or predict all
the inputs to the randomness pool, OR if (SHA is cracked and the
attacker can observe or control the state of network packets and
these packets contribute enough to the pool).  Any of these three
cases are sufficient to invalidate security.

One concern might be that the patch could increase the risk of the
second failure mode by being overly generous with the entropy count
when in the 'on' setting.  /dev/random wasn't designed to be secure
when we feed it predictable values to add_interrupt_randomness(), and
it seems unreasonable to expect it to be reliably safe in this scenario.

>Some diskless machines really have _zero_ entropy.  This patch does them
>good.  If you don't want net devices to contribute, don't enable them.

Again, one concern is that for some devices, this patch might be
making the problem worse by turning off entropy collection for a
number of network devices.

>Hell, without this patch, some network devices do contribute!  It
>actually standardizes the whole thing.

Yes, but my understanding is that the devices that do contribute in
the current kernel were chosen according to some security analysis,
to ensure that they actually provide at least as much entropy as the
/dev/random entropy estimator expects.  My impression was that Ted
Ts'o had looked at this pretty carefully.  Was this impression incorrect?
Has anyone done a similar security analysis for the devices this
patch enables?

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH][RFC] Allow net devices to contribute to /dev/random
  2001-09-26  0:52   ` Robert Love
  2001-09-26  1:36     ` David Wagner
@ 2001-09-26 15:49     ` dean gaudet
  2001-09-26 17:00     ` Oliver Xymoron
  2001-10-01 14:43     ` Pavel Machek
  3 siblings, 0 replies; 22+ messages in thread
From: dean gaudet @ 2001-09-26 15:49 UTC (permalink / raw)
  To: Robert Love; +Cc: David Wagner, linux-kernel

On 25 Sep 2001, Robert Love wrote:

> Some diskless machines really have _zero_ entropy.

have you considered the Network Randomization Protocol?

http://www.usenix.org/publications/library/proceedings/security95/full_papers/chow.txt

-dean


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH][RFC] Allow net devices to contribute to /dev/random
  2001-09-26  0:52   ` Robert Love
  2001-09-26  1:36     ` David Wagner
  2001-09-26 15:49     ` dean gaudet
@ 2001-09-26 17:00     ` Oliver Xymoron
  2001-10-01 14:43     ` Pavel Machek
  3 siblings, 0 replies; 22+ messages in thread
From: Oliver Xymoron @ 2001-09-26 17:00 UTC (permalink / raw)
  To: Robert Love; +Cc: David Wagner, linux-kernel

On 25 Sep 2001, Robert Love wrote:

> On Tue, 2001-09-25 at 20:20, David Wagner wrote:
> > I'm worried about the language in the configuration documentation:
> >   +  Some people, however, feel that network devices should not contribute to
> >   +  /dev/random because an external attacker could observe incoming packets
> >   +  in an attempt to learn the entropy pool's state. Note this is completely
> >   +  theoretical.
> > Incrementing the entropy counter based on externally observable
> > values is dangerous.  Calling this risk 'completely theoretical'
> > is, I believe, a misrepresentation, unless I've missed something.
>
> First, I agree the wording is too `kind' and I will change it.
>
> However, the actual end result -- that the output of /dev/random can be
> predicted -- is theoretical, because of the fact the output is one-way
> hashed.

Explain to me how you can simultaneously prefer /dev/random for its purely
theoretical advantages and yet want to throw exactly that advantage out
the window?

What is really called for is the addition of potential network randomness
without crediting it with entropy. And using /dev/urandom, now exactly as
strong as the /dev/random you propose[1]. And acceptable to everyone.

> Some diskless machines really have _zero_ entropy.

Then we shouldn't be pretending that they can generate secure random
numbers.


[1] Assume an SSL webserver with only network interrupts as a source of
entropy. If entropy is a bottleneck, you're in a catch-22: you must wait
for more traffic to generate keys, but getting more traffic depends on
generating said keys. Blocking = dropping connections = not handling n% of
load at all load levels. Thus, the application demands entropy produced is
greater than entropy consumed.

Assuming that's the case and you'd be able to run a system with
/dev/random+network "entropy", it will work just as well with
/dev/urandom+network "entropy". Blocking buys us nothing if entropy is
proportional to load and capacity is proportional to entropy.

--
 "Love the dolphins," she advised him. "Write by W.A.S.T.E.."


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH][RFC] Allow net devices to contribute to /dev/random
  2001-09-26  1:36     ` David Wagner
@ 2001-09-26 22:55       ` Gordon Oliver
  2001-09-26 23:06         ` Andreas Steinmetz
  0 siblings, 1 reply; 22+ messages in thread
From: Gordon Oliver @ 2001-09-26 22:55 UTC (permalink / raw)
  To: David Wagner; +Cc: linux-kernel

Hi,
  I would actually suggest breaking out a longer discussion of the
risks associated with the patch into Documentation, and _strongly_
suggesting that the person configuring the kernel read it before
enabling the option. Then a statement that this may put the security
of /dev/random at risk would probably be enough.

  So let's start with the possible conditions are.
What I can see are the following:
   1) network card on visible net:
	- BadGuy can monitor the network traffic, extracting
	  timing information. Given enough knowledge about the
	  latency of the network card, all network entropy might
	  be known (making it non-secure). If the network is
	  the only, or primary, source of entropy this leads
	  to compromise
   2) network card on private net:
	- BadGuy must plug into private net to monitor the traffic,
	  any external monitoring is very likely to fail to get
	  much useful information.
   3) TSC not used to add randomness:
	- Prediction of time between interrupts becomes much easier
	  (jiffies are a big target).
   4) Systems that are largely quiescent could lead to easier prediction
      of latencies, and thus easier compromise.

In any case the following must be true for this to cause problems:
   a) The network must be the primary source of entropy (this
      will be common in the case where the patch is useful)
   b) BadGuy must monitor from time 0 (boot of system) to get
      useful information
   c) BadGuy must have information about what network card the system
      has, or _very_ good statistical information about delay to
      interrupt & timing in general.
   d) BadGuy must have information about how long the processing for
      the interrupt handler takes, as the randomness addition is done
      _after_ all processing. This also causes interesting problems
      for prediction if more than one event is handled at once.
   e) BadGuy must have access to information of network traffic on
      all the networks that are attached to the computer.

Now none of this guarantees security (but then again, very little will
_guarantee_ security.

I may have missed some stuff here... (caveat emptor)

Just as a comment, I actually like the patch, and would certainly be
willing to use it for a computer on a private network...
	-gordo

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH][RFC] Allow net devices to contribute to /dev/random
  2001-09-26 22:55       ` Gordon Oliver
@ 2001-09-26 23:06         ` Andreas Steinmetz
  0 siblings, 0 replies; 22+ messages in thread
From: Andreas Steinmetz @ 2001-09-26 23:06 UTC (permalink / raw)
  To: linux-kernel

> In any case the following must be true for this to cause problems:
>    a) The network must be the primary source of entropy (this
>       will be common in the case where the patch is useful)
>    b) BadGuy must monitor from time 0 (boot of system) to get
>       useful information
>    c) BadGuy must have information about what network card the system
>       has, or _very_ good statistical information about delay to
>       interrupt & timing in general.
>    d) BadGuy must have information about how long the processing for
>       the interrupt handler takes, as the randomness addition is done
>       _after_ all processing. This also causes interesting problems
>       for prediction if more than one event is handled at once.
>    e) BadGuy must have access to information of network traffic on
>       all the networks that are attached to the computer.
> 
> Now none of this guarantees security (but then again, very little will
> _guarantee_ security.
> 
> I may have missed some stuff here... (caveat emptor)
> 

To be constructive: why not make this HMAC-SHA1? The key for HMAC can be fed
through a sysctl interface. This would mean that BadGuy would need knowledge of
the key and thus the key generation method, too, especially if the key is
replaced at varying times and independent of network traffic.


Andreas Steinmetz
D.O.M. Datenverarbeitung GmbH

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH][RFC] Allow net devices to contribute to /dev/random
  2001-09-26  0:20 ` David Wagner
  2001-09-26  0:52   ` Robert Love
@ 2001-10-01  9:52   ` Florian Weimer
  2001-10-01 16:59     ` /dev/random entropy calculations broken? Andreas Dilger
  1 sibling, 1 reply; 22+ messages in thread
From: Florian Weimer @ 2001-10-01  9:52 UTC (permalink / raw)
  To: linux-kernel

daw@mozart.cs.berkeley.edu (David Wagner) writes:

> Incrementing the entropy counter based on externally observable
> values is dangerous.

How do you want to collect any entropy with such a requirement in
place?  Computers tend to send out a lot of information on the air.

BTW, I still think that the entropy estimate for mouse movements is
much too high.  And the compression function used probably doesn't
have the intended property.

-- 
Florian Weimer 	                  Florian.Weimer@RUS.Uni-Stuttgart.DE
University of Stuttgart           http://cert.uni-stuttgart.de/
RUS-CERT                          +49-711-685-5973/fax +49-711-685-5898

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH][RFC] Allow net devices to contribute to /dev/random
  2001-09-26  0:52   ` Robert Love
                       ` (2 preceding siblings ...)
  2001-09-26 17:00     ` Oliver Xymoron
@ 2001-10-01 14:43     ` Pavel Machek
  2001-10-01 21:33       ` Robert Love
  3 siblings, 1 reply; 22+ messages in thread
From: Pavel Machek @ 2001-10-01 14:43 UTC (permalink / raw)
  To: Robert Love, David Wagner; +Cc: linux-kernel

Hi!

> > Here is my reasoning.  I'd like to quote drivers/char/random.c:
> >   * add_interrupt_randomness() uses the inter-interrupt timing as random
> >   * inputs to the entropy pool.  Note that not all interrupts are good
> >   * sources of randomness!  For example, the timer interrupts is not a
> >   * good choice, because the periodicity of the interrupts is too
> >   * regular, and hence predictable to an attacker.  Disk interrupts are
> >   * a better measure, since the timing of the disk interrupts are more
> >   * unpredictable.
> >   * 
> >   * All of these routines try to estimate how many bits of randomness a
> >   * particular randomness source.  They do this by keeping track of the
> >   * first and second order deltas of the event timings.
> 
> Obviously the timer interrupt would be the worst idea ever.  Its the
> same value (HZ) on almost all versions of Linux (Alpha being on example
> where it is not the same).

Actually, not quite. On 2.4.9 system, console kept interrupts disabled
for so long that timer interrupt was pretty good source of randomness.

								Pavel
-- 
I'm pavel@ucw.cz. "In my country we have almost anarchy and I don't care."
Panos Katsaloulis describing me w.r.t. patents at discuss@linmodems.org

^ permalink raw reply	[flat|nested] 22+ messages in thread

* /dev/random entropy calculations broken?
  2001-10-01  9:52   ` Florian Weimer
@ 2001-10-01 16:59     ` Andreas Dilger
  2001-10-01 21:55       ` Alex Bligh - linux-kernel
  2001-10-02  7:51       ` Andreas Dilger
  0 siblings, 2 replies; 22+ messages in thread
From: Andreas Dilger @ 2001-10-01 16:59 UTC (permalink / raw)
  To: Florian Weimer; +Cc: linux-kernel, Theodore Ts'o

On Oct 01, 2001  11:52 +0200, Florian Weimer wrote:
> daw@mozart.cs.berkeley.edu (David Wagner) writes:
> > Incrementing the entropy counter based on externally observable
> > values is dangerous.
> 
> How do you want to collect any entropy with such a requirement in
> place?  Computers tend to send out a lot of information on the air.
>
> BTW, I still think that the entropy estimate for mouse movements is
> much too high.  And the compression function used probably doesn't
> have the intended property.

Has anyone even checked whether the current entropy estimates even work
properly?  I was testing this, and it appears something is terribly wrong.
Check /proc/sys/kernel/random/entropy_avail.  On a system that has been
running any length of time, it should be 4096 (512 bytes * 8 bits of
entropy for a full pool).

Now, "dd if=/dev/random bs=16 count=1 | wc -c" and check entropy_avail
again.  It "loses" thousands of bits of entropy for generating 16 bytes
(128 bits) of random data.  Same thing happens with /dev/urandom consuming
the available entropy.

Now if you do the above test on /dev/random several times in a row, you see
that you really HAVE used up the entropy, because it will return a number
of bytes less than what you requested.  At this point, however, it is at
least consistent, returning a number of bytes = entropy_avail/8.

Ted, any ideas about this?  I'm just looking through the code to see where
the entropy is counted, and where it goes.  It _may_ be a bug with the
entropy_avail value itself, but then why the short reads?  The output values
are at least consistent in that they grow slowly only when kb/mouse/disk
activity happens, and are constant otherwise.

This may be a major source of problems for entropy-poor environments, since
you will basically only be able to read a single random value from /dev/random
before the pool "dries up", regardless of the pool size (I tried with a
4096-byte pool, and the same problem happens).  Hence, in such systems there
would be more of a desire to use the "less secure" network interrupts for
entropy.

Cheers, Andreas

PS - For systems which have _some_ entropy, but just not very much, it is
     possible to increase the size of the pool (if it actually worked ;-)
     so that you can save entropy for periods of high demand.  You can do
     this by "echo 4096 > /proc/sys/kernel/random/poolsize" (or some other
     larger power-of-two size).
--
Andreas Dilger  \ "If a man ate a pound of pasta and a pound of antipasto,
                 \  would they cancel out, leaving him still hungry?"
http://www-mddsp.enel.ucalgary.ca/People/adilger/               -- Dogbert


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH][RFC] Allow net devices to contribute to /dev/random
  2001-10-01 14:43     ` Pavel Machek
@ 2001-10-01 21:33       ` Robert Love
  0 siblings, 0 replies; 22+ messages in thread
From: Robert Love @ 2001-10-01 21:33 UTC (permalink / raw)
  To: Pavel Machek; +Cc: David Wagner, linux-kernel

On Mon, 2001-10-01 at 10:43, Pavel Machek wrote:
> > Obviously the timer interrupt would be the worst idea ever.  Its the
> > same value (HZ) on almost all versions of Linux (Alpha being on example
> > where it is not the same).
> 
> Actually, not quite. On 2.4.9 system, console kept interrupts disabled
> for so long that timer interrupt was pretty good source of randomness.

That is pretty sad, to be honest :)

Besides, on some systems interrupts may rarely be disabled -- its too
hard to tell.  We don't want another config option, do we? :)

Also, 2.4.10 merged Andrew Morton's console-locking patch, so one can
hope the console's latency is improved.

-- 
Robert M. Love
rml at ufl.edu
rml at tech9.net


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: /dev/random entropy calculations broken?
  2001-10-01 16:59     ` /dev/random entropy calculations broken? Andreas Dilger
@ 2001-10-01 21:55       ` Alex Bligh - linux-kernel
  2001-10-01 22:43         ` antirez
  2001-10-02  7:51       ` Andreas Dilger
  1 sibling, 1 reply; 22+ messages in thread
From: Alex Bligh - linux-kernel @ 2001-10-01 21:55 UTC (permalink / raw)
  To: Andreas Dilger, Florian Weimer
  Cc: linux-kernel, Theodore Ts'o, Alex Bligh - linux-kernel



--On Monday, 01 October, 2001 10:59 AM -0600 Andreas Dilger 
<adilger@turbolabs.com> wrote:

> Has anyone even checked whether the current entropy estimates even work
> properly?

And while we're at it, many things which add to entropy are
observable by non-root users (interupt timings always, mouse
movements, keypresses if a non-root user is logged in at
the console). And entropy is overestimate on some non-x86
platforms due to lack of fine-grained timer implementations.
And without Robert Love's patch the choice of whether to source
it from network events is completely arbitrary (NIC dependent)

However, unless one is worried about someone having broken
SHA-1 OR one is worried about annoying blocking behavour
on read(), I'm not convinced the entropy calculation is
doing anything useful anyway.

People do, however, seem particularly reluctant to accept
any change in this area.

--
Alex Bligh

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: /dev/random entropy calculations broken?
  2001-10-01 21:55       ` Alex Bligh - linux-kernel
@ 2001-10-01 22:43         ` antirez
  0 siblings, 0 replies; 22+ messages in thread
From: antirez @ 2001-10-01 22:43 UTC (permalink / raw)
  To: Alex Bligh - linux-kernel
  Cc: Andreas Dilger, Florian Weimer, linux-kernel, Theodore Ts'o

On Mon, Oct 01, 2001 at 10:55:26PM +0100, Alex Bligh - linux-kernel wrote:
> However, unless one is worried about someone having broken
> SHA-1 OR one is worried about annoying blocking behavour
> on read(), I'm not convinced the entropy calculation is
> doing anything useful anyway.

I think the /dev/random /dev/urandom solution is perfect
from this point of view. Using /dev/random you can get
random number that are secure _even_ if tomorrow SHA1 will
be broken at the cost of a very slow generation.
Or you may trust SHA1 (or some other crypto primitive) to
avoid to collect too much entropy to generate the output.
Probably real world applications should use
/dev/urandom, assuming it's properly designed, i.e. the
internal state is changed once there is enough entropy
to create a new unguessable key, the entropy isn't
overstimated, and so on.

BTW I agree, instead to create a key being paranoid about
the SHA1 security it's better to double check if the
entropy source is ok. For example the linux PRNG used
to collect a lot of entropy bits from the keyboard
auto-repeat aaaaaaaaaaaaaa ...
It's useless to try to collect enough entropy (and maybe
overstimating it) to produce an output secure against
SHA1 possible weakness (i.e. totally generated with
entropy bits). It's probably better a more conservative
approach in the entropy collection and to use a belived secure
crypto primitive to produce the PRNG output.

After all, unless you are using a one-time pad probably
the key generated with /dev/random will be used with
the crypto algorithms you refused to trust.

-- 
Salvatore Sanfilippo <antirez@invece.org>
http://www.kyuzz.org/antirez
finger antirez@tella.alicom.com for PGP key
28 52 F5 4A 49 65 34 29 - 1D 1B F6 DA 24 C7 12 BF

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: /dev/random entropy calculations broken?
  2001-10-01 16:59     ` /dev/random entropy calculations broken? Andreas Dilger
  2001-10-01 21:55       ` Alex Bligh - linux-kernel
@ 2001-10-02  7:51       ` Andreas Dilger
  2001-10-02  8:10         ` Andreas Dilger
                           ` (2 more replies)
  1 sibling, 3 replies; 22+ messages in thread
From: Andreas Dilger @ 2001-10-02  7:51 UTC (permalink / raw)
  To: Florian Weimer, linux-kernel, Theodore Ts'o

On Oct 01, 2001  10:59 -0600, adilger wrote:
> Has anyone even checked whether the current entropy estimates even work
> properly?  I was testing this, and it appears something is terribly wrong.
> Check /proc/sys/kernel/random/entropy_avail.  On a system that has been
> running any length of time, it should be 4096 (512 bytes * 8 bits of
> entropy for a full pool).
> 
> Now, "dd if=/dev/random bs=16 count=1 | wc -c" and check entropy_avail
> again.  It "loses" thousands of bits of entropy for generating 16 bytes
> (128 bits) of random data.
> 
> Now if you do the above test on /dev/random several times in a row, you see
> that you really HAVE used up the entropy, because it will return a number
> of bytes less than what you requested.  At this point, however, it is at
> least consistent, returning a number of bytes = entropy_avail/8.

OK, after going through the code carefully, it appears that there are
several big problems in entropy accounting in drivers/char/random.c
which essentially mean that any time we read /dev/random or /dev/urandom
or /proc/sys/kernel/random/uuid we would entirely deplete the entropy pool.

While this in itself is not a security problem (i.e. we discard huge
amounts of entropy on each operation) it could be a problem for systems
that need entropy and don't want it wasted.  Similarly, it _may_ be a
factor in "inflating" entropy counts from sources, because we were
throwing so much away it wouldn't be usable without such high entropy
counts.  In my testing, be basically ALWAYS get 9-12 bits of entropy
from each keystroke, disk I/O, and (many, many) mouse interrupts, rather
than dribble in entropy in small bits at a time.


The below patch fixes these problems, at least to be sane in regards
to accounting of entropy in the system.  There are still some issues
to be discussed however.

First fix (minor): in batch_entropy_process() if the store had the maximum
  amount of entropy, the remaining entropy _appeared_ that it should go
  into the secondary store.  However, the test "entropy_count > max_entropy"
  could never be true (entropy_count is clamped in credit_entropy_store()).
  Fixed so we add entropy to secondary store until it is also full, and
  then alternate stirring in values into both stores (accumulates more
  entropy, and adds more randomness even if we don't account for entropy).

Second fix (minor): in batch_entropy_process() IF we switched to adding
  entropy to the secondary store, we credited this store with entropy
  that was actually added to the first store.  Also in this case we only
  woke up a reader if the _secondary_ store had enough entropy, and not
  it the _primary_ store had enough entropy (which is much more likely).

Third fix (minor): in xfer_secondary_pool() we extract entropy from the
  primary pool, even if the secondary pool is already full, if we request
  more entropy than can fit into the secondary pool.  The pool will be
  refilled anyways because it will be depleted after this action.  We now
  only refill the secondary pool if it is not already full (saving entropy).
  We could save even more entropy by only adding as much entropy as we need.

Fourth fix (minor): in xfer_secondary_pool() we credit the secondary pool
  with the correct number of BITS taken from the primary pool(*).  We were
  adding 1/4 of the number of bits taken from the primary to the secondary,
  and losing the other 3/4 of the entropy (byte/word mismatch?)(**).

Fifth fix: in extract_entropy() the "redundant but just in case" check was
  wrong, comparing entropy bit count and pool words.  This had the effect
  of losing 31/32 of the random pool on each access.  BAD, BAD program!


(*) It is my assumption that if you extract N bits of entropy from one
    pool, you add N bits into the other pool.  If this isn't true, then
    something needs to be fixed elsewhere.  I believe the problem is
    that extract_entropy() returns "nbytes" of data regardless of how
    much real entropy is in the pool, hence "creating" entropy where
    there was none in xfer_secondary_pool().  Either we should return
    the REAL amount of entropy backed data (and fix the urandom return
    values), or figure out some way to credit the secondary pool with
    only as much entropy as is really available.

(**) When using the SHA hash, we extract 5+80 words = 2720 bits from the
    primary pool and "add" it to the secondary pool.  However, the
    secondary pool is only 1024 bits in size, so we throw half of this
    entropy away each time (including extra CPU overhead).  Should we
    fix the secondary pool to be at least TMP_BUF_SIZE to hold this data?


The second patch is purely cosmetic fixes (added comments and such),
and also exports "generate_random_uuid()" for use in modules (I was
playing around with this in reiserfs as a module, and it needs to
be exported anyways, according to the comments above it).

Cheers, Andreas
===========================================================================
--- linux.orig/drivers/char/random.c	Tue Jul 10 17:05:24 2001
+++ linux/drivers/char/random.c	Tue Oct  2 00:17:40 2001
@@ -403,6 +403,12 @@
 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
 #endif
 
+#if 0
+#define DEBUG_ENT(fmt, arg...) printk(KERN_DEBUG "random: " fmt, ## arg)
+#else
+#define DEBUG_ENT(fmt, arg...) do {} while (0)
+#endif
+
 /*
  * Unfortunately, while the GCC optimizer for the i386 understands how
  * to optimize a static rotate left of x bits, it doesn't know how to
@@ -651,24 +670,23 @@
 
 static void batch_entropy_process(void *private_)
 {
-	int	num = 0;
-	int	max_entropy;
 	struct entropy_store *r	= (struct entropy_store *) private_, *p;
-	
+	int	max_entropy = r->poolinfo.poolwords*32;
+
 	if (!batch_max)
 		return;
 
-	max_entropy = r->poolinfo.poolwords*32;
+	p = r;
 	while (batch_head != batch_tail) {
+		if (r->entropy_count >= max_entropy) {
+			r = (r == sec_random_state) ?	random_state :
+							sec_random_state;
+		}
 		add_entropy_words(r, batch_entropy_pool + 2*batch_tail, 2);
-		p = r;
-		if (r->entropy_count > max_entropy && (num & 1))
-			r = sec_random_state;
 		credit_entropy_store(r, batch_entropy_credit[batch_tail]);
 		batch_tail = (batch_tail+1) & (batch_max-1);
-		num++;
 	}
-	if (r->entropy_count >= random_read_wakeup_thresh)
+	if (p->entropy_count >= random_read_wakeup_thresh)
 		wake_up_interruptible(&random_read_wait);
 }
 
@@ -1210,12 +1233,19 @@
 {
 	__u32	tmp[TMP_BUF_SIZE];
 
-	if (r->entropy_count < nbytes*8) {
+	if (r->entropy_count < nbytes*8 && r->entropy_count < r->poolsize*32) {
+		DEBUG_ENT("xfer %d from primary to %s (have %d need %d)\n",
+			  sizeof(tmp) * 8,
+			  r == sec_random_state ? "secondary" : "unknown",
+			  r->entropy_count, nbytes * 8);
 		extract_entropy(random_state, tmp, sizeof(tmp), 0);
 		add_entropy_words(r, tmp, TMP_BUF_SIZE);
-		credit_entropy_store(r, TMP_BUF_SIZE*8);
+		credit_entropy_store(r, TMP_BUF_SIZE*32);
 	}
 	if (r->extract_count > 1024) {
+		DEBUG_ENT("reseeding %s with %d from primary\n",
+			  r == sec_random_state ? "secondary" : "unknown",
+			  sizeof(tmp) * 8);
 		extract_entropy(random_state, tmp, sizeof(tmp), 0);
 		add_entropy_words(r, tmp, TMP_BUF_SIZE);
 		r->extract_count = 0;
@@ -1240,10 +1270,10 @@
 	__u32 x;
 
 	add_timer_randomness(&extract_timer_state, nbytes);
-	
+
 	/* Redundant, but just in case... */
-	if (r->entropy_count > r->poolinfo.poolwords) 
-		r->entropy_count = r->poolinfo.poolwords;
+	if (r->entropy_count > r->poolinfo.poolwords * 32)
+		r->entropy_count = r->poolinfo.poolwords * 32;
 
 	if (flags & EXTRACT_ENTROPY_SECONDARY)
 		xfer_secondary_pool(r, nbytes);



===========================================================================
--- linux.orig/drivers/char/random.c	Tue Jul 10 17:05:24 2001
+++ linux/drivers/char/random.c	Tue Oct  2 00:17:40 2001
@@ -272,8 +272,8 @@
 static int random_write_wakeup_thresh = 128;
 
 /*
- * A pool of size POOLWORDS is stirred with a primitive polynomial
- * of degree POOLWORDS over GF(2).  The taps for various sizes are
+ * A pool of size .poolwords is stirred with a primitive polynomial
+ * of degree .poolwords over GF(2).  The taps for various sizes are
  * defined below.  They are chosen to be evenly spaced (minimum RMS
  * distance from evenly spaced; the numbers in the comments are a
  * scaled squared error sum) except for the last tap, which is 1 to
@@ -552,11 +558,12 @@
 		0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 };
 	unsigned i;
 	int new_rotate;
+	int wordmask = r->poolinfo.poolwords - 1;
 	__u32 w;
 
 	while (num--) {
 		w = rotate_left(r->input_rotate, *in);
-		i = r->add_ptr = (r->add_ptr - 1) & (r->poolinfo.poolwords-1);
+		i = r->add_ptr = (r->add_ptr - 1) & wordmask;
 		/*
 		 * Normally, we add 7 bits of rotation to the pool.
 		 * At the beginning of the pool, add an extra 7 bits
@@ -569,11 +576,11 @@
 		r->input_rotate = new_rotate & 31;
 
 		/* XOR in the various taps */
-		w ^= r->pool[(i+r->poolinfo.tap1)&(r->poolinfo.poolwords-1)];
-		w ^= r->pool[(i+r->poolinfo.tap2)&(r->poolinfo.poolwords-1)];
-		w ^= r->pool[(i+r->poolinfo.tap3)&(r->poolinfo.poolwords-1)];
-		w ^= r->pool[(i+r->poolinfo.tap4)&(r->poolinfo.poolwords-1)];
-		w ^= r->pool[(i+r->poolinfo.tap5)&(r->poolinfo.poolwords-1)];
+		w ^= r->pool[(i + r->poolinfo.tap1) & wordmask];
+		w ^= r->pool[(i + r->poolinfo.tap2) & wordmask];
+		w ^= r->pool[(i + r->poolinfo.tap3) & wordmask];
+		w ^= r->pool[(i + r->poolinfo.tap4) & wordmask];
+		w ^= r->pool[(i + r->poolinfo.tap5) & wordmask];
 		w ^= r->pool[i];
 		r->pool[i] = (w >> 3) ^ twist_table[w & 7];
 	}
@@ -586,12 +593,20 @@
 {
 	int	max_entropy = r->poolinfo.poolwords*32;
 
-	if (r->entropy_count + num < 0)
+	if (r->entropy_count + num < 0) {
+		DEBUG_ENT("negative entropy/overflow (%d+%d)\n",
+			  r->entropy_count, num);
 		r->entropy_count = 0;
-	else if (r->entropy_count + num > max_entropy)
+	} else if (r->entropy_count + num > max_entropy) {
 		r->entropy_count = max_entropy;
-	else
-		r->entropy_count = r->entropy_count + num;
+	} else {
+		r->entropy_count += num;
+		if (num)
+			DEBUG_ENT("%s added %d bits, now %d\n",
+				  r == sec_random_state ? "secondary" :
+				  r == random_state ? "primary" : "unknown",
+				  num, r->entropy_count);
+	}
 }
 
 /**********************************************************************
@@ -627,6 +642,12 @@
 	return 0;
 }
 
+/*
+ * Changes to the entropy data are put into a queue rather than being added
+ * to the entropy counts directly.  This is presumably to avoid doing heavy
+ * hashing calculations during an interrupt in add_timer_randomness().
+ * Instead, the entropy is only added to the pool once per timer tick.
+ */
 void batch_entropy_store(u32 a, u32 b, int num)
 {
 	int	new;
@@ -643,12 +664,15 @@
 		queue_task(&batch_tqueue, &tq_timer);
 		batch_head = new;
 	} else {
-#if 0
-		printk(KERN_NOTICE "random: batch entropy buffer full\n");
-#endif
+		DEBUG_ENT("batch entropy buffer full\n");
 	}
 }
 
+/*
+ * Flush out the accumulated entropy operations, adding entropy to the passed
+ * store (normally random_state).  If that store has enough entropy, alternate
+ * between randomizing the data of the primary and secondary stores.
+ */
 static void batch_entropy_process(void *private_)
 {
 	struct entropy_store *r	= (struct entropy_store *) private_, *p;
@@ -1228,9 +1258,12 @@
  * bits of entropy are left in the pool, but it does not restrict the
  * number of bytes that are actually obtained.  If the EXTRACT_ENTROPY_USER
  * flag is given, then the buf pointer is assumed to be in user space.
- * If the EXTRACT_ENTROPY_SECONDARY flag is given, then this function will 
  *
- * Note: extract_entropy() assumes that POOLWORDS is a multiple of 16 words.
+ * If the EXTRACT_ENTROPY_SECONDARY flag is given, then we are actually
+ * extracting entropy from the secondary pool, and can refill from the
+ * primary pool if needed.
+ *
+ * Note: extract_entropy() assumes that .poolwords is a multiple of 16 words.
  */
 static ssize_t extract_entropy(struct entropy_store *r, void * buf,
 			       size_t nbytes, int flags)
@@ -1248,6 +1278,11 @@
 	if (flags & EXTRACT_ENTROPY_SECONDARY)
 		xfer_secondary_pool(r, nbytes);
 
+	DEBUG_ENT("%s has %d bits, want %d bits\n",
+		  r == sec_random_state ? "secondary" :
+		  r == random_state ? "primary" : "unknown",
+		  r->entropy_count, nbytes * 8);
+
 	if (r->entropy_count / 8 >= nbytes)
 		r->entropy_count -= nbytes*8;
 	else
@@ -2238,4 +2276,5 @@
 EXPORT_SYMBOL(add_interrupt_randomness);
 EXPORT_SYMBOL(add_blkdev_randomness);
 EXPORT_SYMBOL(batch_entropy_store);
+EXPORT_SYMBOL(generate_random_uuid);
 
--
Andreas Dilger  \ "If a man ate a pound of pasta and a pound of antipasto,
                 \  would they cancel out, leaving him still hungry?"
http://www-mddsp.enel.ucalgary.ca/People/adilger/               -- Dogbert


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: /dev/random entropy calculations broken?
  2001-10-02  7:51       ` Andreas Dilger
@ 2001-10-02  8:10         ` Andreas Dilger
  2001-10-02 15:37         ` Oliver Xymoron
  2001-10-19 22:59         ` [PATCH] " Andreas Dilger
  2 siblings, 0 replies; 22+ messages in thread
From: Andreas Dilger @ 2001-10-02  8:10 UTC (permalink / raw)
  To: Florian Weimer, linux-kernel, Theodore Ts'o

On Oct 02, 2001  01:51 -0600, Andreas Dilger wrote:
> -	if (r->entropy_count < nbytes*8) {
> +	if (r->entropy_count < nbytes*8 && r->entropy_count < r->poolsize*32) {
                                                                 ^^^^^^^^
Doh!  I hate it when I do that (compile, test, think, edit, send patch
without the compile, test steps).   Should be "poolinfo.poolwords".

Cheers, Andreas
--
Andreas Dilger  \ "If a man ate a pound of pasta and a pound of antipasto,
                 \  would they cancel out, leaving him still hungry?"
http://www-mddsp.enel.ucalgary.ca/People/adilger/               -- Dogbert


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: /dev/random entropy calculations broken?
  2001-10-02  7:51       ` Andreas Dilger
  2001-10-02  8:10         ` Andreas Dilger
@ 2001-10-02 15:37         ` Oliver Xymoron
  2001-10-02 21:02           ` Andreas Dilger
  2001-10-19 22:59         ` [PATCH] " Andreas Dilger
  2 siblings, 1 reply; 22+ messages in thread
From: Oliver Xymoron @ 2001-10-02 15:37 UTC (permalink / raw)
  To: Andreas Dilger; +Cc: linux-kernel, Theodore Y. Ts'o

On Tue, 2 Oct 2001, Andreas Dilger wrote:

> Fifth fix: in extract_entropy() the "redundant but just in case" check was
>   wrong, comparing entropy bit count and pool words.  This had the effect
>   of losing 31/32 of the random pool on each access.  BAD, BAD program!

> +	if (r->entropy_count > r->poolinfo.poolwords * 32)
> +		r->entropy_count = r->poolinfo.poolwords * 32;

Damnit, I read that line 30 times yesterday!

While we're on words/bytes/bits confusion, add_entropy_words() seems to
get called with number of bytes rather than words.

--
 "Love the dolphins," she advised him. "Write by W.A.S.T.E.."


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: /dev/random entropy calculations broken?
  2001-10-02 15:37         ` Oliver Xymoron
@ 2001-10-02 21:02           ` Andreas Dilger
  2001-10-02 21:29             ` Oliver Xymoron
  0 siblings, 1 reply; 22+ messages in thread
From: Andreas Dilger @ 2001-10-02 21:02 UTC (permalink / raw)
  To: Oliver Xymoron; +Cc: linux-kernel, Theodore Y. Ts'o

On Oct 02, 2001  10:37 -0500, Oliver Xymoron wrote:
> On Tue, 2 Oct 2001, Andreas Dilger wrote:
> > Fifth fix: in extract_entropy() the "redundant but just in case" check was
> >   wrong, comparing entropy bit count and pool words.  This had the effect
> >   of losing 31/32 of the random pool on each access.  BAD, BAD program!
> 
> > +	if (r->entropy_count > r->poolinfo.poolwords * 32)
> > +		r->entropy_count = r->poolinfo.poolwords * 32;
> 
> Damnit, I read that line 30 times yesterday!

So did I.  It wasn't till my system was spewing debugging output that I
saw where it was going.

> While we're on words/bytes/bits confusion, add_entropy_words() seems to
> get called with number of bytes rather than words.

Makes it that much more random, doesn't it ;-).  OK, here is a new version
of the patch.  It clears up the parameters to the functions, and makes sure
that we pass the right values to each.

Cheers, Andreas
===========================================================================
--- linux.orig/drivers/char/random.c	Tue Jul 10 17:05:24 2001
+++ linux/drivers/char/random.c	Tue Oct  2 14:50:19 2001
@@ -272,8 +272,8 @@
 static int random_write_wakeup_thresh = 128;
 
 /*
- * A pool of size POOLWORDS is stirred with a primitive polynomial
- * of degree POOLWORDS over GF(2).  The taps for various sizes are
+ * A pool of size .poolwords is stirred with a primitive polynomial
+ * of degree .poolwords over GF(2).  The taps for various sizes are
  * defined below.  They are chosen to be evenly spaced (minimum RMS
  * distance from evenly spaced; the numbers in the comments are a
  * scaled squared error sum) except for the last tap, which is 1 to
@@ -281,49 +281,47 @@
  */
 static struct poolinfo {
 	int	poolwords;
+	int	poolbits;	/* poolwords * 32 */
 	int	tap1, tap2, tap3, tap4, tap5;
 } poolinfo_table[] = {
 	/* x^2048 + x^1638 + x^1231 + x^819 + x^411 + x + 1  -- 115 */
-	{ 2048,	1638,	1231,	819, 	411,	1 },
+	{ 2048,	65536,	1638,	1231,	819,	411,	1 },
 
 	/* x^1024 + x^817 + x^615 + x^412 + x^204 + x + 1 -- 290 */
-	{ 1024,	817, 	615,	412,	204,	1 },
-
+	{ 1024,	32768,	817,	615,	412,	204,	1 },
 #if 0				/* Alternate polynomial */
 	/* x^1024 + x^819 + x^616 + x^410 + x^207 + x^2 + 1 -- 115 */
-	{ 1024,	819,	616,	410,	207,	2 },
+	{ 1024,	32768,	819,	616,	410,	207,	2 },
 #endif
-	
-	/* x^512 + x^411 + x^308 + x^208 + x^104 + x + 1 -- 225 */
-	{ 512,	411,	308,	208,	104,	1 },
 
+	/* x^512 + x^411 + x^308 + x^208 + x^104 + x + 1 -- 225 */
+	{ 512,	16384,	411,	308,	208,	104,	1 },
 #if 0				/* Alternates */
 	/* x^512 + x^409 + x^307 + x^206 + x^102 + x^2 + 1 -- 95 */
-	{ 512,	409,	307,	206,	102,	2 },
+	{ 512,	16384,	409,	307,	206,	102,	2 },
 	/* x^512 + x^409 + x^309 + x^205 + x^103 + x^2 + 1 -- 95 */
-	{ 512,	409,	309,	205,	103,	2 },
+	{ 512,	16384,	409,	309,	205,	103,	2 },
 #endif
 
 	/* x^256 + x^205 + x^155 + x^101 + x^52 + x + 1 -- 125 */
-	{ 256,	205,	155,	101,	52,	1 },
-	
-	/* x^128 + x^103 + x^76 + x^51 +x^25 + x + 1 -- 105 */
-	{ 128,	103,	76,	51,	25,	1 },
+	{ 256,	8192,	205,	155,	101,	52,	1 },
 
+	/* x^128 + x^103 + x^76 + x^51 +x^25 + x + 1 -- 105 */
+	{ 128,	4096,	103,	76,	51,	25,	1 },
 #if 0	/* Alternate polynomial */
 	/* x^128 + x^103 + x^78 + x^51 + x^27 + x^2 + 1 -- 70 */
-	{ 128,	103,	78,	51,	27,	2 },
+	{ 128,	4096,	103,	78,	51,	27,	2 },
 #endif
 
 	/* x^64 + x^52 + x^39 + x^26 + x^14 + x + 1 -- 15 */
-	{ 64,	52,	39,	26,	14,	1 },
+	{ 64,	2048,	52,	39,	26,	14,	1 },
 
 	/* x^32 + x^26 + x^20 + x^14 + x^7 + x + 1 -- 15 */
-	{ 32,	26,	20,	14,	7,	1 },
+	{ 32,	1024,	26,	20,	14,	7,	1 },
+
+	{ 0,	0,	0,	0,	0,	0,	0 },
+};
 
-	{ 0, 	0,	0,	0,	0,	0 },
-};		
-	
 /*
  * For the purposes of better mixing, we use the CRC-32 polynomial as
  * well to make a twisted Generalized Feedback Shift Reigster
@@ -461,6 +459,12 @@
 }
 #endif
 
+#if 0
+#define DEBUG_ENT(fmt, arg...) printk(KERN_DEBUG "random: " fmt, ## arg)
+#else
+#define DEBUG_ENT(fmt, arg...) do {} while (0)
+#endif
+
 /**********************************************************************
  *
  * OS independent entropy store.   Here are the functions which handle
@@ -545,18 +549,19 @@
  * the entropy is concentrated in the low-order bits.
  */
 static void add_entropy_words(struct entropy_store *r, const __u32 *in,
-			     int num)
+			      int nwords)
 {
 	static __u32 const twist_table[8] = {
 		         0, 0x3b6e20c8, 0x76dc4190, 0x4db26158,
 		0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 };
 	unsigned i;
 	int new_rotate;
+	int wordmask = r->poolinfo.poolwords - 1;
 	__u32 w;
 
-	while (num--) {
+	while (nwords--) {
 		w = rotate_left(r->input_rotate, *in);
-		i = r->add_ptr = (r->add_ptr - 1) & (r->poolinfo.poolwords-1);
+		i = r->add_ptr = (r->add_ptr - 1) & wordmask;
 		/*
 		 * Normally, we add 7 bits of rotation to the pool.
 		 * At the beginning of the pool, add an extra 7 bits
@@ -569,11 +574,11 @@
 		r->input_rotate = new_rotate & 31;
 
 		/* XOR in the various taps */
-		w ^= r->pool[(i+r->poolinfo.tap1)&(r->poolinfo.poolwords-1)];
-		w ^= r->pool[(i+r->poolinfo.tap2)&(r->poolinfo.poolwords-1)];
-		w ^= r->pool[(i+r->poolinfo.tap3)&(r->poolinfo.poolwords-1)];
-		w ^= r->pool[(i+r->poolinfo.tap4)&(r->poolinfo.poolwords-1)];
-		w ^= r->pool[(i+r->poolinfo.tap5)&(r->poolinfo.poolwords-1)];
+		w ^= r->pool[(i + r->poolinfo.tap1) & wordmask];
+		w ^= r->pool[(i + r->poolinfo.tap2) & wordmask];
+		w ^= r->pool[(i + r->poolinfo.tap3) & wordmask];
+		w ^= r->pool[(i + r->poolinfo.tap4) & wordmask];
+		w ^= r->pool[(i + r->poolinfo.tap5) & wordmask];
 		w ^= r->pool[i];
 		r->pool[i] = (w >> 3) ^ twist_table[w & 7];
 	}
@@ -582,16 +587,22 @@
 /*
  * Credit (or debit) the entropy store with n bits of entropy
  */
-static void credit_entropy_store(struct entropy_store *r, int num)
+static void credit_entropy_store(struct entropy_store *r, int nbits)
 {
-	int	max_entropy = r->poolinfo.poolwords*32;
-
-	if (r->entropy_count + num < 0)
+	if (r->entropy_count + nbits < 0) {
+		DEBUG_ENT("negative entropy/overflow (%d+%d)\n",
+			  r->entropy_count, nbits);
 		r->entropy_count = 0;
-	else if (r->entropy_count + num > max_entropy)
-		r->entropy_count = max_entropy;
-	else
-		r->entropy_count = r->entropy_count + num;
+	} else if (r->entropy_count + nbits > r->poolinfo.poolbits) {
+		r->entropy_count = r->poolinfo.poolbits;
+	} else {
+		r->entropy_count += nbits;
+		if (nbits)
+			DEBUG_ENT("%s added %d bits, now %d\n",
+				  r == sec_random_state ? "secondary" :
+				  r == random_state ? "primary" : "unknown",
+				  nbits, r->entropy_count);
+	}
 }
 
 /**********************************************************************
@@ -627,6 +638,12 @@
 	return 0;
 }
 
+/*
+ * Changes to the entropy data is put into a queue rather than being added to
+ * the entropy counts directly.  This is presumably to avoid doing heavy
+ * hashing calculations during an interrupt in add_timer_randomness().
+ * Instead, the entropy is only added to the pool once per timer tick.
+ */
 void batch_entropy_store(u32 a, u32 b, int num)
 {
 	int	new;
@@ -643,32 +660,33 @@
 		queue_task(&batch_tqueue, &tq_timer);
 		batch_head = new;
 	} else {
-#if 0
-		printk(KERN_NOTICE "random: batch entropy buffer full\n");
-#endif
+		DEBUG_ENT("batch entropy buffer full\n");
 	}
 }
 
+/*
+ * Flush out the accumulated entropy operations, adding entropy to the passed
+ * store (normally random_state).  If that store has enough entropy, alternate
+ * between randomizing the data of the primary and secondary stores.
+ */
 static void batch_entropy_process(void *private_)
 {
-	int	num = 0;
-	int	max_entropy;
 	struct entropy_store *r	= (struct entropy_store *) private_, *p;
-	
+
 	if (!batch_max)
 		return;
 
-	max_entropy = r->poolinfo.poolwords*32;
+	p = r;
 	while (batch_head != batch_tail) {
+		if (r->entropy_count >= r->poolinfo.poolbits) {
+			r = (r == sec_random_state) ?	random_state :
+							sec_random_state;
+		}
 		add_entropy_words(r, batch_entropy_pool + 2*batch_tail, 2);
-		p = r;
-		if (r->entropy_count > max_entropy && (num & 1))
-			r = sec_random_state;
 		credit_entropy_store(r, batch_entropy_credit[batch_tail]);
 		batch_tail = (batch_tail+1) & (batch_max-1);
-		num++;
 	}
-	if (r->entropy_count >= random_read_wakeup_thresh)
+	if (p->entropy_count >= random_read_wakeup_thresh)
 		wake_up_interruptible(&random_read_wait);
 }
 
@@ -1210,14 +1228,26 @@
 {
 	__u32	tmp[TMP_BUF_SIZE];
 
-	if (r->entropy_count < nbytes*8) {
-		extract_entropy(random_state, tmp, sizeof(tmp), 0);
-		add_entropy_words(r, tmp, TMP_BUF_SIZE);
-		credit_entropy_store(r, TMP_BUF_SIZE*8);
+	if (r->entropy_count < nbytes * 8 &&
+	    r->entropy_count < r->poolinfo.poolbits) {
+		int nwords = MIN(r->poolinfo.poolwords - r->entropy_count/32,
+				 sizeof(tmp) / 4);
+
+		DEBUG_ENT("xfer %d from primary to %s (have %d need %d)\n",
+			  nwords * 32,
+			  r == sec_random_state ? "secondary" : "unknown",
+			  r->entropy_count, nbytes * 8);
+
+		extract_entropy(random_state, tmp, nwords, 0);
+		add_entropy_words(r, tmp, nwords);
+		credit_entropy_store(r, nwords * 32);
 	}
 	if (r->extract_count > 1024) {
+		DEBUG_ENT("reseeding %s with %d from primary\n",
+			  r == sec_random_state ? "secondary" : "unknown",
+			  sizeof(tmp) * 8);
 		extract_entropy(random_state, tmp, sizeof(tmp), 0);
-		add_entropy_words(r, tmp, TMP_BUF_SIZE);
+		add_entropy_words(r, tmp, sizeof(tmp) / 4);
 		r->extract_count = 0;
 	}
 }
@@ -1228,9 +1258,12 @@
  * bits of entropy are left in the pool, but it does not restrict the
  * number of bytes that are actually obtained.  If the EXTRACT_ENTROPY_USER
  * flag is given, then the buf pointer is assumed to be in user space.
- * If the EXTRACT_ENTROPY_SECONDARY flag is given, then this function will 
  *
- * Note: extract_entropy() assumes that POOLWORDS is a multiple of 16 words.
+ * If the EXTRACT_ENTROPY_SECONDARY flag is given, then we are actually
+ * extracting entropy from the secondary pool, and can refill from the
+ * primary pool if needed.
+ *
+ * Note: extract_entropy() assumes that .poolwords is a multiple of 16 words.
  */
 static ssize_t extract_entropy(struct entropy_store *r, void * buf,
 			       size_t nbytes, int flags)
@@ -1240,14 +1273,19 @@
 	__u32 x;
 
 	add_timer_randomness(&extract_timer_state, nbytes);
-	
+
 	/* Redundant, but just in case... */
-	if (r->entropy_count > r->poolinfo.poolwords) 
-		r->entropy_count = r->poolinfo.poolwords;
+	if (r->entropy_count > r->poolinfo.poolbits)
+		r->entropy_count = r->poolinfo.poolbits;
 
 	if (flags & EXTRACT_ENTROPY_SECONDARY)
 		xfer_secondary_pool(r, nbytes);
 
+	DEBUG_ENT("%s has %d bits, want %d bits\n",
+		  r == sec_random_state ? "secondary" :
+		  r == random_state ? "primary" : "unknown",
+		  r->entropy_count, nbytes * 8);
+
 	if (r->entropy_count / 8 >= nbytes)
 		r->entropy_count -= nbytes*8;
 	else
@@ -1543,9 +1581,7 @@
 		c -= bytes;
 		p += bytes;
 
-		/* Convert bytes to words */
-		bytes = (bytes + 3) / sizeof(__u32);
-		add_entropy_words(random_state, buf, bytes);
+		add_entropy_words(random_state, buf, (bytes + 3) / 4);
 	}
 	if (p == buffer) {
 		return (ssize_t)ret;
@@ -1589,7 +1625,7 @@
 		ent_count = random_state->entropy_count;
 		if (put_user(ent_count, p++))
 			return -EFAULT;
-			
+
 		if (get_user(size, p))
 			return -EFAULT;
 		if (put_user(random_state->poolinfo.poolwords, p++))
@@ -1598,7 +1634,7 @@
 			return -EINVAL;
 		if (size > random_state->poolinfo.poolwords)
 			size = random_state->poolinfo.poolwords;
-		if (copy_to_user(p, random_state->pool, size*sizeof(__u32)))
+		if (copy_to_user(p, random_state->pool, size * 4))
 			return -EFAULT;
 		return 0;
 	case RNDADDENTROPY:
@@ -1845,8 +1881,7 @@
 {
 	min_read_thresh = 8;
 	min_write_thresh = 0;
-	max_read_thresh = max_write_thresh =
-		random_state->poolinfo.poolwords * 32;
+	max_read_thresh = max_write_thresh = random_state->poolinfo.poolbits;
 	random_table[1].data = &random_state->entropy_count;
 }
 #endif 	/* CONFIG_SYSCTL */
@@ -2238,4 +2273,5 @@
 EXPORT_SYMBOL(add_interrupt_randomness);
 EXPORT_SYMBOL(add_blkdev_randomness);
 EXPORT_SYMBOL(batch_entropy_store);
+EXPORT_SYMBOL(generate_random_uuid);
 
--
Andreas Dilger  \ "If a man ate a pound of pasta and a pound of antipasto,
                 \  would they cancel out, leaving him still hungry?"
http://www-mddsp.enel.ucalgary.ca/People/adilger/               -- Dogbert


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: /dev/random entropy calculations broken?
  2001-10-02 21:02           ` Andreas Dilger
@ 2001-10-02 21:29             ` Oliver Xymoron
  2001-10-02 22:28               ` Andreas Dilger
  0 siblings, 1 reply; 22+ messages in thread
From: Oliver Xymoron @ 2001-10-02 21:29 UTC (permalink / raw)
  To: Andreas Dilger; +Cc: linux-kernel, Theodore Y. Ts'o

On Tue, 2 Oct 2001, Andreas Dilger wrote:

> > While we're on words/bytes/bits confusion, add_entropy_words() seems to
> > get called with number of bytes rather than words.
>
> Makes it that much more random, doesn't it ;-).  OK, here is a new version
> of the patch.  It clears up the parameters to the functions, and makes sure
> that we pass the right values to each.

Cool. Not sure if I like the introduction of poolbits. My personal
preference would be to s/poolwords/words/ and just use ->words*32, since
foo->foomember is a throwback to pre-ANSI compilers with a flat namespace
for structure members. Note that we don't bother prefixing tap*.

If not, at least put poolbits in the structure first...

>  static struct poolinfo {
>  	int	poolwords;
> +	int	poolbits;	/* poolwords * 32 */
>  	int	tap1, tap2, tap3, tap4, tap5;
>  } poolinfo_table[] = {
>  	/* x^2048 + x^1638 + x^1231 + x^819 + x^411 + x + 1  -- 115 */
> -	{ 2048,	1638,	1231,	819, 	411,	1 },
> +	{ 2048,	65536,	1638,	1231,	819,	411,	1 },
                ^^^^^
...because it's not as confusing comparing the polynomial in the comment
to the initializer.

--
 "Love the dolphins," she advised him. "Write by W.A.S.T.E.."


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: /dev/random entropy calculations broken?
  2001-10-02 21:29             ` Oliver Xymoron
@ 2001-10-02 22:28               ` Andreas Dilger
  0 siblings, 0 replies; 22+ messages in thread
From: Andreas Dilger @ 2001-10-02 22:28 UTC (permalink / raw)
  To: Oliver Xymoron; +Cc: linux-kernel, Theodore Y. Ts'o

On Oct 02, 2001  16:29 -0500, Oliver Xymoron wrote:
> Cool. Not sure if I like the introduction of poolbits. My personal
> preference would be to s/poolwords/words/ and just use ->words*32, since
> foo->foomember is a throwback to pre-ANSI compilers with a flat namespace
> for structure members. Note that we don't bother prefixing tap*.

I added poolbits because we were doing poolwords * 32 all the time in the
commonly called functions credit_entropy_store() and batch_entropy_process()).
I don't really care either way, except that it makes the code easier to read.

We could always do the following (hackish, but makes code more readable):

#define POOLBITS	poolwords*32
#define POOLBYTES 	poolwords*4

> If not, at least put poolbits in the structure first...
> 
> >  static struct poolinfo {
> >  	int	poolwords;
> > +	int	poolbits;	/* poolwords * 32 */
> >  	int	tap1, tap2, tap3, tap4, tap5;
> >  } poolinfo_table[] = {
> >  	/* x^2048 + x^1638 + x^1231 + x^819 + x^411 + x + 1  -- 115 */
> > -	{ 2048,	1638,	1231,	819, 	411,	1 },
> > +	{ 2048,	65536,	1638,	1231,	819,	411,	1 },
>                 ^^^^^
> ...because it's not as confusing comparing the polynomial in the comment
> to the initializer.

Sorry, I didn't notice that the poolwords was also part of the polynomial.
I'll wait a while before reposting in case of more comments (Ted has been
silent thus far).

Cheers, Andreas
--
Andreas Dilger  \ "If a man ate a pound of pasta and a pound of antipasto,
                 \  would they cancel out, leaving him still hungry?"
http://www-mddsp.enel.ucalgary.ca/People/adilger/               -- Dogbert


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH] Re: /dev/random entropy calculations broken?
  2001-10-02  7:51       ` Andreas Dilger
  2001-10-02  8:10         ` Andreas Dilger
  2001-10-02 15:37         ` Oliver Xymoron
@ 2001-10-19 22:59         ` Andreas Dilger
  2001-10-21  5:05           ` Robert Love
  2 siblings, 1 reply; 22+ messages in thread
From: Andreas Dilger @ 2001-10-19 22:59 UTC (permalink / raw)
  To: Theodore Ts'o, Alan Cox, torvalds; +Cc: linux-kernel

I never heard back from Ted on this issue, but it is plain to see that
entropy accounting for /dev/random is broken.  Linus, Alan, please apply
the new patch.

Testing for broken entropy accounting (in kernel debugging verifies this):

$cat /proc/sys/kernel/random/entropy_count	# current entropy count in bits
4096
$dd if=/dev/random bs=64 count=1 | wc -c	# should get 64 bytes back, and
1+0 records in					# reduce entropy count by 512
1+0 records out
     64
$cat /proc/sys/kernel/random/entropy_count	# the entropy is all gone!
81
$dd if=/dev/random bs=64 count=1 | wc -c	# we don't get 64 bytes back
0+1 records in
0+1 records out
     24


In my testing, the second "dd" returns a short read because the entropy has
disappeared because of bad accounting.

Attached are two patches - the first corrects the real bugs in the accounting,
and the second updates comments, cleans up the code a bit (no functional
changes just debugging and clarifying where we are using bits/bytes/ words
as quantities, which was the source of the original bug), removes some
extraneous whitespace, etc.

Note that I have NOT changed anything in the way that random data values are
calculated in either patch, only making the "accounting" of the entropy sane.

Cheers, Andreas

On Oct 02, 2001  01:51 -0600, adilger wrote:
> OK, after going through the code carefully, it appears that there are
> several big problems in entropy accounting in drivers/char/random.c
> which essentially mean that any time we read /dev/random or /dev/urandom
> or /proc/sys/kernel/random/uuid we would entirely deplete the entropy pool.
> 
> While this in itself is not a security problem (i.e. we discard huge
> amounts of entropy on each operation) it could be a problem for systems
> that are entropy poor and don't want it wasted.
> 
> 
> The below patch fixes these problems, at least to be sane in regards
> to accounting of entropy in the system.  There are still some issues
> to be discussed however.  Patch hunk descriptions with severity of bug:
> 
> First fix (small): in batch_entropy_process() if the store had the maximum
>   amount of entropy, the remaining entropy _appeared_ that it should go
>   into the secondary store.  However, the test "entropy_count > max_entropy"
>   could never be true (entropy_count is clamped in credit_entropy_store()).
>   Fixed so we add entropy to secondary store until it is also full, and
>   then alternate stirring in values into both stores (accumulates more
>   entropy, and adds more randomness even if we don't account for entropy).
> 
> Second fix (small): in batch_entropy_process() IF we switched to adding
>   entropy to the secondary store, we credited this store with entropy
>   that was actually added to the first store.  Also in this case we only
>   woke up a reader if the _secondary_ store had enough entropy, and not
>   it the _primary_ store had enough entropy (which is much more likely,
>   since we are adding entropy to the primary store first).
> 
> Third fix (small): in xfer_secondary_pool() we extract entropy from the
>   primary pool to add to the secondary pool, even if the secondary pool
>   is already full, if we make a large request.  The pool will be refilled
>   anyways because it will be depleted after this action.  We now only
>   refill the secondary pool if it is not already full (saving entropy).
> 
> Fourth fix (medium): in xfer_secondary_pool() we credit the secondary pool
>   with the correct number of BITS taken from the primary pool(*).  We were
>   adding 1/4 of the number of bits taken from the primary to the secondary,
>   and losing the other 3/4 of the entropy (byte/word mismatch?)(**).
> 
> Fifth fix (LARGE): in extract_entropy() the "redundant but just in case"
>   check was wrong, comparing entropy BITS and pool WORDS.  This had
>   the effect of losing 31/32 of the random pool each access.  BAD program!
> 
> 
> (*) It is my assumption that if you extract N bits of entropy from one
>     pool, you add N bits into the other pool.  If this isn't true, then
>     something needs to be fixed elsewhere.  I believe the problem is
>     that extract_entropy() returns "nbytes" of data regardless of how
>     much real entropy is in the pool, hence "creating" entropy where
>     there was none in xfer_secondary_pool().  Either we should return
>     the REAL amount of entropy backed data (and fix the urandom return
>     values), or figure out some way to credit the secondary pool with
>     only as much entropy as is really available.
> 
> (**) When using the SHA hash, we extract 5+80 words = 2720 bits from the
>     primary pool and "add" it to the secondary pool.  However, the
>     secondary pool is only 1024 bits in size, so we throw half of this
>     entropy away each time (including extra CPU overhead).  Should we
>     fix the secondary pool to be at least TMP_BUF_SIZE to hold this data?

===========================================================================
--- linux.orig/drivers/char/random.c	Tue Jul 10 17:05:24 2001
+++ linux/drivers/char/random.c	Fri Oct 19 14:41:07 2001
@@ -321,9 +325,12 @@
 	/* x^32 + x^26 + x^20 + x^14 + x^7 + x + 1 -- 15 */
 	{ 32,	26,	20,	14,	7,	1 },
 
-	{ 0, 	0,	0,	0,	0,	0 },
-};		
-	
+	{ 0,	0,	0,	0,	0,	0 },
+};
+
+#define POOLBITS	poolwords*32
+#define POOLBYTES	poolwords*4
+
 /*
  * For the purposes of better mixing, we use the CRC-32 polynomial as
  * well to make a twisted Generalized Feedback Shift Reigster
@@ -461,6 +468,12 @@
 }
 #endif
 
+#if 0
+#define DEBUG_ENT(fmt, arg...) printk(KERN_DEBUG "random: " fmt, ## arg)
+#else
+#define DEBUG_ENT(fmt, arg...) do {} while (0)
+#endif
+
 /**********************************************************************
  *
  * OS independent entropy store.   Here are the functions which handle
@@ -649,26 +673,31 @@
 	}
 }
 
+/*
+ * Flush out the accumulated entropy operations, adding entropy to the passed
+ * store (normally random_state).  If that store has enough entropy, alternate
+ * between randomizing the data of the primary and secondary stores.
+ */
 static void batch_entropy_process(void *private_)
 {
-	int	num = 0;
-	int	max_entropy;
 	struct entropy_store *r	= (struct entropy_store *) private_, *p;
-	
+	int max_entropy = r->poolinfo.POOLBITS;
+
 	if (!batch_max)
 		return;
 
-	max_entropy = r->poolinfo.poolwords*32;
+	p = r;
 	while (batch_head != batch_tail) {
+		if (r->entropy_count >= max_entropy) {
+			r = (r == sec_random_state) ?	random_state :
+							sec_random_state;
+			max_entropy = r->poolinfo.POOLBITS;
+		}
 		add_entropy_words(r, batch_entropy_pool + 2*batch_tail, 2);
-		p = r;
-		if (r->entropy_count > max_entropy && (num & 1))
-			r = sec_random_state;
 		credit_entropy_store(r, batch_entropy_credit[batch_tail]);
 		batch_tail = (batch_tail+1) & (batch_max-1);
-		num++;
 	}
-	if (r->entropy_count >= random_read_wakeup_thresh)
+	if (p->entropy_count >= random_read_wakeup_thresh)
 		wake_up_interruptible(&random_read_wait);
 }
 
@@ -1210,14 +1239,26 @@
 {
 	__u32	tmp[TMP_BUF_SIZE];
 
-	if (r->entropy_count < nbytes*8) {
-		extract_entropy(random_state, tmp, sizeof(tmp), 0);
-		add_entropy_words(r, tmp, TMP_BUF_SIZE);
-		credit_entropy_store(r, TMP_BUF_SIZE*8);
+	if (r->entropy_count < nbytes * 8 &&
+	    r->entropy_count < r->poolinfo.POOLBITS) {
+		int nwords = min(r->poolinfo.poolwords - r->entropy_count/32,
+				 sizeof(tmp) / 4);
+
+		DEBUG_ENT("xfer %d from primary to %s (have %d, need %d)\n",
+			  nwords * 32,
+			  r == sec_random_state ? "secondary" : "unknown",
+			  r->entropy_count, nbytes * 8);
+
+		extract_entropy(random_state, tmp, nwords, 0);
+		add_entropy_words(r, tmp, nwords);
+		credit_entropy_store(r, nwords * 32);
 	}
 	if (r->extract_count > 1024) {
+		DEBUG_ENT("reseeding %s with %d from primary\n",
+			  r == sec_random_state ? "secondary" : "unknown",
+			  sizeof(tmp) * 8);
 		extract_entropy(random_state, tmp, sizeof(tmp), 0);
-		add_entropy_words(r, tmp, TMP_BUF_SIZE);
+		add_entropy_words(r, tmp, sizeof(tmp) / 4);
 		r->extract_count = 0;
 	}
 }
@@ -1240,10 +1284,10 @@
 	__u32 x;
 
 	add_timer_randomness(&extract_timer_state, nbytes);
-	
+
 	/* Redundant, but just in case... */
-	if (r->entropy_count > r->poolinfo.poolwords) 
-		r->entropy_count = r->poolinfo.poolwords;
+	if (r->entropy_count > r->poolinfo.POOLBITS)
+		r->entropy_count = r->poolinfo.POOLBITS;
 
 	if (flags & EXTRACT_ENTROPY_SECONDARY)
 		xfer_secondary_pool(r, nbytes);
@@ -2238,4 +2284,5 @@
 EXPORT_SYMBOL(add_interrupt_randomness);
 EXPORT_SYMBOL(add_blkdev_randomness);
 EXPORT_SYMBOL(batch_entropy_store);
+EXPORT_SYMBOL(generate_random_uuid);
 
===========================================================================
--- linux.orig/drivers/char/random.c	Tue Jul 10 17:05:24 2001
+++ linux/drivers/char/random.c	Fri Oct 19 14:41:07 2001
@@ -164,25 +164,32 @@
  * sequence: 
  *
  *	echo "Initializing random number generator..."
- * 	random_seed=/var/run/random-seed
+ *	random_seed=/var/run/random-seed
  *	# Carry a random seed from start-up to start-up
- *	# Load and then save 512 bytes, which is the size of the entropy pool
- * 	if [ -f $random_seed ]; then
+ *	# Load and then save the whole entropy pool
+ *	if [ -f $random_seed ]; then
  *		cat $random_seed >/dev/urandom
- * 	fi
- *	dd if=/dev/urandom of=$random_seed count=1
- * 	chmod 600 $random_seed
+ *	else
+ *		touch $random_seed
+ *	fi
+ *	chmod 600 $random_seed
+ *	poolfile=/proc/sys/kernel/random/poolsize
+ *	[ -r $poolfile ] && bytes=`cat $poolfile` || bytes=512
+ *	dd if=/dev/urandom of=$random_seed count=1 bs=bytes
  *
  * and the following lines in an appropriate script which is run as
  * the system is shutdown:
- * 
+ *
  *	# Carry a random seed from shut-down to start-up
- *	# Save 512 bytes, which is the size of the entropy pool
+ *	# Save the whole entropy pool
  *	echo "Saving random seed..."
- * 	random_seed=/var/run/random-seed
- *	dd if=/dev/urandom of=$random_seed count=1
- * 	chmod 600 $random_seed
- * 
+ *	random_seed=/var/run/random-seed
+ *	touch $random_seed
+ *	chmod 600 $random_seed
+ *	poolfile=/proc/sys/kernel/random/poolsize
+ *	[ -r $poolfile ] && bytes=`cat $poolfile` || bytes=512
+ *	dd if=/dev/urandom of=$random_seed count=1 bs=bytes
+ *
  * For example, on most modern systems using the System V init
  * scripts, such code fragments would be found in
  * /etc/rc.d/init.d/random.  On older Linux systems, the correct script
@@ -272,8 +279,8 @@
 static int random_write_wakeup_thresh = 128;
 
 /*
- * A pool of size POOLWORDS is stirred with a primitive polynomial
- * of degree POOLWORDS over GF(2).  The taps for various sizes are
+ * A pool of size .poolwords is stirred with a primitive polynomial
+ * of degree .poolwords over GF(2).  The taps for various sizes are
  * defined below.  They are chosen to be evenly spaced (minimum RMS
  * distance from evenly spaced; the numbers in the comments are a
  * scaled squared error sum) except for the last tap, which is 1 to
@@ -284,19 +291,17 @@
 	int	tap1, tap2, tap3, tap4, tap5;
 } poolinfo_table[] = {
 	/* x^2048 + x^1638 + x^1231 + x^819 + x^411 + x + 1  -- 115 */
-	{ 2048,	1638,	1231,	819, 	411,	1 },
+	{ 2048,	1638,	1231,	819,	411,	1 },
 
 	/* x^1024 + x^817 + x^615 + x^412 + x^204 + x + 1 -- 290 */
-	{ 1024,	817, 	615,	412,	204,	1 },
-
+	{ 1024,	817,	615,	412,	204,	1 },
 #if 0				/* Alternate polynomial */
 	/* x^1024 + x^819 + x^616 + x^410 + x^207 + x^2 + 1 -- 115 */
 	{ 1024,	819,	616,	410,	207,	2 },
 #endif
-	
+
 	/* x^512 + x^411 + x^308 + x^208 + x^104 + x + 1 -- 225 */
 	{ 512,	411,	308,	208,	104,	1 },
-
 #if 0				/* Alternates */
 	/* x^512 + x^409 + x^307 + x^206 + x^102 + x^2 + 1 -- 95 */
 	{ 512,	409,	307,	206,	102,	2 },
@@ -306,10 +311,9 @@
 
 	/* x^256 + x^205 + x^155 + x^101 + x^52 + x + 1 -- 125 */
 	{ 256,	205,	155,	101,	52,	1 },
-	
+
 	/* x^128 + x^103 + x^76 + x^51 +x^25 + x + 1 -- 105 */
 	{ 128,	103,	76,	51,	25,	1 },
-
 #if 0	/* Alternate polynomial */
 	/* x^128 + x^103 + x^78 + x^51 + x^27 + x^2 + 1 -- 70 */
 	{ 128,	103,	78,	51,	27,	2 },
@@ -480,7 +493,7 @@
 /*
  * Initialize the entropy store.  The input argument is the size of
  * the random pool.
- * 
+ *
  * Returns an negative error if there is a problem.
  */
 static int create_entropy_store(int size, struct entropy_store **ret_bucket)
@@ -507,12 +520,12 @@
 	memset (r, 0, sizeof(struct entropy_store));
 	r->poolinfo = *p;
 
-	r->pool = kmalloc(poolwords*4, GFP_KERNEL);
+	r->pool = kmalloc(POOLBYTES, GFP_KERNEL);
 	if (!r->pool) {
 		kfree(r);
 		return -ENOMEM;
 	}
-	memset(r->pool, 0, poolwords*4);
+	memset(r->pool, 0, POOLBYTES);
 	*ret_bucket = r;
 	return 0;
 }
@@ -524,7 +537,7 @@
 	r->entropy_count = 0;
 	r->input_rotate = 0;
 	r->extract_count = 0;
-	memset(r->pool, 0, r->poolinfo.poolwords*4);
+	memset(r->pool, 0, r->poolinfo.POOLBYTES);
 }
 
 static void free_entropy_store(struct entropy_store *r)
@@ -545,18 +558,19 @@
  * the entropy is concentrated in the low-order bits.
  */
 static void add_entropy_words(struct entropy_store *r, const __u32 *in,
-			     int num)
+			      int nwords)
 {
 	static __u32 const twist_table[8] = {
 		         0, 0x3b6e20c8, 0x76dc4190, 0x4db26158,
 		0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 };
 	unsigned i;
 	int new_rotate;
+	int wordmask = r->poolinfo.poolwords - 1;
 	__u32 w;
 
-	while (num--) {
+	while (nwords--) {
 		w = rotate_left(r->input_rotate, *in);
-		i = r->add_ptr = (r->add_ptr - 1) & (r->poolinfo.poolwords-1);
+		i = r->add_ptr = (r->add_ptr - 1) & wordmask;
 		/*
 		 * Normally, we add 7 bits of rotation to the pool.
 		 * At the beginning of the pool, add an extra 7 bits
@@ -569,11 +583,11 @@
 		r->input_rotate = new_rotate & 31;
 
 		/* XOR in the various taps */
-		w ^= r->pool[(i+r->poolinfo.tap1)&(r->poolinfo.poolwords-1)];
-		w ^= r->pool[(i+r->poolinfo.tap2)&(r->poolinfo.poolwords-1)];
-		w ^= r->pool[(i+r->poolinfo.tap3)&(r->poolinfo.poolwords-1)];
-		w ^= r->pool[(i+r->poolinfo.tap4)&(r->poolinfo.poolwords-1)];
-		w ^= r->pool[(i+r->poolinfo.tap5)&(r->poolinfo.poolwords-1)];
+		w ^= r->pool[(i + r->poolinfo.tap1) & wordmask];
+		w ^= r->pool[(i + r->poolinfo.tap2) & wordmask];
+		w ^= r->pool[(i + r->poolinfo.tap3) & wordmask];
+		w ^= r->pool[(i + r->poolinfo.tap4) & wordmask];
+		w ^= r->pool[(i + r->poolinfo.tap5) & wordmask];
 		w ^= r->pool[i];
 		r->pool[i] = (w >> 3) ^ twist_table[w & 7];
 	}
@@ -582,16 +596,22 @@
 /*
  * Credit (or debit) the entropy store with n bits of entropy
  */
-static void credit_entropy_store(struct entropy_store *r, int num)
+static void credit_entropy_store(struct entropy_store *r, int nbits)
 {
-	int	max_entropy = r->poolinfo.poolwords*32;
-
-	if (r->entropy_count + num < 0)
+	if (r->entropy_count + nbits < 0) {
+		DEBUG_ENT("negative entropy/overflow (%d+%d)\n",
+			  r->entropy_count, nbits);
 		r->entropy_count = 0;
-	else if (r->entropy_count + num > max_entropy)
-		r->entropy_count = max_entropy;
-	else
-		r->entropy_count = r->entropy_count + num;
+	} else if (r->entropy_count + nbits > r->poolinfo.POOLBITS) {
+		r->entropy_count = r->poolinfo.POOLBITS;
+	} else {
+		r->entropy_count += nbits;
+		if (nbits)
+			DEBUG_ENT("%s added %d bits, now %d\n",
+				  r == sec_random_state ? "secondary" :
+				  r == random_state ? "primary" : "unknown",
+				  nbits, r->entropy_count);
+	}
 }
 
 /**********************************************************************
@@ -627,6 +647,12 @@
 	return 0;
 }
 
+/*
+ * Changes to the entropy data is put into a queue rather than being added to
+ * the entropy counts directly.  This is presumably to avoid doing heavy
+ * hashing calculations during an interrupt in add_timer_randomness().
+ * Instead, the entropy is only added to the pool once per timer tick.
+ */
 void batch_entropy_store(u32 a, u32 b, int num)
 {
 	int	new;
@@ -643,9 +669,7 @@
 		queue_task(&batch_tqueue, &tq_timer);
 		batch_head = new;
 	} else {
-#if 0
-		printk(KERN_NOTICE "random: batch entropy buffer full\n");
-#endif
+		DEBUG_ENT("batch entropy buffer full\n");
 	}
 }
 
@@ -1200,9 +1229,9 @@
 
 /*
  * This utility inline function is responsible for transfering entropy
- * from the primary pool to the secondary extraction pool.  We pull 
- * randomness under two conditions; one is if there isn't enough entropy 
- * in the secondary pool.  The other is after we have extract 1024 bytes,
+ * from the primary pool to the secondary extraction pool.  We pull
+ * randomness under two conditions; one is if there isn't enough entropy
+ * in the secondary pool.  The other is after we have extracted 1024 bytes,
  * at which point we do a "catastrophic reseeding".
  */
 static inline void xfer_secondary_pool(struct entropy_store *r,
@@ -1228,9 +1269,12 @@
  * bits of entropy are left in the pool, but it does not restrict the
  * number of bytes that are actually obtained.  If the EXTRACT_ENTROPY_USER
  * flag is given, then the buf pointer is assumed to be in user space.
- * If the EXTRACT_ENTROPY_SECONDARY flag is given, then this function will 
  *
- * Note: extract_entropy() assumes that POOLWORDS is a multiple of 16 words.
+ * If the EXTRACT_ENTROPY_SECONDARY flag is given, then we are actually
+ * extracting entropy from the secondary pool, and can refill from the
+ * primary pool if needed.
+ *
+ * Note: extract_entropy() assumes that .poolwords is a multiple of 16 words.
  */
 static ssize_t extract_entropy(struct entropy_store *r, void * buf,
 			       size_t nbytes, int flags)
@@ -1248,6 +1292,11 @@
 	if (flags & EXTRACT_ENTROPY_SECONDARY)
 		xfer_secondary_pool(r, nbytes);
 
+	DEBUG_ENT("%s has %d bits, want %d bits\n",
+		  r == sec_random_state ? "secondary" :
+		  r == random_state ? "primary" : "unknown",
+		  r->entropy_count, nbytes * 8);
+
 	if (r->entropy_count / 8 >= nbytes)
 		r->entropy_count -= nbytes*8;
 	else
@@ -1543,9 +1592,7 @@
 		c -= bytes;
 		p += bytes;
 
-		/* Convert bytes to words */
-		bytes = (bytes + 3) / sizeof(__u32);
-		add_entropy_words(random_state, buf, bytes);
+		add_entropy_words(random_state, buf, (bytes + 3) / 4);
 	}
 	if (p == buffer) {
 		return (ssize_t)ret;
@@ -1589,7 +1636,7 @@
 		ent_count = random_state->entropy_count;
 		if (put_user(ent_count, p++))
 			return -EFAULT;
-			
+
 		if (get_user(size, p))
 			return -EFAULT;
 		if (put_user(random_state->poolinfo.poolwords, p++))
@@ -1598,7 +1645,7 @@
 			return -EINVAL;
 		if (size > random_state->poolinfo.poolwords)
 			size = random_state->poolinfo.poolwords;
-		if (copy_to_user(p, random_state->pool, size*sizeof(__u32)))
+		if (copy_to_user(p, random_state->pool, size * 4))
 			return -EFAULT;
 		return 0;
 	case RNDADDENTROPY:
@@ -1715,11 +1762,11 @@
 {
 	int	ret;
 
-	sysctl_poolsize = random_state->poolinfo.poolwords * 4;
+	sysctl_poolsize = random_state->poolinfo.POOLBYTES;
 
 	ret = proc_dointvec(table, write, filp, buffer, lenp);
 	if (ret || !write ||
-	    (sysctl_poolsize == random_state->poolinfo.poolwords * 4))
+	    (sysctl_poolsize == random_state->poolinfo.POOLBYTES))
 		return ret;
 
 	return change_poolsize(sysctl_poolsize);
@@ -1731,7 +1778,7 @@
 {
 	int	len;
 	
-	sysctl_poolsize = random_state->poolinfo.poolwords * 4;
+	sysctl_poolsize = random_state->poolinfo.POOLBYTES;
 
 	/*
 	 * We only handle the write case, since the read case gets
@@ -1746,7 +1793,7 @@
 			return -EFAULT;
 	}
 
-	if (sysctl_poolsize != random_state->poolinfo.poolwords * 4)
+	if (sysctl_poolsize != random_state->poolinfo.POOLBYTES)
 		return change_poolsize(sysctl_poolsize);
 
 	return 0;
@@ -1845,8 +1892,7 @@
 {
 	min_read_thresh = 8;
 	min_write_thresh = 0;
-	max_read_thresh = max_write_thresh =
-		random_state->poolinfo.poolwords * 32;
+	max_read_thresh = max_write_thresh = random_state->poolinfo.POOLBITS;
 	random_table[1].data = &random_state->entropy_count;
 }
 #endif 	/* CONFIG_SYSCTL */

Cheers, Andreas
--
Andreas Dilger  \ "If a man ate a pound of pasta and a pound of antipasto,
                 \  would they cancel out, leaving him still hungry?"
http://www-mddsp.enel.ucalgary.ca/People/adilger/               -- Dogbert


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH] Re: /dev/random entropy calculations broken?
  2001-10-19 22:59         ` [PATCH] " Andreas Dilger
@ 2001-10-21  5:05           ` Robert Love
  0 siblings, 0 replies; 22+ messages in thread
From: Robert Love @ 2001-10-21  5:05 UTC (permalink / raw)
  To: Andreas Dilger; +Cc: Theodore Ts'o, Alan Cox, torvalds, linux-kernel

On Fri, 2001-10-19 at 18:59, Andreas Dilger wrote:
> I never heard back from Ted on this issue, but it is plain to see that
> entropy accounting for /dev/random is broken.  Linus, Alan, please apply
> the new patch.

I show the same problem on my machines here.  Upon applying the patch:

[00:57:14]rml@phantasy:~$ cat /proc/sys/kernel/random/entropy_avail
4096
[00:57:18]rml@phantasy:~$ dd if=/dev/random bs=64 count=2 | wc -c
2+0 records in
2+0 records out
    128
[00:57:19]rml@phantasy:~$ cat /proc/sys/kernel/random/entropy_avail
3977

(initial - final < 128 because entropy is being added, I assume)

IANADRE, but the patch design looks good to me.  I tried to figure out
where things should be bytes and where things should be words and
everything matched up.

Also, I couldn't get the patch to apply but Andreas kindly sent me a
copy.  It did not diff perfectly against the latest trees, so I rediffed
it.  The attached patches fine against 2.4.12-ac3 and 2.4.13-pre5.  Alan
and Linus, it looks right to me.


diff -u linux-2.4.12-ac3/drivers/char/random.c linux/drivers/char/random.c
--- linux-2.4.12-ac3/drivers/char/random.c	Sun Oct 21 00:32:39 2001
+++ linux/drivers/char/random.c	Sun Oct 21 00:37:18 2001
@@ -164,25 +164,32 @@
  * sequence: 
  *
  *	echo "Initializing random number generator..."
- * 	random_seed=/var/run/random-seed
+ *	random_seed=/var/run/random-seed
  *	# Carry a random seed from start-up to start-up
- *	# Load and then save 512 bytes, which is the size of the entropy pool
- * 	if [ -f $random_seed ]; then
+ *	# Load and then save the whole entropy pool
+ *	if [ -f $random_seed ]; then
  *		cat $random_seed >/dev/urandom
- * 	fi
- *	dd if=/dev/urandom of=$random_seed count=1
- * 	chmod 600 $random_seed
+ *	else
+ *		touch $random_seed
+ *	fi
+ *	chmod 600 $random_seed
+ *	poolfile=/proc/sys/kernel/random/poolsize
+ *	[ -r $poolfile ] && bytes=`cat $poolfile` || bytes=512
+ *	dd if=/dev/urandom of=$random_seed count=1 bs=bytes
  *
  * and the following lines in an appropriate script which is run as
  * the system is shutdown:
- * 
+ *
  *	# Carry a random seed from shut-down to start-up
- *	# Save 512 bytes, which is the size of the entropy pool
+ *	# Save the whole entropy pool
  *	echo "Saving random seed..."
- * 	random_seed=/var/run/random-seed
- *	dd if=/dev/urandom of=$random_seed count=1
- * 	chmod 600 $random_seed
- * 
+ *	random_seed=/var/run/random-seed
+ *	touch $random_seed
+ *	chmod 600 $random_seed
+ *	poolfile=/proc/sys/kernel/random/poolsize
+ *	[ -r $poolfile ] && bytes=`cat $poolfile` || bytes=512
+ *	dd if=/dev/urandom of=$random_seed count=1 bs=bytes
+ *
  * For example, on most modern systems using the System V init
  * scripts, such code fragments would be found in
  * /etc/rc.d/init.d/random.  On older Linux systems, the correct script
@@ -272,8 +279,8 @@
 static int random_write_wakeup_thresh = 128;
 
 /*
- * A pool of size POOLWORDS is stirred with a primitive polynomial
- * of degree POOLWORDS over GF(2).  The taps for various sizes are
+ * A pool of size .poolwords is stirred with a primitive polynomial
+ * of degree .poolwords over GF(2).  The taps for various sizes are
  * defined below.  They are chosen to be evenly spaced (minimum RMS
  * distance from evenly spaced; the numbers in the comments are a
  * scaled squared error sum) except for the last tap, which is 1 to
@@ -284,19 +291,17 @@
 	int	tap1, tap2, tap3, tap4, tap5;
 } poolinfo_table[] = {
 	/* x^2048 + x^1638 + x^1231 + x^819 + x^411 + x + 1  -- 115 */
-	{ 2048,	1638,	1231,	819, 	411,	1 },
+	{ 2048,	1638,	1231,	819,	411,	1 },
 
 	/* x^1024 + x^817 + x^615 + x^412 + x^204 + x + 1 -- 290 */
-	{ 1024,	817, 	615,	412,	204,	1 },
-
+	{ 1024,	817,	615,	412,	204,	1 },
 #if 0				/* Alternate polynomial */
 	/* x^1024 + x^819 + x^616 + x^410 + x^207 + x^2 + 1 -- 115 */
 	{ 1024,	819,	616,	410,	207,	2 },
 #endif
-	
+
 	/* x^512 + x^411 + x^308 + x^208 + x^104 + x + 1 -- 225 */
 	{ 512,	411,	308,	208,	104,	1 },
-
 #if 0				/* Alternates */
 	/* x^512 + x^409 + x^307 + x^206 + x^102 + x^2 + 1 -- 95 */
 	{ 512,	409,	307,	206,	102,	2 },
@@ -306,10 +311,9 @@
 
 	/* x^256 + x^205 + x^155 + x^101 + x^52 + x + 1 -- 125 */
 	{ 256,	205,	155,	101,	52,	1 },
-	
+
 	/* x^128 + x^103 + x^76 + x^51 +x^25 + x + 1 -- 105 */
 	{ 128,	103,	76,	51,	25,	1 },
-
 #if 0	/* Alternate polynomial */
 	/* x^128 + x^103 + x^78 + x^51 + x^27 + x^2 + 1 -- 70 */
 	{ 128,	103,	78,	51,	27,	2 },
@@ -321,9 +325,12 @@
 	/* x^32 + x^26 + x^20 + x^14 + x^7 + x + 1 -- 15 */
 	{ 32,	26,	20,	14,	7,	1 },
 
-	{ 0, 	0,	0,	0,	0,	0 },
-};		
-	
+	{ 0,	0,	0,	0,	0,	0 },
+};
+
+#define POOLBITS	poolwords*32
+#define POOLBYTES	poolwords*4
+
 /*
  * For the purposes of better mixing, we use the CRC-32 polynomial as
  * well to make a twisted Generalized Feedback Shift Reigster
@@ -461,6 +468,12 @@
 }
 #endif
 
+#if 0
+#define DEBUG_ENT(fmt, arg...) printk(KERN_DEBUG "random: " fmt, ## arg)
+#else
+#define DEBUG_ENT(fmt, arg...) do {} while (0)
+#endif
+
 /**********************************************************************
  *
  * OS independent entropy store.   Here are the functions which handle
@@ -480,7 +493,7 @@
 /*
  * Initialize the entropy store.  The input argument is the size of
  * the random pool.
- * 
+ *
  * Returns an negative error if there is a problem.
  */
 static int create_entropy_store(int size, struct entropy_store **ret_bucket)
@@ -507,12 +520,12 @@
 	memset (r, 0, sizeof(struct entropy_store));
 	r->poolinfo = *p;
 
-	r->pool = kmalloc(poolwords*4, GFP_KERNEL);
+	r->pool = kmalloc(POOLBYTES, GFP_KERNEL);
 	if (!r->pool) {
 		kfree(r);
 		return -ENOMEM;
 	}
-	memset(r->pool, 0, poolwords*4);
+	memset(r->pool, 0, POOLBYTES);
 	*ret_bucket = r;
 	return 0;
 }
@@ -524,7 +537,7 @@
 	r->entropy_count = 0;
 	r->input_rotate = 0;
 	r->extract_count = 0;
-	memset(r->pool, 0, r->poolinfo.poolwords*4);
+	memset(r->pool, 0, r->poolinfo.POOLBYTES);
 }
 
 static void free_entropy_store(struct entropy_store *r)
@@ -545,18 +558,19 @@
  * the entropy is concentrated in the low-order bits.
  */
 static void add_entropy_words(struct entropy_store *r, const __u32 *in,
-			     int num)
+			      int nwords)
 {
 	static __u32 const twist_table[8] = {
 		         0, 0x3b6e20c8, 0x76dc4190, 0x4db26158,
 		0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 };
 	unsigned i;
 	int new_rotate;
+	int wordmask = r->poolinfo.poolwords - 1;
 	__u32 w;
 
-	while (num--) {
+	while (nwords--) {
 		w = rotate_left(r->input_rotate, *in);
-		i = r->add_ptr = (r->add_ptr - 1) & (r->poolinfo.poolwords-1);
+		i = r->add_ptr = (r->add_ptr - 1) & wordmask;
 		/*
 		 * Normally, we add 7 bits of rotation to the pool.
 		 * At the beginning of the pool, add an extra 7 bits
@@ -569,11 +583,11 @@
 		r->input_rotate = new_rotate & 31;
 
 		/* XOR in the various taps */
-		w ^= r->pool[(i+r->poolinfo.tap1)&(r->poolinfo.poolwords-1)];
-		w ^= r->pool[(i+r->poolinfo.tap2)&(r->poolinfo.poolwords-1)];
-		w ^= r->pool[(i+r->poolinfo.tap3)&(r->poolinfo.poolwords-1)];
-		w ^= r->pool[(i+r->poolinfo.tap4)&(r->poolinfo.poolwords-1)];
-		w ^= r->pool[(i+r->poolinfo.tap5)&(r->poolinfo.poolwords-1)];
+		w ^= r->pool[(i + r->poolinfo.tap1) & wordmask];
+		w ^= r->pool[(i + r->poolinfo.tap2) & wordmask];
+		w ^= r->pool[(i + r->poolinfo.tap3) & wordmask];
+		w ^= r->pool[(i + r->poolinfo.tap4) & wordmask];
+		w ^= r->pool[(i + r->poolinfo.tap5) & wordmask];
 		w ^= r->pool[i];
 		r->pool[i] = (w >> 3) ^ twist_table[w & 7];
 	}
@@ -582,16 +596,22 @@
 /*
  * Credit (or debit) the entropy store with n bits of entropy
  */
-static void credit_entropy_store(struct entropy_store *r, int num)
+static void credit_entropy_store(struct entropy_store *r, int nbits)
 {
-	int	max_entropy = r->poolinfo.poolwords*32;
-
-	if (r->entropy_count + num < 0)
+	if (r->entropy_count + nbits < 0) {
+		DEBUG_ENT("negative entropy/overflow (%d+%d)\n",
+			  r->entropy_count, nbits);
 		r->entropy_count = 0;
-	else if (r->entropy_count + num > max_entropy)
-		r->entropy_count = max_entropy;
-	else
-		r->entropy_count = r->entropy_count + num;
+	} else if (r->entropy_count + nbits > r->poolinfo.POOLBITS) {
+		r->entropy_count = r->poolinfo.POOLBITS;
+	} else {
+		r->entropy_count += nbits;
+		if (nbits)
+			DEBUG_ENT("%s added %d bits, now %d\n",
+				  r == sec_random_state ? "secondary" :
+				  r == random_state ? "primary" : "unknown",
+				  nbits, r->entropy_count);
+	}
 }
 
 /**********************************************************************
@@ -627,6 +647,12 @@
 	return 0;
 }
 
+/*
+ * Changes to the entropy data is put into a queue rather than being added to
+ * the entropy counts directly.  This is presumably to avoid doing heavy
+ * hashing calculations during an interrupt in add_timer_randomness().
+ * Instead, the entropy is only added to the pool once per timer tick.
+ */
 void batch_entropy_store(u32 a, u32 b, int num)
 {
 	int	new;
@@ -643,32 +669,35 @@
 		queue_task(&batch_tqueue, &tq_timer);
 		batch_head = new;
 	} else {
-#if 0
-		printk(KERN_NOTICE "random: batch entropy buffer full\n");
-#endif
+		DEBUG_ENT("batch entropy buffer full\n");
 	}
 }
 
+/*
+ * Flush out the accumulated entropy operations, adding entropy to the passed
+ * store (normally random_state).  If that store has enough entropy, alternate
+ * between randomizing the data of the primary and secondary stores.
+ */
 static void batch_entropy_process(void *private_)
 {
-	int	num = 0;
-	int	max_entropy;
 	struct entropy_store *r	= (struct entropy_store *) private_, *p;
-	
+	int max_entropy = r->poolinfo.POOLBITS;
+
 	if (!batch_max)
 		return;
 
-	max_entropy = r->poolinfo.poolwords*32;
+	p = r;
 	while (batch_head != batch_tail) {
+		if (r->entropy_count >= max_entropy) {
+			r = (r == sec_random_state) ?	random_state :
+							sec_random_state;
+			max_entropy = r->poolinfo.POOLBITS;
+		}
 		add_entropy_words(r, batch_entropy_pool + 2*batch_tail, 2);
-		p = r;
-		if (r->entropy_count > max_entropy && (num & 1))
-			r = sec_random_state;
 		credit_entropy_store(r, batch_entropy_credit[batch_tail]);
 		batch_tail = (batch_tail+1) & (batch_max-1);
-		num++;
 	}
-	if (r->entropy_count >= random_read_wakeup_thresh)
+	if (p->entropy_count >= random_read_wakeup_thresh)
 		wake_up_interruptible(&random_read_wait);
 }
 
@@ -1204,9 +1233,9 @@
 
 /*
  * This utility inline function is responsible for transfering entropy
- * from the primary pool to the secondary extraction pool.  We pull 
- * randomness under two conditions; one is if there isn't enough entropy 
- * in the secondary pool.  The other is after we have extract 1024 bytes,
+ * from the primary pool to the secondary extraction pool.  We pull
+ * randomness under two conditions; one is if there isn't enough entropy
+ * in the secondary pool.  The other is after we have extracted 1024 bytes,
  * at which point we do a "catastrophic reseeding".
  */
 static inline void xfer_secondary_pool(struct entropy_store *r,
@@ -1214,14 +1243,26 @@
 {
 	__u32	tmp[TMP_BUF_SIZE];
 
-	if (r->entropy_count < nbytes*8) {
-		extract_entropy(random_state, tmp, sizeof(tmp), 0);
-		add_entropy_words(r, tmp, TMP_BUF_SIZE);
-		credit_entropy_store(r, TMP_BUF_SIZE*8);
+	if (r->entropy_count < nbytes * 8 &&
+	    r->entropy_count < r->poolinfo.POOLBITS) {
+		int nwords = min(r->poolinfo.poolwords - r->entropy_count/32,
+				 sizeof(tmp) / 4);
+
+		DEBUG_ENT("xfer %d from primary to %s (have %d, need %d)\n",
+			  nwords * 32,
+			  r == sec_random_state ? "secondary" : "unknown",
+			  r->entropy_count, nbytes * 8);
+
+		extract_entropy(random_state, tmp, nwords, 0);
+		add_entropy_words(r, tmp, nwords);
+		credit_entropy_store(r, nwords * 32);
 	}
 	if (r->extract_count > 1024) {
+		DEBUG_ENT("reseeding %s with %d from primary\n",
+			  r == sec_random_state ? "secondary" : "unknown",
+			  sizeof(tmp) * 8);
 		extract_entropy(random_state, tmp, sizeof(tmp), 0);
-		add_entropy_words(r, tmp, TMP_BUF_SIZE);
+		add_entropy_words(r, tmp, sizeof(tmp) / 4);
 		r->extract_count = 0;
 	}
 }
@@ -1232,9 +1273,12 @@
  * bits of entropy are left in the pool, but it does not restrict the
  * number of bytes that are actually obtained.  If the EXTRACT_ENTROPY_USER
  * flag is given, then the buf pointer is assumed to be in user space.
- * If the EXTRACT_ENTROPY_SECONDARY flag is given, then this function will 
  *
- * Note: extract_entropy() assumes that POOLWORDS is a multiple of 16 words.
+ * If the EXTRACT_ENTROPY_SECONDARY flag is given, then we are actually
+ * extracting entropy from the secondary pool, and can refill from the
+ * primary pool if needed.
+ *
+ * Note: extract_entropy() assumes that .poolwords is a multiple of 16 words.
  */
 static ssize_t extract_entropy(struct entropy_store *r, void * buf,
 			       size_t nbytes, int flags)
@@ -1244,14 +1288,19 @@
 	__u32 x;
 
 	add_timer_randomness(&extract_timer_state, nbytes);
-	
+
 	/* Redundant, but just in case... */
-	if (r->entropy_count > r->poolinfo.poolwords) 
-		r->entropy_count = r->poolinfo.poolwords;
+	if (r->entropy_count > r->poolinfo.POOLBITS)
+		r->entropy_count = r->poolinfo.POOLBITS;
 
 	if (flags & EXTRACT_ENTROPY_SECONDARY)
 		xfer_secondary_pool(r, nbytes);
 
+	DEBUG_ENT("%s has %d bits, want %d bits\n",
+		  r == sec_random_state ? "secondary" :
+		  r == random_state ? "primary" : "unknown",
+		  r->entropy_count, nbytes * 8);
+
 	if (r->entropy_count / 8 >= nbytes)
 		r->entropy_count -= nbytes*8;
 	else
@@ -1547,9 +1596,7 @@
 		c -= bytes;
 		p += bytes;
 
-		/* Convert bytes to words */
-		bytes = (bytes + 3) / sizeof(__u32);
-		add_entropy_words(random_state, buf, bytes);
+		add_entropy_words(random_state, buf, (bytes + 3) / 4);
 	}
 	if (p == buffer) {
 		return (ssize_t)ret;
@@ -1599,7 +1646,7 @@
 			return -EINVAL;
 		if (size > random_state->poolinfo.poolwords)
 			size = random_state->poolinfo.poolwords;
-		if (copy_to_user(p, random_state->pool, size*sizeof(__u32)))
+		if (copy_to_user(p, random_state->pool, size * 4))
 			return -EFAULT;
 		return 0;
 	case RNDADDENTROPY:
@@ -1716,11 +1763,11 @@
 {
 	int	ret;
 
-	sysctl_poolsize = random_state->poolinfo.poolwords * 4;
+	sysctl_poolsize = random_state->poolinfo.POOLBYTES;
 
 	ret = proc_dointvec(table, write, filp, buffer, lenp);
 	if (ret || !write ||
-	    (sysctl_poolsize == random_state->poolinfo.poolwords * 4))
+	    (sysctl_poolsize == random_state->poolinfo.POOLBYTES))
 		return ret;
 
 	return change_poolsize(sysctl_poolsize);
@@ -1732,7 +1779,7 @@
 {
 	int	len;
 	
-	sysctl_poolsize = random_state->poolinfo.poolwords * 4;
+	sysctl_poolsize = random_state->poolinfo.POOLBYTES;
 
 	/*
 	 * We only handle the write case, since the read case gets
@@ -1747,7 +1794,7 @@
 			return -EFAULT;
 	}
 
-	if (sysctl_poolsize != random_state->poolinfo.poolwords * 4)
+	if (sysctl_poolsize != random_state->poolinfo.POOLBYTES)
 		return change_poolsize(sysctl_poolsize);
 
 	return 0;
@@ -1846,8 +1893,7 @@
 {
 	min_read_thresh = 8;
 	min_write_thresh = 0;
-	max_read_thresh = max_write_thresh =
-		random_state->poolinfo.poolwords * 32;
+	max_read_thresh = max_write_thresh = random_state->poolinfo.POOLBITS;
 	random_table[1].data = &random_state->entropy_count;
 }
 #endif 	/* CONFIG_SYSCTL */
@@ -2239,4 +2285,5 @@
 EXPORT_SYMBOL(add_interrupt_randomness);
 EXPORT_SYMBOL(add_blkdev_randomness);
 EXPORT_SYMBOL(batch_entropy_store);
+EXPORT_SYMBOL(generate_random_uuid);
 

	Robert Love


^ permalink raw reply	[flat|nested] 22+ messages in thread

end of thread, other threads:[~2001-10-21  5:06 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2001-09-25 23:36 [PATCH][RFC] Allow net devices to contribute to /dev/random Robert Love
2001-09-26  0:20 ` David Wagner
2001-09-26  0:52   ` Robert Love
2001-09-26  1:36     ` David Wagner
2001-09-26 22:55       ` Gordon Oliver
2001-09-26 23:06         ` Andreas Steinmetz
2001-09-26 15:49     ` dean gaudet
2001-09-26 17:00     ` Oliver Xymoron
2001-10-01 14:43     ` Pavel Machek
2001-10-01 21:33       ` Robert Love
2001-10-01  9:52   ` Florian Weimer
2001-10-01 16:59     ` /dev/random entropy calculations broken? Andreas Dilger
2001-10-01 21:55       ` Alex Bligh - linux-kernel
2001-10-01 22:43         ` antirez
2001-10-02  7:51       ` Andreas Dilger
2001-10-02  8:10         ` Andreas Dilger
2001-10-02 15:37         ` Oliver Xymoron
2001-10-02 21:02           ` Andreas Dilger
2001-10-02 21:29             ` Oliver Xymoron
2001-10-02 22:28               ` Andreas Dilger
2001-10-19 22:59         ` [PATCH] " Andreas Dilger
2001-10-21  5:05           ` Robert Love

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox