This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: platform-specific entropy
Type: Stage:
Components: Interpreter Core Versions: Python 2.4
process
Status: closed Resolution: accepted
Dependencies: Superseder:
Assigned To: loewis Nosy List: loewis, nickm, trevp
Priority: high Keywords: patch

Created on 2004-04-14 04:05 by trevp, last changed 2022-04-11 14:56 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
urandom.diff trevp, 2004-08-26 08:53 5th version, combined patch to CVS HEAD, with tests and docs
Messages (12)
msg45752 - (view) Author: Trevor Perrin (trevp) Date: 2004-04-14 04:05
This is a very simple module for accessing platform-
specific entropy sources.  

On Win32, it uses CryptGenRandom.  Otherwise, it tries 
to use /dev/urandom.  If it can't find that, it raises 
NotImplementedError.

>>> import entropy
>>> entropy.entropy(10)
'\x99/\xbd\x8e\xb0\xfbz/\xf6\xe9'

To compile for Win32, you have to link with "advapi32".  
The /dev/urandom code has not been tested.

Issues:

 - I believe this works with all versions of Windows later 
than Win95.  But I'm not sure.

 - there's overhead in opening/closing the source.  On 
Win32 at least, opening it is slower than reading from it -
 it seems to take around 1/5th of a millisecond (i.e. I 
can make 5000 calls per second).  I think this is okay; 
you can make fewer calls and buffer the results, or seed 
your own PRNG.  However, we could make it more 
object-based, where you open an entropy-source, and 
then repeatedly read from it.

 - It would be nice if there was some attribute that told 
you what source you were using, so you could display 
it, and in case you trust /dev/urandom but not Win32's 
CryptoAPI, for example.
msg45753 - (view) Author: Trevor Perrin (trevp) Date: 2004-04-14 06:19
Logged In: YES 
user_id=973611

Just a thought - this might make sense as a function within 
the 'os' module, instead of its own module.
msg45754 - (view) Author: Nick Mathewson (nickm) Date: 2004-04-25 18:55
Logged In: YES 
user_id=499

This patch would be tremendously valuable to me.  I've had
to maintain similar code for a couple of projects, and
having this functionality in Python would make my life one
step similar.

A few comments:
- According to MSDN, CryptGenRandom exists on Win98 and
later, and on Win95 OSR2, and on Win95 with IE 3.something
or later.

- It's necessary on some platforms, and for some
applications, to use a device other than /dev/urandom. 
(Some high-security code demands /dev/random; some OpenBSD
people swear by /dev/arandom; and so on.)

- Maybe it would be a good idea to only implement the
windows CryptGenRandom part in C, and implement the Unix
part in Python.  Then you could expose the windows code as
(say) "entopy.win_entropy(nBytes)", the unix part as (say)
"entropy.unix_entropy(nBytes, file='/dev/urandom')", and
have "entropy.entropy(nBytes)" be a cross-platform wrapper.
 This would cut down on the amount of C you need to add;
make it easier to switch entropy devices; provide better
errors when /dev/urandom is unreadable; and provide users
the option to trust only certain entropy sources.

- I believe that you're right not to worry too much about
the performance here.

- According to the MSDN documentation for
CryptAcquireContext, if your first call fails, you're
supposed to retry with a final argument of CRYPT_NEWKEYSET
before you report an error.  I don't know when/if this is
necessary, or whether there are hidden gotchas.

Once again, this patch is a great idea, and I heartily hope
that it gets in!
msg45755 - (view) Author: Trevor Perrin (trevp) Date: 2004-04-26 00:08
Logged In: YES 
user_id=973611


Thanks for the comments! - 

> - According to MSDN, CryptGenRandom exists on Win98 and
> later, and on Win95 OSR2, and on Win95 with IE 3.something
> or later.

I'm uploading a new version that fails gracefully on old
Win95s (that's the only change).

> - It's necessary on some platforms, and for some
> applications, to use a device other than /dev/urandom. 
> (Some high-security code demands /dev/random; some 
> OpenBSD people swear by /dev/arandom; and so on.)

My understanding is that /dev/random should only be used in
exceptionally rare cases, if at all.  You can turn up
several posts by David Wagner, a respected cryptographer,
about this.  For example:
http://tinyurl.com/2z2fx

In any case, if you really want /dev/random, or one of the
OpenBSD variants (arandom, srandom, prandom, etc.), it's
easy to do it yourself: open("/dev/random").read().

So I think we should ignore these and stick with
/dev/urandom, since it's the easiest-to-use (non-blocking)
and most portable (unless there are systems that don't offer
it?).

> - Maybe it would be a good idea to only implement the
> windows CryptGenRandom part in C, and implement the Unix
> part in Python.

That's not a bad idea - I sorta think this function should
be placed in the 'os' module, instead of its own module.  So
we could put the /dev/urandom code in 'os.py', and allow
more specific code in, e.g., posixmodule.c to override it.  

We could also add a variable 'os.entropySource' which would
return '/dev/urandom', or 'CryptoAPI', or whatever.

> - According to the MSDN documentation for
> CryptAcquireContext, if your first call fails, you're
> supposed to retry with a final argument of 
> CRYPT_NEWKEYSET before you report an error.

I'm pretty sure using CRYPT_VERIFYCONTEXT eliminates the
need for that:
http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q238187&ID=KB;EN-US;Q238187






msg45756 - (view) Author: Nick Mathewson (nickm) Date: 2004-04-26 00:49
Logged In: YES 
user_id=499

Thanks for the reply!

- As for /dev/*random -- yes, I believe you are right, and
/dev/urandom is almost always what you want.  I haven't been
able to find a platform that has one of the others, but
lacks /dev/urandom.

- I can't find a statement on the page you link about using
CRYPT_VERIFYCONTEXT that way, but you may well be right anway.

- One more important issue: It is a bad idea to use stdio
(C's 'fopen', Python's builtin 'open') to read from
/dev/urandom.  Most stdio implementation buffer data; on my
GNU/Linux box, when I call open('/dev/urandom').read(10), my
underlying fread() function sucks 4096 bytes into memory. 
(It does other weird stuff too, including calls to stat64,
mmap, and others.)  This has proved to be a problem in the
past, especially when running on systems with heavy user
process limits.  Instead, it is a better idea to use the
open syscall directly (open in C, os.open in Python).
msg45757 - (view) Author: Trevor Perrin (trevp) Date: 2004-04-26 03:53
Logged In: YES 
user_id=973611


> - I can't find a statement on the page you link about using
> CRYPT_VERIFYCONTEXT that way,

Note that retrying on NTE_BAD_KEYSET is only described under
the heading  "Private Key Operations Are Performed", in
which case you need to open/create a private key container.
 But if you use CRYPT_VERIFYCONTEXT it just creates a
temporary context in memory.  More corroboration:
http://tinyurl.com/2ct2o

For awhile I was distributing code without
CRYPT_VERIFYCONTEXT, and a user ran into the NTE_BAD_KEYSET
error.  But CRYPT_VERIFYCONTEXT fixed it, which is how I
stumbled on this...

> - One more important issue: It is a bad idea to use stdio
> (C's 'fopen', Python's builtin 'open') to read from
> /dev/urandom.

Good point.  I've tried to update the code to use syscalls.
 Is there any chance you could test this out, and see
whether the #includes look correct and portable?  I don't
have a UNIX box available.  If it needs fixes, feel free to
upload a new version.
msg45758 - (view) Author: Nick Mathewson (nickm) Date: 2004-04-26 17:11
Logged In: YES 
user_id=499

Your lastest version works fine for me (on Redhat Linux 9),
but I still think it would be a better idea to write the
/dev/urandom-ish code in Python, using os.open.  That way,
you could report better error messages if /dev/urandom is
inaccessable, instead of simply claiming that it "wasn't
found."  (Maybe it was found, but we don't have permission
to access it.)
msg45759 - (view) Author: Trevor Perrin (trevp) Date: 2004-04-27 07:21
Logged In: YES 
user_id=973611

I'm uploading a 4th version that does this as an
"os.urandom()" function.  The win32 code is in
posixmodule.c, and the /dev/urandom code is in os.py.

I think the name "urandom" is better than "entropy" - to
some people, "entropy" sounds more like /dev/random. 
"urandom" makes it clear that we're trying to provide
/dev/urandom-like functionality across platforms (i.e.: try
to use real entropy, but if you have to stretch it with a
cryptographic PRNG that's okay).

Also, I cleaned up error-handling, so it will always return
OSError (or an OSError subclass, like WindowsError).  

Finally I changed it to open the random source once, instead
of re-opening it every call.  At least on windows this is a
big speedup - from being able to make 5K calls per second it
can now make 250K.  It's probable that people will write
crypto code using this as their sole RNG source, so this is
probably worthwhile.  This introduces a small risk of
leaking file descriptors/handles if the os module is
reloaded, but I think that's okay. 
msg45760 - (view) Author: Nick Mathewson (nickm) Date: 2004-05-02 02:20
Logged In: YES 
user_id=499

This version looks exactly like what I need.  I really hope
it gets accepted for 2.4.

(I have no strong opinion on whether the fd/crypto context
should be held open.)
msg45761 - (view) Author: Martin v. Löwis (loewis) * (Python committer) Date: 2004-08-25 13:31
Logged In: YES 
user_id=21627

I also like the patch, but I think a number of changes are
still needed:
- there is no documentation
- there are no test cases
- Don't use Py_BuildValue, use PyString_FromStringAndSize
instead
- Don't stat /dev/urandom when importing os, instead, stat
on first usage, and raise NotImplementedError if no source
of randomness can be found.

As an option, support for PRNG could be added (e.g. by
checking an environment variable), but that can be left to
anybody who actually has and needs PRNG.

As for leaving the fd open: this is ok, I think, if it isn't
opened until first use.
msg45762 - (view) Author: Trevor Perrin (trevp) Date: 2004-08-26 08:53
Logged In: YES 
user_id=973611

Hi Martin,

Thanks for looking at this.  I uploaded a new patch (5th
version) with docs, tests, and your suggestions.
msg45763 - (view) Author: Martin v. Löwis (loewis) * (Python committer) Date: 2004-08-29 15:47
Logged In: YES 
user_id=21627

Thanks for the patch. Applied as

libos.tex 1.141
os.py 1.78
test_os.py 1.26
NEWS 1.1117
posixmodule.c 2.322
History
Date User Action Args
2022-04-11 14:56:03adminsetgithub: 40148
2004-04-14 04:05:35trevpcreate