Session-Token-1.503/0000755000175000017500000000000012755075053012712 5ustar dougdougSession-Token-1.503/lib/0000755000175000017500000000000012755075053013460 5ustar dougdougSession-Token-1.503/lib/Session/0000755000175000017500000000000012755075053015103 5ustar dougdougSession-Token-1.503/lib/Session/Token.pm0000644000175000017500000006364612755074657016551 0ustar dougdougpackage Session::Token; use strict; use Carp qw/croak/; use POSIX qw/ceil/; our $VERSION = '1.503'; require XSLoader; XSLoader::load('Session::Token', $VERSION); my $default_alphabet = join('', ('0'..'9', 'a'..'z', 'A'..'Z',)); my $default_entropy = 128; my $is_windows; if ($^O =~ /mswin/i) { require Crypt::Random::Source::Strong::Win32; $is_windows = 1; } sub new { my ($class, @args) = @_; ## support arguments in a hash ref @args = %{$args[0]} if @args == 1 && ref $args[0] eq 'HASH'; my %args = @args; ## Init seed my $seed; if (defined $args{seed}) { croak "seed argument should be a 1024 byte long bytestring" unless length($args{seed}) == 1024; $seed = $args{seed}; } if (!defined $seed) { if ($is_windows) { my $windows_rng_source = Crypt::Random::Source::Strong::Win32->new; $seed = $windows_rng_source->get(1024); die "Win32 RNG source didn't provide 1024 bytes" unless length($seed) == 1024; } else { my ($fh, $err1, $err2); open($fh, '<:raw', '/dev/urandom') || ($err1 = $!); open($fh, '<:raw', '/dev/arandom') || ($err2 = $!) unless defined $fh; if (!defined $fh) { croak "unable to open /dev/urandom ($err1) or /dev/arandom ($err2)"; } sysread($fh, $seed, 1024) == 1024 || croak "unable to read from random device: $!"; } } ## Init alphabet my $alphabet = defined $args{alphabet} ? $args{alphabet} : $default_alphabet; $alphabet = join('', @$alphabet) if ref $alphabet eq 'ARRAY'; croak "alphabet must be between 2 and 256 bytes long" if length($alphabet) < 2 || length($alphabet) > 256; ## Init token length croak "you can't specify both length and entropy" if defined $args{length} && defined $args{entropy}; my $token_length; if (defined $args{length}) { croak "bad value for length" unless $args{length} =~ m/^\d+$/ && $args{length} > 0; $token_length = $args{length}; } else { my $entropy = $args{entropy} || $default_entropy; croak "bad value for entropy" unless $entropy > 0; my $alphabet_entropy = log(length($alphabet)) / log(2); $token_length = ceil($entropy / $alphabet_entropy); } return _new_context($seed, $alphabet, $token_length); } 1; __END__ =encoding utf-8 =head1 NAME Session::Token - Secure, efficient, simple random session token generation =head1 SYNOPSIS =head2 Simple 128-bit session token my $token = Session::Token->new->get; ## 74da9DABOqgoipxqQDdygw =head2 Keep generator around my $generator = Session::Token->new; my $token = $generator->get; ## bu4EXqWt5nEeDjTAZcbTKY my $token2 = $generator->get; ## 4Vez56Zc7el5Ggx4PoXCNL =head2 Custom minimum entropy in bits my $token = Session::Token->new(entropy => 256)->get; ## WdLiluxxZVkPUHsoqnfcQ1YpARuj9Z7or3COA4HNNAv =head2 Custom alphabet and length my $token = Session::Token->new(alphabet => 'ACGT', length => 100_000_000)->get; ## AGTACTTAGCAATCAGCTGGTTCATGGTTGCCCCCATAG... =head1 DESCRIPTION This module provides a secure, efficient, and simple interface for creating session tokens, password reset codes, temporary passwords, random identifiers, and anything else you can think of. When a Session::Token object is created, 1024 bytes are read from C (Linux, Solaris, most BSDs), C (some older BSDs), or L (Windows). These bytes are used to seed the L pseudo random number generator. Once a generator is created, you can repeatedly call the C method on the generator object and it will return a new token each time. B: If your application calls C, make sure that any generators are re-created in one of the processes after the fork since forking will duplicate the generator state and both parent and child processes will go on to produce identical tokens (just like perl's L after it is seeded). After the generator context is created, no system calls are used to generate tokens. This is one way that Session::Token helps with efficiency. However, this is only important for certain use cases (generally not web sessions). ISAAC is a cryptographically secure PRNG that improves on the well-known RC4 algorithm in some important areas. For instance, it doesn't have short cycles or initial bias like RC4 does. A theoretical shortest possible cycle in ISAAC is C<2**40>, although no cycles this short have ever been found (and probably don't exist at all). On average, ISAAC cycles are C<2**8295>. =head1 GENERATORS AND URANDOM Developers must choose whether a single token generator will be kept around and used to generate all tokens, or if a new Session::Token object will be created every time a token is needed. As mentioned above, this module accesses urandom in its constructor for seeding purposes, but not subsequently while generating tokens. Generally speaking the generator should be kept around and re-used. Probably the most important reason for this is that generating a new token from an existing generator cannot fail due to a full file-descriptor table. Creating a new Session::Token object for every token can fail because, as described above, the constructor needs to open C and this will not succeed if all allotted descriptors are in use, or if the read is interrupted by a signal. In these events a perl exception will be thrown. Programs that re-use a generator are more likely to be portable to Ced environments where C may not be present. Finally, accessing urandom frequently is inefficient because it requires making system calls and because (at least on linux) reading from urandom acquires a system-wide kernel lock. On the other hand, re-using a generator may be undesirable because servers are typically started immediately after a system reboot and the kernel's randomness pool might be poorly seeded at that point. Similarly, when starting a virtual machine a previously used entropy pool state may be restored. In these cases all subsequently generated tokens will be derived from a weak/predictable seed. For this reason, you might choose to defer creating the generator until the first request actually comes in, periodically re-create the generator object, and/or manually handle seeding in some other way. Programs that assume opening C will always succeed can return session tokens based only on the contents of nulled or uninitialised memory. This is not the case with Session::Token since its constructor will always throw an exception if it can't seed itself. Some modern systems provide system calls with fewer failure modes (ie `getentropy(2)` on OpenBSD and `getrandom(2)` on linux). Future versions of Session::Token will likely use these system calls when available. =head1 CUSTOM ALPHABETS Being able to choose exactly which characters appear in your token is sometimes useful. This set of characters is called the I. B (C). For some purposes, base-62 is a sweet spot. It is more compact than hexadecimal encoding which helps with efficiency because session tokens are usually transferred over the network many times during a session (often uncompressed in HTTP headers). Also, base-62 tokens don't use "wacky" characters like base-64 encodings do. These characters sometimes cause encoding/escaping problems (ie when embedded in URLs) and are annoying because often you can't select tokens by double-clicking on them. Although the default is base-62, there are all kinds of reasons for using another alphabet. One example is if your users are reading tokens from a print-out or SMS or whatever, you may choose to omit characters like C, C, and C<0> that can easily be confused. To set a custom alphabet, just pass in either a string or an array of characters to the C parameter of the constructor: Session::Token->new(alphabet => '01')->get; Session::Token->new(alphabet => ['0', '1'])->get; # same thing Session::Token->new(alphabet => ['a'..'z'])->get; # character range Constructor args can be a hash-ref too: Session::Token->new({ alphabet => ['a'..'z'] })->get; =head1 ENTROPY There are two ways to specify the length of tokens. The most primitive is in terms of characters: print Session::Token->new(length => 5)->get; ## -> wpLH4 But the primary way is to specify their minimum entropy in terms of bits: print Session::Token->new(entropy => 24)->get; ## -> Fo5SX In the above example, the resulting token contains at least 24 bits of entropy. Given the default base-62 alphabet, we can compute the exact entropy of a 5 character token as follows: $ perl -E 'say 5 * log(62)/log(2)' 29.7709815519344 So these tokens have about 29.8 bits of entropy. Note that if we removed one character from this token, it would bring it below our desired 24 bits of entropy: $ perl -E 'say 4 * log(62)/log(2)' 23.8167852415475 B Default tokens are 22 characters long and therefore have about 131 bits of entropy: $ perl -E 'say 22 * log(62)/log(2)' 130.992318828511 An interesting observation is that in base-64 representation, 128-bit minimum tokens also require 22 characters and that these tokens contain only 1 more bit of entropy. Another Session::Token design criterion is that all tokens should be the same length. The default token length is 22 characters and the tokens are always exactly 22 characters (no more, no less). Instead of tokens that are exactly C characters, some libraries that use arbitrary precision arithmetic end up creating tokens of I C characters. A fixed token length is nice because it makes writing matching regular expressions easier, simplifies storage (you never have to store length), causes various log files and things to line up neatly on your screen, and ensures that encrypted tokens won't leak token entropy due to length (see L). In summary, the default token length of exactly 22 characters is a consequence of these decisions: base-62 representation, 128 bit minimum token entropy, and fixed token length. =head1 MOD BIAS Some token generation libraries that implement custom alphabets will generate a random value, compute its modulus over the size of an alphabet, and then use this modulus to index into the alphabet to determine an output character. Assume we have a uniform random number source that generates values in the set C<[0,1,2,3]> (most PRNGs provide sequences of bits, in other words power-of-2 size sets) and wish to use the alphabet C<"abc">. If we use the naïve modulus algorithm described above then C<0> maps to C, C<1> maps to C, C<2> maps to C, and C<3> I maps to C. This results in the following biased distribution for each character in the token: P(a) = 2/4 = 1/2 P(b) = 1/4 P(c) = 1/4 Of course in an unbiased distribution, each character would have the same chance: P(a) = 1/3 P(b) = 1/3 P(c) = 1/3 Bias is undesirable because certain tokens are obvious starting points when token guessing and certain other tokens are very unlikely. Tokens that are unbiased are equally likely and therefore there is no obvious starting point with them. Session::Token provides unbiased tokens regardless of the size of your alphabet (though see the L section for a mis-use warning). It does this in the same way that you might simulate producing unbiased random numbers from 1 to 5 given an unbiased 6-sided die: Re-roll every time a 6 comes up. In the above example, Session::Token eliminates bias by only using values of C<0>, C<1>, and C<2> (the C test contains some more notes on this topic). Note that mod bias can be made arbitrarily small by increasing the amount of data consumed from a random number generator (provided that arbitrary precision modulus is available). Because this module fundamentally avoids mod bias, it can use each of the 4 bytes from an ISAAC-32 word for a separate character (excepting "re-rolls"). =head1 EFFICIENCY OF RE-ROLLING Throwing away a portion of random data in order to avoid mod bias is slightly inefficient. How many bytes from ISAAC do we expect to consume for every character in the token? It depends on the size of the alphabet. Session::Token masks off each byte using the smallest power of two greater than or equal to the alphabet size minus one so the probability that any particular byte can be used is: P = alphabet_size / next_power_of_two(alphabet_size) For example, with the default base-62 alphabet C

is C<62/64>. In order to find the average number of bytes consumed for each character, calculate the expected value C. There is a probability C

that the first byte will be used and therefore only one byte will be consumed, and a probability C<1 - P> that C<1 + E> bytes will be consumed: E = P*1 + (1 - P)*(1 + E) E = P + 1 + E - P - P*E 0 = 1 - P*E P*E = 1 E = 1/P So for the default base-62 alphabet, the average number of bytes consumed for each character in a token is: E = 1/(62/64) = 64/62 ≅ 1.0323 Because of the next power of two masking optimisation described above, C will always be less than C<2>. In the worst case scenario of an alphabet with 129 characters, C is roughly C<1.9845>. This minor inefficiency isn't an issue because the ISAAC implementation used is quite fast and this module is very thrifty in how it uses ISAAC's output. =head1 INTRODUCING BIAS If your alphabet contains the same character two or more times, this character will be more biased than a character that only occurs once. You should be careful that your alphabets don't repeat in this way if you are trying to create random session tokens. However, if you wish to introduce bias this library doesn't try to stop you. (Maybe it should print a warning?) Session::Token->new(alphabet => '0000001', length => 5000)->get; # don't do this ## -> 0000000000010000000110000000000000000000000100... Due to a limitation discussed below, alphabets larger than 256 aren't currently supported so your bias can't get very granular. Aside: If you have a constant-biased output stream like the above example produces then you can re-construct an un-biased bit sequence with the von neumann algorithm. This works by comparing pairs of bits. If the pair consists of identical bits, it is discarded. Otherwise the order of the different bits is used to determine an output bit, ie C<00> and C<11> are discarded but C<01> and C<10> are mapped to output bits of C<0> and C<1> respectively. This only works if the bias in each bit is constant (like all characters in a Session::Token are). =head1 ALPHABET SIZE LIMITATION Due to a limitation in this module's code, alphabets can't be larger than 256 characters. Everywhere the above manual says "characters" it actually means bytes. This isn't a Unicode limitation per se, just the maximum size of the alphabet. If you like, you can map tokens onto new alphabets as long as they aren't more than 256 characters long. Here is how to generate a 128-bit minimum entropy token using the lowercase greek alphabet (note that both forms of lowercase sigma are included which may not be desirable): use utf8; my $token = Session::Token->new(alphabet => [map {chr} 0..25])->get; $token = join '', map {chr} map {ord($_) + ord('α')} split //, $token; # ρφνδαπξδββφδοςλχτμγσψδψζειετ Here's an interesting way to generate a uniform random integer between 0 to 999 inclusive: 0 + Session::Token->new(alphabet => ['0'..'9'], length => 3)->get If you wanted to natively support high code points, there is no point in hard-coding a limitation on the size of Unicode or even the (higher) limitation of perl characters. Instead, arbitrary precision "characters" should be supported with L. Here's an example of something similar in lisp: L. This module is not however designed to be the ultimate random number generator and at this time I think changing the design as described above would interfere with its goal of being secure, efficient, and simple. =head1 TOKEN TEMPLATES L has a method called C where you provide a pattern that serves as a template when creating the token. You define the meaning of 1 or more template characters and each one that occurs in the pattern is replaced by a random character from a corresponding alphabet. Andrew Beverley requested this feature for L and I suggested approximately the following: use Session::Token; sub token_template { my (%m) = @_; %m = map { $_ => Session::Token->new(alphabet => $m{$_}, length => 1) } keys %m; return sub { my $v = shift; $v =~ s/(.)/exists $m{$1} ? $m{$1}->get : $1/eg; return $v; }; } In order to use C you should pass it key-vaue pairs of the different token characters and the alphabets they represent. It will return a sub that should be passed the template pattern and it will return the resulting random tokens. For example, here is how to create L tokens: sub uuid_v4_generator { my $t = token_template( x => [ 0..9, 'a'..'f' ], y => [ 8, 9, 'a', 'b' ], ); return sub { return $t->('xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'); } } C returns a generator function that will return tokens of the following form: 1b782499-9913-4726-a80a-25e7b2221a7c 90f85a64-d826-43bf-98e7-94ba87406bfb b8b73175-3cce-4861-b43b-3dec5ed5d641 3afb64ab-6de3-4647-bbff-eb94dfa7d4b0 447d2001-2aec-4d32-9910-8c289ae34c48 Note that characters in the pattern which don't have template characters defined (C<-> and C<4> in the above example) are passed through to the output token. =head1 SEEDING This module is designed to always seed itself from your kernel's secure random number source. You should never need to seed it yourself. However if you know what you're doing you can pass in a custom seed as a 1024 byte long string. For example, here is how to create a "null seeded" generator: my $gen = Session::Token(seed => "\x00" x 1024); This is done in the test-suite to compare against Jenkins' reference ISAAC output, but obviously don't do this in regular applications because the generated tokens will be the same every time your program is run. One valid reason for manually seeding is if you have some reason to believe that there isn't enough entropy in your kernel's randomness pool and therefore you don't trust C. In this case you should acquire your own seed data from somewhere trustworthy (maybe C or a previously stored trusted seed). =head1 VARIABLE LENGTH TOKENS As mentioned above, all tokens produced by a Session::Token generator are the same length. If you prefer tokens of variable length, it is possible to post-process the tokens in order to achieve this so long as you keep some things in mind. If you randomly truncate tokens created by Session::Token, be careful not to introduce bias. For example, if you choose the length of the token as a uniformly distributed random length between 8 and 10, then the output will be biased towards shorter token sizes. Length 8 tokens should appear less frequently than length 9 or 10 tokens because there are fewer of them. Another approach is to eliminate leading characters of a given value in the same way as leading C<0>s are commonly eliminated from numeric representations. Although this approach doesn't introduce bias, the tokens C<1> and C<01> are not distinct so it does not increase token entropy given a fixed maximum token length which is the main reason for preferring variable length tokens. The ideal variable length algorithm would generate both C<1> and C<01> tokens (with identical frequency of course). Implementing unbiased, variable-length tokens would complicate the Session::Token implementation especially since you should still be able to specify minimum entropy variable-length tokens. Minimum entropy is the primary input to Session::Token, not token length. This is the reason that the default token length of C<22> isn't hard-coded anywhere in the Session::Token source code (but C<128> is). The final reason that Session::Token discourages variable length tokens is that they can leak token information through a side-channel. This could occur when a message is encrypted but the length of the original message can be inferred from the encrypted ciphertext. =head1 BUGS Should check for biased alphabets and print warnings. Would be cool if it could detect forks and warn or re-seed in the child process (without incurring C overhead). There is currently no way to extract the seed from a Session::Token object. Note when implementing this: The saved seed must either store the current state of the ISAAC round as well as the 1024 byte C array or else do some kind of minimum fast forwarding in order to protect against a partially duplicated output-stream bug. Doesn't work on perl 5.6 and below due to the use of C<:raw> (thanks CPAN testers). It could probably use C instead, but meh. On windows we use L which has a big dependency tree. We should instead use a slimmer module like L. =head1 COMMAND-LINE APP There is a command-line application called L which is a convenience wrapper around L. You can generate session tokens by running the C binary: $ echo "Your password is `session-token`" Your password is 8Yom6z4AeB1RXxCGzklJFt It supports all the options of this module via command line parameters, and multiple session tokens can be generated with the C<--num> (aka C<-n>) switch. For example: $ session-token --alphabet ABC --entropy 32 --num 5 BACAACABCCCCAACBBBCAB BCBACACBBCACCBABABCBA ABBBCBABBACBBBCBBBCCA AACCBBBCCAAACBABACABC CCABCABBCCCAACAAACCAA =head1 SEE ALSO L L L There are lots of different modules for generating random data. If the characterisations of any of them below are inaccurate or out-of-date, please file a github issue and I will correct them. Like this module, perl's C function implements a user-space PRNG seeded from C. However, perl's C is not secure. Perl doesn't specify a PRNG algorithm at all. On linux, whatever it is is seeded with a mere 4 bytes from C. L is the first thing I saw when I looked around on CPAN. It has an inflexible and unspecified alphabet. It tries to get its source of unpredictability from UUIDs and then hashes these UUIDs with SHA-1. I think this is bad design because some standard UUID formats aren't designed to be unpredictable at all. This is acknowledged in RFC 4122 section 6: "Do not assume that UUIDs are hard to guess; they should not be used as security capabilities (identifiers whose mere possession grants access)." With certain UUIDs, knowing a target's MAC address or the rough time the token was issued may help you predict a reduced area of token-space to concentrate guessing attacks upon. I don't know if Data::Token uses these types of UUIDs or the potentially secure "version 4" UUIDs, but because this wasn't addressed in the documentation and because of an apparent misapplication of hash functions (if you really had an unpredictable UUID, there would be no need to hash), I don't feel good about using this module. There are several decent random number generators like L and L but they usually don't implement alphabets and some of them require you open or read from C for every chunk of random bytes. Note that Math::Random::Secure does prevent mod bias in its random integers and could be used to implement unbiased alphabets (slowly). L has a neat regexp-like language for specifying random tokens which is more flexible than alphabets. However, it uses perl's C and its documentation fails to discuss performance, bias, or security. See the L section for a similar feature. L has alphabets, but it uses the flawed mod algorithm described above and opens C for every token. There are other modules like L, L, L, L, L, and L but they use insecure PRNGs such as C or mersenne twister, don't adequately deal with bias, and/or don't let you specify generic alphabets. L has alphabets (aka "bags"), uses ISAAC, and avoids mod bias using the re-roll algorithm. It is much slower than Session::Token (even when using L) but does support alphabets larger than C<256> and might work in environments without XS. Neil Bowers has conducted a L<3rd party review|http://neilb.org/reviews/passwords.html> of various token/password generation modules including Session::Token. Leo Zovic has created a L. =head1 AUTHOR Doug Hoyte, C<< >> =head1 COPYRIGHT & LICENSE Copyright 2012-2016 Doug Hoyte. This module is licensed under the same terms as perl itself. ISAAC code: By Bob Jenkins. My random number generator, ISAAC. Public Domain =cut TODO * Write a full file descriptor table test * Make the urandom/arandom checking code more readable/maintainable * Kill off arandom? * Use getentropy(2)/getrandom(2) if available * Seed extractor API * Issue warning when an alphabet contains a duplicated character * Consider switching from ISAAC to ChaCha20 since this is a more commonly relied on stream cipher nowadays Session-Token-1.503/Changes0000644000175000017500000000455612755074723014222 0ustar dougdoug1.503 2016-08-17 * Link to my TPM presentation * Documentation updates 1.502 2015-09-19 * The alphabet test was using a regexp with ok() which I think may be causing cpantesters failures. I changed it to use like() which will also have better reporting. 1.501 2015-09-09 * Add benchmarking example that compares token templates versus String::Random's randpattern 1.500 2015-09-09 * Use typemap to bless the C context to Session::Token package and implement get() fully in XS. This is about 40% faster when tokens are created in a tight loop. 1.010 2015-08-27 * Fix POD link 1.009 2015-08-27 * Link to Leo Zovic's lisp implementation of Session::Token * Fix doc typo noticed by Per Carlson * Mention App::Session::Token in the docs * Add token template example requested by Andrew Beverley 1.008 2014-04-18 * Delete reference to seed pointer after de-allocation * This is defensive programming in case of obscure bugs in apps that invoke DESTROY multiple times * Lots of documentation improvements * Make podchecker happy 1.007 2013-03-09 * Support passing constructor args in as a hash-ref as well as a list * Update description of Bytes::Random::Secure * Doc updates 1.006 2013-02-14 * Add section to docs describing the efficiency of the re-rolling technique for eliminating mod bias * Add comparison to Bytes::Random::Secure * Improve non-ASCII alphabet example * Don't include MYMETA files in CPAN distribution * kernel-seeding test was missing from manifest * Require perl 5.8 due to use of :raw 0.900 2012-12-05 * Version number format change * Include github repo and bugtracker in CPAN meta data * Updated docs to describe variable-length tokens * Other doc updates 0.82 2012-07-20 * Make no-mod-bias test more useful as a stand-alone tool * New test to specifically verify the kernel seeding procedure is working (win32 is primary concern) * Improve docs 0.81 2012-06-28 * OK now I have CPAN versions figured out :) 0.10 2012-06-28 * Oops, bad previous version number 0.9.0 2012-06-28 * Experimental win32 support * Fix reference test on big endian archs 0.8 2012-06-26 * Open /dev/urandom as :raw to prevent CRLF translation &c * Improve docs * Record some system information in test output * Add seed coverage test 0.7 2012-06-17 * Initial release Session-Token-1.503/rand.h0000644000175000017500000000301612413466012013775 0ustar dougdoug/* ------------------------------------------------------------------------------ rand.h: definitions for a random number generator By Bob Jenkins, 1996, Public Domain MODIFIED: 960327: Creation (addition of randinit, really) 970719: use context, not global variables, for internal state 980324: renamed seed to flag 980605: recommend RANDSIZL=4 for noncryptography. 010626: note this is public domain ------------------------------------------------------------------------------ */ #ifndef STANDARD #include "standard.h" #endif #ifndef RAND #define RAND #define RANDSIZL (8) #define RANDSIZ (1<randcnt-- ? \ (isaac(r), (r)->randcnt=RANDSIZ-1, (r)->randrsl[(r)->randcnt]) : \ (r)->randrsl[(r)->randcnt]) #endif /* RAND */ Session-Token-1.503/README0000664000175000017500000006074112755075054013605 0ustar dougdougNAME Session::Token - Secure, efficient, simple random session token generation SYNOPSIS Simple 128-bit session token my $token = Session::Token->new->get; ## 74da9DABOqgoipxqQDdygw Keep generator around my $generator = Session::Token->new; my $token = $generator->get; ## bu4EXqWt5nEeDjTAZcbTKY my $token2 = $generator->get; ## 4Vez56Zc7el5Ggx4PoXCNL Custom minimum entropy in bits my $token = Session::Token->new(entropy => 256)->get; ## WdLiluxxZVkPUHsoqnfcQ1YpARuj9Z7or3COA4HNNAv Custom alphabet and length my $token = Session::Token->new(alphabet => 'ACGT', length => 100_000_000)->get; ## AGTACTTAGCAATCAGCTGGTTCATGGTTGCCCCCATAG... DESCRIPTION This module provides a secure, efficient, and simple interface for creating session tokens, password reset codes, temporary passwords, random identifiers, and anything else you can think of. When a Session::Token object is created, 1024 bytes are read from "/dev/urandom" (Linux, Solaris, most BSDs), "/dev/arandom" (some older BSDs), or Crypt::Random::Source::Strong::Win32 (Windows). These bytes are used to seed the ISAAC-32 pseudo random number generator. Once a generator is created, you can repeatedly call the "get" method on the generator object and it will return a new token each time. IMPORTANT: If your application calls "fork", make sure that any generators are re-created in one of the processes after the fork since forking will duplicate the generator state and both parent and child processes will go on to produce identical tokens (just like perl's rand after it is seeded). After the generator context is created, no system calls are used to generate tokens. This is one way that Session::Token helps with efficiency. However, this is only important for certain use cases (generally not web sessions). ISAAC is a cryptographically secure PRNG that improves on the well-known RC4 algorithm in some important areas. For instance, it doesn't have short cycles or initial bias like RC4 does. A theoretical shortest possible cycle in ISAAC is "2**40", although no cycles this short have ever been found (and probably don't exist at all). On average, ISAAC cycles are "2**8295". GENERATORS AND URANDOM Developers must choose whether a single token generator will be kept around and used to generate all tokens, or if a new Session::Token object will be created every time a token is needed. As mentioned above, this module accesses urandom in its constructor for seeding purposes, but not subsequently while generating tokens. Generally speaking the generator should be kept around and re-used. Probably the most important reason for this is that generating a new token from an existing generator cannot fail due to a full file-descriptor table. Creating a new Session::Token object for every token can fail because, as described above, the constructor needs to open "/dev/urandom" and this will not succeed if all allotted descriptors are in use, or if the read is interrupted by a signal. In these events a perl exception will be thrown. Programs that re-use a generator are more likely to be portable to "chroot"ed environments where "/dev/urandom" may not be present. Finally, accessing urandom frequently is inefficient because it requires making system calls and because (at least on linux) reading from urandom acquires a system-wide kernel lock. On the other hand, re-using a generator may be undesirable because servers are typically started immediately after a system reboot and the kernel's randomness pool might be poorly seeded at that point. Similarly, when starting a virtual machine a previously used entropy pool state may be restored. In these cases all subsequently generated tokens will be derived from a weak/predictable seed. For this reason, you might choose to defer creating the generator until the first request actually comes in, periodically re-create the generator object, and/or manually handle seeding in some other way. Programs that assume opening "/dev/urandom" will always succeed can return session tokens based only on the contents of nulled or uninitialised memory. This is not the case with Session::Token since its constructor will always throw an exception if it can't seed itself. Some modern systems provide system calls with fewer failure modes (ie `getentropy(2)` on OpenBSD and `getrandom(2)` on linux). Future versions of Session::Token will likely use these system calls when available. CUSTOM ALPHABETS Being able to choose exactly which characters appear in your token is sometimes useful. This set of characters is called the *alphabet*. The default alphabet size is 62 characters: uppercase letters, lowercase letters, and digits ("a-zA-Z0-9"). For some purposes, base-62 is a sweet spot. It is more compact than hexadecimal encoding which helps with efficiency because session tokens are usually transferred over the network many times during a session (often uncompressed in HTTP headers). Also, base-62 tokens don't use "wacky" characters like base-64 encodings do. These characters sometimes cause encoding/escaping problems (ie when embedded in URLs) and are annoying because often you can't select tokens by double-clicking on them. Although the default is base-62, there are all kinds of reasons for using another alphabet. One example is if your users are reading tokens from a print-out or SMS or whatever, you may choose to omit characters like "o", "O", and 0 that can easily be confused. To set a custom alphabet, just pass in either a string or an array of characters to the "alphabet" parameter of the constructor: Session::Token->new(alphabet => '01')->get; Session::Token->new(alphabet => ['0', '1'])->get; # same thing Session::Token->new(alphabet => ['a'..'z'])->get; # character range Constructor args can be a hash-ref too: Session::Token->new({ alphabet => ['a'..'z'] })->get; ENTROPY There are two ways to specify the length of tokens. The most primitive is in terms of characters: print Session::Token->new(length => 5)->get; ## -> wpLH4 But the primary way is to specify their minimum entropy in terms of bits: print Session::Token->new(entropy => 24)->get; ## -> Fo5SX In the above example, the resulting token contains at least 24 bits of entropy. Given the default base-62 alphabet, we can compute the exact entropy of a 5 character token as follows: $ perl -E 'say 5 * log(62)/log(2)' 29.7709815519344 So these tokens have about 29.8 bits of entropy. Note that if we removed one character from this token, it would bring it below our desired 24 bits of entropy: $ perl -E 'say 4 * log(62)/log(2)' 23.8167852415475 The default minimum entropy is 128 bits. Default tokens are 22 characters long and therefore have about 131 bits of entropy: $ perl -E 'say 22 * log(62)/log(2)' 130.992318828511 An interesting observation is that in base-64 representation, 128-bit minimum tokens also require 22 characters and that these tokens contain only 1 more bit of entropy. Another Session::Token design criterion is that all tokens should be the same length. The default token length is 22 characters and the tokens are always exactly 22 characters (no more, no less). Instead of tokens that are exactly "N" characters, some libraries that use arbitrary precision arithmetic end up creating tokens of *at most* "N" characters. A fixed token length is nice because it makes writing matching regular expressions easier, simplifies storage (you never have to store length), causes various log files and things to line up neatly on your screen, and ensures that encrypted tokens won't leak token entropy due to length (see "VARIABLE LENGTH TOKENS"). In summary, the default token length of exactly 22 characters is a consequence of these decisions: base-62 representation, 128 bit minimum token entropy, and fixed token length. MOD BIAS Some token generation libraries that implement custom alphabets will generate a random value, compute its modulus over the size of an alphabet, and then use this modulus to index into the alphabet to determine an output character. Assume we have a uniform random number source that generates values in the set "[0,1,2,3]" (most PRNGs provide sequences of bits, in other words power-of-2 size sets) and wish to use the alphabet "abc". If we use the naïve modulus algorithm described above then 0 maps to "a", 1 maps to "b", 2 maps to "c", and 3 *also* maps to "a". This results in the following biased distribution for each character in the token: P(a) = 2/4 = 1/2 P(b) = 1/4 P(c) = 1/4 Of course in an unbiased distribution, each character would have the same chance: P(a) = 1/3 P(b) = 1/3 P(c) = 1/3 Bias is undesirable because certain tokens are obvious starting points when token guessing and certain other tokens are very unlikely. Tokens that are unbiased are equally likely and therefore there is no obvious starting point with them. Session::Token provides unbiased tokens regardless of the size of your alphabet (though see the "INTRODUCING BIAS" section for a mis-use warning). It does this in the same way that you might simulate producing unbiased random numbers from 1 to 5 given an unbiased 6-sided die: Re-roll every time a 6 comes up. In the above example, Session::Token eliminates bias by only using values of 0, 1, and 2 (the "t/no-mod-bias.t" test contains some more notes on this topic). Note that mod bias can be made arbitrarily small by increasing the amount of data consumed from a random number generator (provided that arbitrary precision modulus is available). Because this module fundamentally avoids mod bias, it can use each of the 4 bytes from an ISAAC-32 word for a separate character (excepting "re-rolls"). EFFICIENCY OF RE-ROLLING Throwing away a portion of random data in order to avoid mod bias is slightly inefficient. How many bytes from ISAAC do we expect to consume for every character in the token? It depends on the size of the alphabet. Session::Token masks off each byte using the smallest power of two greater than or equal to the alphabet size minus one so the probability that any particular byte can be used is: P = alphabet_size / next_power_of_two(alphabet_size) For example, with the default base-62 alphabet "P" is "62/64". In order to find the average number of bytes consumed for each character, calculate the expected value "E". There is a probability "P" that the first byte will be used and therefore only one byte will be consumed, and a probability "1 - P" that "1 + E" bytes will be consumed: E = P*1 + (1 - P)*(1 + E) E = P + 1 + E - P - P*E 0 = 1 - P*E P*E = 1 E = 1/P So for the default base-62 alphabet, the average number of bytes consumed for each character in a token is: E = 1/(62/64) = 64/62 ≅ 1.0323 Because of the next power of two masking optimisation described above, "E" will always be less than 2. In the worst case scenario of an alphabet with 129 characters, "E" is roughly 1.9845. This minor inefficiency isn't an issue because the ISAAC implementation used is quite fast and this module is very thrifty in how it uses ISAAC's output. INTRODUCING BIAS If your alphabet contains the same character two or more times, this character will be more biased than a character that only occurs once. You should be careful that your alphabets don't repeat in this way if you are trying to create random session tokens. However, if you wish to introduce bias this library doesn't try to stop you. (Maybe it should print a warning?) Session::Token->new(alphabet => '0000001', length => 5000)->get; # don't do this ## -> 0000000000010000000110000000000000000000000100... Due to a limitation discussed below, alphabets larger than 256 aren't currently supported so your bias can't get very granular. Aside: If you have a constant-biased output stream like the above example produces then you can re-construct an un-biased bit sequence with the von neumann algorithm. This works by comparing pairs of bits. If the pair consists of identical bits, it is discarded. Otherwise the order of the different bits is used to determine an output bit, ie 00 and 11 are discarded but 01 and 10 are mapped to output bits of 0 and 1 respectively. This only works if the bias in each bit is constant (like all characters in a Session::Token are). ALPHABET SIZE LIMITATION Due to a limitation in this module's code, alphabets can't be larger than 256 characters. Everywhere the above manual says "characters" it actually means bytes. This isn't a Unicode limitation per se, just the maximum size of the alphabet. If you like, you can map tokens onto new alphabets as long as they aren't more than 256 characters long. Here is how to generate a 128-bit minimum entropy token using the lowercase greek alphabet (note that both forms of lowercase sigma are included which may not be desirable): use utf8; my $token = Session::Token->new(alphabet => [map {chr} 0..25])->get; $token = join '', map {chr} map {ord($_) + ord('α')} split //, $token; # ρφνδαπξδββφδοςλχτμγσψδψζειετ Here's an interesting way to generate a uniform random integer between 0 to 999 inclusive: 0 + Session::Token->new(alphabet => ['0'..'9'], length => 3)->get If you wanted to natively support high code points, there is no point in hard-coding a limitation on the size of Unicode or even the (higher) limitation of perl characters. Instead, arbitrary precision "characters" should be supported with bigint. Here's an example of something similar in lisp: isaac.lisp . This module is not however designed to be the ultimate random number generator and at this time I think changing the design as described above would interfere with its goal of being secure, efficient, and simple. TOKEN TEMPLATES String::Random has a method called "randpattern" where you provide a pattern that serves as a template when creating the token. You define the meaning of 1 or more template characters and each one that occurs in the pattern is replaced by a random character from a corresponding alphabet. Andrew Beverley requested this feature for Session::Token and I suggested approximately the following: use Session::Token; sub token_template { my (%m) = @_; %m = map { $_ => Session::Token->new(alphabet => $m{$_}, length => 1) } keys %m; return sub { my $v = shift; $v =~ s/(.)/exists $m{$1} ? $m{$1}->get : $1/eg; return $v; }; } In order to use "token_template" you should pass it key-vaue pairs of the different token characters and the alphabets they represent. It will return a sub that should be passed the template pattern and it will return the resulting random tokens. For example, here is how to create UUID version 4 tokens: sub uuid_v4_generator { my $t = token_template( x => [ 0..9, 'a'..'f' ], y => [ 8, 9, 'a', 'b' ], ); return sub { return $t->('xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'); } } "uuid_v4_generator" returns a generator function that will return tokens of the following form: 1b782499-9913-4726-a80a-25e7b2221a7c 90f85a64-d826-43bf-98e7-94ba87406bfb b8b73175-3cce-4861-b43b-3dec5ed5d641 3afb64ab-6de3-4647-bbff-eb94dfa7d4b0 447d2001-2aec-4d32-9910-8c289ae34c48 Note that characters in the pattern which don't have template characters defined ("-" and 4 in the above example) are passed through to the output token. SEEDING This module is designed to always seed itself from your kernel's secure random number source. You should never need to seed it yourself. However if you know what you're doing you can pass in a custom seed as a 1024 byte long string. For example, here is how to create a "null seeded" generator: my $gen = Session::Token(seed => "\x00" x 1024); This is done in the test-suite to compare against Jenkins' reference ISAAC output, but obviously don't do this in regular applications because the generated tokens will be the same every time your program is run. One valid reason for manually seeding is if you have some reason to believe that there isn't enough entropy in your kernel's randomness pool and therefore you don't trust "/dev/urandom". In this case you should acquire your own seed data from somewhere trustworthy (maybe "/dev/random" or a previously stored trusted seed). VARIABLE LENGTH TOKENS As mentioned above, all tokens produced by a Session::Token generator are the same length. If you prefer tokens of variable length, it is possible to post-process the tokens in order to achieve this so long as you keep some things in mind. If you randomly truncate tokens created by Session::Token, be careful not to introduce bias. For example, if you choose the length of the token as a uniformly distributed random length between 8 and 10, then the output will be biased towards shorter token sizes. Length 8 tokens should appear less frequently than length 9 or 10 tokens because there are fewer of them. Another approach is to eliminate leading characters of a given value in the same way as leading 0s are commonly eliminated from numeric representations. Although this approach doesn't introduce bias, the tokens 1 and 01 are not distinct so it does not increase token entropy given a fixed maximum token length which is the main reason for preferring variable length tokens. The ideal variable length algorithm would generate both 1 and 01 tokens (with identical frequency of course). Implementing unbiased, variable-length tokens would complicate the Session::Token implementation especially since you should still be able to specify minimum entropy variable-length tokens. Minimum entropy is the primary input to Session::Token, not token length. This is the reason that the default token length of 22 isn't hard-coded anywhere in the Session::Token source code (but 128 is). The final reason that Session::Token discourages variable length tokens is that they can leak token information through a side-channel. This could occur when a message is encrypted but the length of the original message can be inferred from the encrypted ciphertext. BUGS Should check for biased alphabets and print warnings. Would be cool if it could detect forks and warn or re-seed in the child process (without incurring "getpid" overhead). There is currently no way to extract the seed from a Session::Token object. Note when implementing this: The saved seed must either store the current state of the ISAAC round as well as the 1024 byte "randsl" array or else do some kind of minimum fast forwarding in order to protect against a partially duplicated output-stream bug. Doesn't work on perl 5.6 and below due to the use of ":raw" (thanks CPAN testers). It could probably use "binmode" instead, but meh. On windows we use Crypt::Random::Source::Strong::Win32 which has a big dependency tree. We should instead use a slimmer module like Crypt::Random::Seed. COMMAND-LINE APP There is a command-line application called App::Session::Token which is a convenience wrapper around Session::Token. You can generate session tokens by running the "session-token" binary: $ echo "Your password is `session-token`" Your password is 8Yom6z4AeB1RXxCGzklJFt It supports all the options of this module via command line parameters, and multiple session tokens can be generated with the "--num" (aka "-n") switch. For example: $ session-token --alphabet ABC --entropy 32 --num 5 BACAACABCCCCAACBBBCAB BCBACACBBCACCBABABCBA ABBBCBABBACBBBCBBBCCA AACCBBBCCAAACBABACABC CCABCABBCCCAACAAACCAA SEE ALSO The Session::Token github repo App::Session::Token Presentation for Toronto Perl Mongers There are lots of different modules for generating random data. If the characterisations of any of them below are inaccurate or out-of-date, please file a github issue and I will correct them. Like this module, perl's "rand()" function implements a user-space PRNG seeded from "/dev/urandom". However, perl's "rand()" is not secure. Perl doesn't specify a PRNG algorithm at all. On linux, whatever it is is seeded with a mere 4 bytes from "/dev/urandom". Data::Token is the first thing I saw when I looked around on CPAN. It has an inflexible and unspecified alphabet. It tries to get its source of unpredictability from UUIDs and then hashes these UUIDs with SHA-1. I think this is bad design because some standard UUID formats aren't designed to be unpredictable at all. This is acknowledged in RFC 4122 section 6: "Do not assume that UUIDs are hard to guess; they should not be used as security capabilities (identifiers whose mere possession grants access)." With certain UUIDs, knowing a target's MAC address or the rough time the token was issued may help you predict a reduced area of token-space to concentrate guessing attacks upon. I don't know if Data::Token uses these types of UUIDs or the potentially secure "version 4" UUIDs, but because this wasn't addressed in the documentation and because of an apparent misapplication of hash functions (if you really had an unpredictable UUID, there would be no need to hash), I don't feel good about using this module. There are several decent random number generators like Math::Random::Secure and Crypt::URandom but they usually don't implement alphabets and some of them require you open or read from "/dev/urandom" for every chunk of random bytes. Note that Math::Random::Secure does prevent mod bias in its random integers and could be used to implement unbiased alphabets (slowly). String::Random has a neat regexp-like language for specifying random tokens which is more flexible than alphabets. However, it uses perl's "rand()" and its documentation fails to discuss performance, bias, or security. See the "TOKEN TEMPLATES" section for a similar feature. String::Urandom has alphabets, but it uses the flawed mod algorithm described above and opens "/dev/urandom" for every token. There are other modules like Data::Random, App::Genpass, String::MkPasswd, Crypt::RandPasswd, Crypt::GeneratePassword, and Data::SimplePassword but they use insecure PRNGs such as "rand()" or mersenne twister, don't adequately deal with bias, and/or don't let you specify generic alphabets. Bytes::Random::Secure has alphabets (aka "bags"), uses ISAAC, and avoids mod bias using the re-roll algorithm. It is much slower than Session::Token (even when using Math::Random::ISAAC::XS) but does support alphabets larger than 256 and might work in environments without XS. Neil Bowers has conducted a 3rd party review of various token/password generation modules including Session::Token. Leo Zovic has created a Common Lisp implementation of session-token . AUTHOR Doug Hoyte, "" COPYRIGHT & LICENSE Copyright 2012-2016 Doug Hoyte. This module is licensed under the same terms as perl itself. ISAAC code: By Bob Jenkins. My random number generator, ISAAC. Public Domain Session-Token-1.503/randport.c0000644000175000017500000000724112413466012014701 0ustar dougdoug/* ------------------------------------------------------------------------------ rand.c: By Bob Jenkins. My random number generator, ISAAC. Public Domain MODIFIED: 960327: Creation (addition of randinit, really) 970719: use context, not global variables, for internal state 980324: make a portable version 010626: Note this is public domain 100725: Mask on use of >32 bits, not on assignment: from Paul Eggert ------------------------------------------------------------------------------ */ #ifndef STANDARD #include "standard.h" #endif #ifndef RAND #include "rand.h" #endif #define ind(mm,x) ((mm)[(x>>2)&(RANDSIZ-1)]) #define rngstep(mix,a,b,mm,m,m2,r,x) \ { \ x = *m; \ a = ((a^(mix)) + *(m2++)); \ *(m++) = y = (ind(mm,x) + a + b); \ *(r++) = b = (ind(mm,y>>RANDSIZL) + x) & 0xffffffff; \ } void isaac(ctx) randctx *ctx; { ub4 a,b,x,y,*m,*mm,*m2,*r,*mend; mm=ctx->randmem; r=ctx->randrsl; a = ctx->randa; b = ctx->randb + (++ctx->randc); for (m = mm, mend = m2 = m+(RANDSIZ/2); m>6 , a, b, mm, m, m2, r, x); rngstep( a<<2 , a, b, mm, m, m2, r, x); rngstep( (a & 0xffffffff) >>16, a, b, mm, m, m2, r, x); } for (m2 = mm; m2>6 , a, b, mm, m, m2, r, x); rngstep( a<<2 , a, b, mm, m, m2, r, x); rngstep( (a & 0xffffffff) >>16, a, b, mm, m, m2, r, x); } ctx->randb = b; ctx->randa = a; } #define mix(a,b,c,d,e,f,g,h) \ { \ a^=b<<11; d+=a; b+=c; \ b^=(c&0xffffffff)>>2; e+=b; c+=d; \ c^=d<<8; f+=c; d+=e; \ d^=(e&0xffffffff)>>16; g+=d; e+=f; \ e^=f<<10; h+=e; f+=g; \ f^=(g&0xffffffff)>>4; a+=f; g+=h; \ g^=h<<8; b+=g; h+=a; \ h^=(a&0xffffffff)>>9; c+=h; a+=b; \ } /* if (flag==TRUE), then use the contents of randrsl[] to initialize mm[]. */ void randinit(ctx, flag) randctx *ctx; word flag; { word i; ub4 a,b,c,d,e,f,g,h; ub4 *m,*r; ctx->randa = ctx->randb = ctx->randc = 0; m=ctx->randmem; r=ctx->randrsl; a=b=c=d=e=f=g=h=0x9e3779b9; /* the golden ratio */ for (i=0; i<4; ++i) /* scramble it */ { mix(a,b,c,d,e,f,g,h); } if (flag) { /* initialize using the contents of r[] as the seed */ for (i=0; irandcnt=RANDSIZ; /* prepare to use the first set of results */ } #ifdef NEVER int main() { ub4 i,j; randctx ctx; ctx.randa=ctx.randb=ctx.randc=(ub4)0; for (i=0; i<256; ++i) ctx.randrsl[i]=(ub4)0; randinit(&ctx, TRUE); for (i=0; i<2; ++i) { isaac(&ctx); for (j=0; j<256; ++j) { printf("%.8lx",ctx.randrsl[j]); if ((j&7)==7) printf("\n"); } } } #endif Session-Token-1.503/Token.xs0000644000175000017500000000515512755074621014354 0ustar dougdoug#include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "standard.h" #include "rand.h" #include #include #include struct session_token_ctx { int mask; int count; int curr_word; int bytes_left_in_curr_word; struct randctx isaac_ctx; char *alphabet; size_t alphabet_length; size_t token_length; }; typedef struct session_token_ctx * Session_Token; static inline int get_new_byte(struct session_token_ctx *ctx) { int output; if (ctx->bytes_left_in_curr_word == 0) { if (ctx->count > 255) { isaac(&ctx->isaac_ctx); ctx->count = 0; } ctx->curr_word = ctx->isaac_ctx.randrsl[ctx->count]; ctx->count++; ctx->bytes_left_in_curr_word = 4; } output = ctx->curr_word & 0xFF; ctx->bytes_left_in_curr_word--; ctx->curr_word >>= 8; return output; } static int get_mask(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; return v; } MODULE = Session::Token PACKAGE = Session::Token PROTOTYPES: ENABLE Session_Token _new_context(seed, alphabet, token_length) SV *seed SV *alphabet size_t token_length CODE: struct session_token_ctx *ctx; char *seedp; size_t len; len = SvCUR(seed); seedp = SvPV(seed, len); assert(sizeof(ctx->isaac_ctx.randrsl) == 1024); if (len != 1024) { croak("unexpected seed length: %lu", len); } ctx = malloc(sizeof(struct session_token_ctx)); memset(ctx, '\0', sizeof(struct session_token_ctx)); memcpy(&ctx->isaac_ctx.randrsl, seedp, 1024); randinit(&ctx->isaac_ctx, TRUE); isaac(&ctx->isaac_ctx); ctx->alphabet_length = SvCUR(alphabet); ctx->alphabet = malloc(ctx->alphabet_length); memcpy(ctx->alphabet, SvPV(alphabet, ctx->alphabet_length), ctx->alphabet_length); ctx->token_length = token_length; ctx->mask = get_mask(ctx->alphabet_length); RETVAL = ctx; OUTPUT: RETVAL void DESTROY(ctx) Session_Token ctx CODE: free(ctx->alphabet); free(ctx); SV * get(ctx) Session_Token ctx CODE: SV *output; char *outputp; size_t i, curr; output = newSVpvn("", 0); SvGROW(output, ctx->token_length); SvCUR_set(output, ctx->token_length); outputp = SvPV(output, ctx->token_length); for (i=0; itoken_length; i++) { while((curr = (get_new_byte(ctx) & ctx->mask)) >= ctx->alphabet_length) ; outputp[i] = ctx->alphabet[curr]; } RETVAL = output; OUTPUT: RETVAL Session-Token-1.503/typemap0000644000175000017500000000062512755074621014317 0ustar dougdougTYPEMAP Session_Token T_PTROBJ_SPECIAL INPUT T_PTROBJ_SPECIAL if (sv_derived_from($arg, \"${(my $ntt=$ntype)=~s/_/::/g;\$ntt}\")){ IV tmp = SvIV((SV*)SvRV($arg)); $var = INT2PTR($type, tmp); } else croak(\"$var is not of type ${(my $ntt=$ntype)=~s/_/::/g;\$ntt}\") OUTPUT T_PTROBJ_SPECIAL sv_setref_pv($arg, \"${(my $ntt=$ntype)=~s/_/::/g;\$ntt}\", (void*)$var); Session-Token-1.503/MANIFEST0000644000175000017500000000063712755075053014051 0ustar dougdougChanges COPYING ex/token-template-bench.pl lib/Session/Token.pm Makefile.PL MANIFEST MANIFEST.SKIP rand.h randport.c README standard.h t/00-system.t t/alphabets.t t/kernel-seeding.t t/no-mod-bias.t t/reference.t t/seed-coverage.t Token.xs typemap META.yml Module YAML meta-data (added by MakeMaker) META.json Module JSON meta-data (added by MakeMaker) Session-Token-1.503/ex/0000755000175000017500000000000012755075053013326 5ustar dougdougSession-Token-1.503/ex/token-template-bench.pl0000644000175000017500000000133012755074621017666 0ustar dougdouguse strict; use Benchmark qw(:all); use String::Random; use Session::Token; my $sr = String::Random->new; $sr->{x} = [ 'A' .. 'F' ]; $sr->{y} = [ 'a' .. 'z' ]; $sr->{z} = [ '0' .. '9' ]; sub token_template { my (%m) = @_; %m = map { $_ => Session::Token->new(alphabet => $m{$_}, length => 1) } keys %m; return sub { my $v = shift; $v =~ s/(.)/exists $m{$1} ? $m{$1}->get : $1/eg; return $v; }; } my $st = token_template( x => [ 'A' .. 'F' ], y => [ 'a' .. 'z' ], z => [ '0' .. '9' ], ); timethese(500_000, { 'String::Random' => sub { $sr->randpattern('xxyyyzyyzzz'); }, 'Session::Token' => sub { $st->('xxyyyzyyzzz'); }, }); Session-Token-1.503/META.yml0000664000175000017500000000117612755075053014172 0ustar dougdoug--- abstract: unknown author: - unknown build_requires: ExtUtils::MakeMaker: '0' configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 1 generated_by: 'ExtUtils::MakeMaker version 7.1, CPAN::Meta::Converter version 2.150005' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Session-Token no_index: directory: - t - inc requires: perl: '5.008000' resources: bugtracker: https://github.com/hoytech/Session-Token/issues repository: git://github.com/hoytech/Session-Token.git version: '1.503' x_serialization_backend: 'CPAN::Meta::YAML version 0.012' Session-Token-1.503/standard.h0000644000175000017500000000161612413466012014655 0ustar dougdoug/* ------------------------------------------------------------------------------ Standard definitions and types, Bob Jenkins ------------------------------------------------------------------------------ Modified a bit for Success::Token */ #ifndef STANDARD # define STANDARD // Support ILP32, LP64, and LLP64 typedef unsigned int ub4; typedef int word; #define bis(target,mask) ((target) |= (mask)) #define bic(target,mask) ((target) &= ~(mask)) #define bit(target,mask) ((target) & (mask)) #ifndef min # define min(a,b) (((a)<(b)) ? (a) : (b)) #endif /* min */ #ifndef max # define max(a,b) (((a)<(b)) ? (b) : (a)) #endif /* max */ #ifndef align # define align(a) (((ub4)a+(sizeof(void *)-1))&(~(sizeof(void *)-1))) #endif /* align */ #ifndef abs # define abs(a) (((a)>0) ? (a) : -(a)) #endif #ifndef TRUE # define TRUE 1 #endif #ifndef FALSE # define FALSE 0 #endif #endif /* STANDARD */ Session-Token-1.503/COPYING0000644000175000017500000000022212413466012013727 0ustar dougdougThis module is licensed under the same terms as perl itself. ISAAC code: By Bob Jenkins. My random number generator, ISAAC. Public Domain Session-Token-1.503/Makefile.PL0000644000175000017500000000157012413466012014655 0ustar dougdouguse 5.008; use strict; use ExtUtils::MakeMaker; my $deps = {}; $deps->{'Crypt::Random::Source::Strong::Win32'} = 0 if $^O =~ /mswin/i; my %args = ( NAME => 'Session::Token', VERSION_FROM => 'lib/Session/Token.pm', PREREQ_PM => $deps, LIBS => [''], DEFINE => '', INC => '-I.', OBJECT => 'Token.o randport.o', LICENSE => 'perl', dist => { PREOP => 'pod2text $(VERSION_FROM) > $(DISTVNAME)/README', }, MIN_PERL_VERSION => '5.8.0', ); my $eummv = eval ($ExtUtils::MakeMaker::VERSION); if ($eummv >= 6.45) { $args{META_MERGE} = { resources => { repository => 'git://github.com/hoytech/Session-Token.git', bugtracker => 'https://github.com/hoytech/Session-Token/issues', }, }; } WriteMakefile(%args); Session-Token-1.503/META.json0000664000175000017500000000212412755075053014334 0ustar dougdoug{ "abstract" : "unknown", "author" : [ "unknown" ], "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 7.1, CPAN::Meta::Converter version 2.150005", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "Session-Token", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "runtime" : { "requires" : { "perl" : "5.008000" } } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/hoytech/Session-Token/issues" }, "repository" : { "url" : "git://github.com/hoytech/Session-Token.git" } }, "version" : "1.503", "x_serialization_backend" : "JSON::PP version 2.27203" } Session-Token-1.503/t/0000755000175000017500000000000012755075053013155 5ustar dougdougSession-Token-1.503/t/00-system.t0000644000175000017500000000134712413466012015076 0ustar dougdouguse Session::Token; ## The point of this test is to verify some system assumptions and print ## out some diagnostic information in the test output. use strict; use Test::More tests => 1; my $little_endian = pack("I!", 1) =~ /^\x01/; my $int_size = length(pack("I!", 0)); my $long_size = length(pack("L!", 0)); my $pointer_size = length(pack("P", 0)); my $system_info = ($little_endian ? 'little' : 'big') . " endian, ILP: $int_size, $long_size, $pointer_size"; diag("System: $^O - $system_info"); ok(($int_size == 4 && $long_size == 4 && $pointer_size == 4) || ($int_size == 4 && $long_size == 8 && $pointer_size == 8) || ($int_size == 4 && $long_size == 4 && $pointer_size == 8), 'only ILP32, LP64, and LLP64 are supported'); Session-Token-1.503/t/reference.t0000644000175000017500000001167412413466012015277 0ustar dougdouguse Session::Token; ## The point of this test is to ensure that we are correctly using the ## ISAAC algorithm by comparing our output to Jenkins' null seeded ## test vector published on his website. use strict; use Test::More tests => 2; my $tokenctx = Session::Token->new( alphabet => [ map { chr } (0 .. 255) ], length => 4, seed => "\x00" x 1024, ## null seed ); ok($tokenctx, "created context"); my $output = ''; for (0..1) { for my $j (0 .. 255) { my $v = $tokenctx->get; $v = reverse $v; $output .= unpack('H*', $v); $output .= "\n" if ($j & 7) == 7; } } my $reference_output; { local $/; $reference_output = ; } is($output, $reference_output, "reference output matches"); ## This is reference null seeded output from http://www.burtleburtle.net/bob/rand/randvect.txt __DATA__ f650e4c8e448e96d98db2fb4f5fad54f433f1afbedec154ad837048746ca4f9a 5de3743e88381097f1d444eb823cedb66a83e1e04a5f6355c744243325890e2e 7452e31957161df638a824f3002ed71329f5544951c08d83d78cb99ea0cc74f3 8f651659cbc8b7c2f5f71c6912ad6419e5792e1b860536b809b3ce98d45d6d81 f3b2612917e38f8529cf72ce349947b0c998f9ffb5e13dae32ae2a2bf7cf814c 8ebfa303cf22e0640b923200eca4d58aef53cec4d0f7b37d9c411a2affdf8a80 b40e27bcb4d2f97644b89b08f37c71d51a70e7e90bdb9c3060dc5207b3c3f24b d7386806229749b54e232cd091dabc65a70e11018b87437e5781414fcdbc62e2 8107c9ff69d2e4ae3b18e752b143b6886f4e077295138769943c3c74afc17a97 0fd439636a529b0bd8c58a6aa8bcc22d2db35dfea7a2f4026cb167db538e1f4e 7275e2771d3b8e97ecc5dc9115e3a5b90369661430ab93ecac9fe69d7bc76811 60eda8da28833522d5295ebc5adb60e7f7e1cdd097166d14b67ec13a210f3925 64af0fef0d0286843aea3decb058bafbb8b0ccfcf2b5cc05e3a662d9814bc24c 2364a1aa37c0ed052b36505c451e7ec85d2a542fe43d0fbb91c8d92560d4d5f8 12a0594b9e8a51dacd49ebdb1b0dcdc1cd57c7f7e63444517ded386f2f36fa86 a6d1210133bc405db388d96cdb6dbe96fe29661c13edc0cbcb0eee4a70cc94ae de11ed340606cf9f3a6ce38923d74f4ea37f63ff917bdec2d73f72d40e7e0e67 3d77d9a213add9228891b3db01a9bd7056a001e3d51f093dcc033ce35ad0d3b0 34105a8c6a123f57bd2e50247364944be89b1a3b21835c4d9f39e2d9d405ded8 294d37e5bccaaeed35a124b56708a2bcb00960ba2a98121a4d8fae820bb3263f 12595a196a1075890809e49421c171ec884d682514c8009bb0b84e7b03fb88f4 28e7cb789388b13bdd2dc1d5848f520a07c28cd168a3935872c9137d127dd430 c613f1578c2f0d55f7d3f39f309bfb788406b13746c0a6f53718d59708607f04 76904b6d04db4e13cd7411a7b510ce0ebfc7f7ccb83f957afdfef62dc35e4580 3ff1e5244112d96c02c9b944d5990dfbe7e265810d9c7e7e826dfa8966f1e0ab 30bcc764eadebeaced35e5ee0c571a7de4f3a26af7f58f7badf6bc235d023e65 1ed3ff4eec46b0b6d2a93b51e75b41c97e315aeb61119a5a53245b7933f6d7b1 cae8deba50fc8194afa92a6dc87c80064188bfcd8bace62e78ffa5685597ec0f b4415f7d08294766ad56764309c36f903dde9f394a0a283c18080c8e080c79ec 79ae4c10cb9e15637cdd662f62d31911a4ca0cf15cf824cd3b708f991e16614c b6b9d7665de87abb7229ea81d5b2d75056e6cd21fe1e42d596da2655c2b9aa36 b8f6fd4a6a158d1001913fd3af7d1fb80b5e435f90c107576554abda7a68710f 82ac484fd7e1c7be95c85eaa94a302f44d3cfbda786b29081010b27582d53d12 21e2a51c3d1e9150b059261dd0638e1a31860f0581f2864dff4cfc350451516d bd086f26bc5654c165dfa427a82427f5582e3014b8d2486dc79a17499a1d7745 8766bb541e04a7f73d3dff8ad5ec6bf4dbef7d9f36ec0ea31feb2e4f15cfcc5c d8c423fbd0ef3cc9eb244925ba5590c8a5f48ac433c5321c613b67b2479c3a22 e21339cc10d210aa931dd7e2ef05ee06b82f2703a385cb2c5d67133c877eb7b4 1e3437f75afb43ae53c078f394d904811d96458908063a85e13222281956b1e5 31860f132e7b022f21182ca396f703ac46819e2e0d28fe523724d4dca0eabe6b c66699fdc6112fdd19c1e69c04d3658a4b55dd9931907d62f854b5224d678f26 22ae0582eafed133e4a51d2184bd6dd6c1a513753f28ee63fb737b1a70a1660e 8a8dfaa31be79937f7476978513c1764531ac6bf12c06908001cdb951a4b6a53 d067fce512b2cfb69ddb477f740e006639ddf25acc8bfa2df1b20eaf64f2632c 9783cdee63bfd4d80084cfe575f4e9e219b48fd06c48ddd87a36af9371865c4c 9ce0199d867027d72cb7b77f84ef01da72f5972f040f7074df9afa29c921f94e 75c08a3618c1ef9ad649a428c5b719378a30738ad97cd348858129a6239e3b0a bbb8abc480fac4c2ecfcf20bd9d711f9e2a4ef71b5fe87c0be8b06b2aafef5a7 9c15db3b0aeb81654389a84a253b1d7a19047c797cdc78a2d20adf0356f55a71 3e730fa8fd8650d8959e234eb7546681dad1b22a142a6e858ef4bce668235b9d 85a13f8574096ae7a949bea229322d0dd568385882846526403dae086dd1943a e1279bff9e7e4f041c3a4524484525e481d4cc5fe24124c0037464c0bf1bd691 26ceb003275ead3ac5bde90826414ff3a30519add7b43abe2ce5d3d588412761 97ca2070e5fbb9c7276df0b4308f751f37a97df6c9cd808cfe4cb3803d469303 aee19096c0d5d42a4e823ad3f5f9cc3b4286619c9ca45e1c66c97340891aec49 45bae606c798f04752649d6cce86fdfc80c6e402d6ec2f2b27c822821fe26ce0 92f57ea7de462f4d07497cae5a48755c721502dd6cbe7935836d80039ead7f70 9ab3a42f4c8652d632e39273e8fa38601da4f25a0cd6ef8102503f7d8854a0a1 9a30c4e88815715305efe29457c4c9252887d96fc1a71e3ce9f841632d0985de d21e796c6fb5ce5602614abfc3c7be2cb54fed6fa617a083c3142d8f6079e4ce ceffc1471d0cb81bdc153e5fe36ef5bbd531161a165b10157aa114ed3f7579b3 f7f395f1bc6172c7a86f875e0e6c51b3cdfec2af73c0e762824c2009c5a87748 94d401258aba3ffbd32be0608c17eff021e2547e07cffad905340e15f3310c92 9d8d190886ba527ff943f672ef73fbf046d95ca5c54cd95b9d855e894bb5af29 Session-Token-1.503/t/seed-coverage.t0000644000175000017500000000101312413466012016034 0ustar dougdouguse Session::Token; ## The point of this test is to verify that every byte of the seed is ## propogated to the ISAAC context and therefore impacts the token ## sequence. use strict; use Test::More tests => 1; my $tokens = {}; my $token_count = 0; foreach my $seed ("\x00" x 1024, "\x00" x 1023 . "\x01", "\x01" . "\x00" x 1023) { my $ctx = Session::Token->new( seed => $seed, ); for my $i (1..2000) { $tokens->{$ctx->get} = 1; $token_count++; } } is(scalar keys %$tokens, $token_count, "no uniques"); Session-Token-1.503/t/kernel-seeding.t0000644000175000017500000000106412413466012016225 0ustar dougdouguse Session::Token; ## The point of this test is to verify that the kernel seeding interface ## (ie /dev/urandom or Crypt::Random::Source::Strong::Win32) is working ## and gives us a different stream each time. Note that this test is ## technically non-deterministic but a failure is very unlikely. use strict; use Test::More tests => 1; my $tokens = {}; my $token_count = 0; for (1..2) { my $ctx = Session::Token->new; for my $i (1..2000) { $tokens->{$ctx->get} = 1; $token_count++; } } is(scalar keys %$tokens, $token_count, "no uniques"); Session-Token-1.503/t/alphabets.t0000644000175000017500000000266112755074621015312 0ustar dougdouguse Session::Token; ## The point of this test is to test the various alphabet, length, and ## entropy interfaces are behaving according to the design. It also ## checks that passing args in as a hash-ref works. use strict; use Test::More tests => 10; my $ctx; ## default entropy is 128 bits and default alphabet is length 62 $ctx = Session::Token->new; is(length($ctx->get), 22); ## can specify length directly $ctx = Session::Token->new( alphabet => '01', length => 30, ); is(length($ctx->get), 30); like($ctx->get, qr/^[01]+$/); $ctx = Session::Token->new( alphabet => '01', length => 100000, ); is(length($ctx->get), 100000); $ctx = Session::Token->new( alphabet => '01', entropy => 4, ); is(length($ctx->get), 4); $ctx = Session::Token->new( alphabet => '01', entropy => 8, ); is(length($ctx->get), 8); $ctx = Session::Token->new( alphabet => '01', entropy => 8.1, ); is(length($ctx->get), 9); ## verify hash-ref interface $ctx = Session::Token->new({ alphabet => '01', entropy => 8.1, }); is(length($ctx->get), 9); ## alphabet has 1.585 bits of entropy, 10/1.585 = 6.309, round up to 7 chars $ctx = Session::Token->new( alphabet => '012', entropy => 10, ); is(length($ctx->get), 7); ## alphabet has 4.700 bits of entropy, 256/4.700 = 54.463, round up to 55 chars ## - also tests passing the alphabet in as an array ref instead of string $ctx = Session::Token->new( alphabet => ['a' .. 'z'], entropy => 256, ); is(length($ctx->get), 55); Session-Token-1.503/t/no-mod-bias.t0000644000175000017500000000546312413466012015445 0ustar dougdouguse Session::Token; ## The point of this test is to verify that we aren't doing anything stupid ## like a naive "mod" over our alphabet which would introduce character bias. =pod This test isn't so much a unit-test as a simple tool for verifying that generated values fall within a certain acceptable range of bias. However, this test is distributed to use the null seed because there is a chance that some short-term run of random values would trigger a failure (they are random numbers after all). Of course, as the total number of values generated increases, we get more confident that the values generated are unbiased. Because Session::Token extracts bytes from the PRNG, any bias in our characters will be fairly pronounced. The most difficult alphabet to detect bias in would be something like this: [ map { chr } (0, 0 .. 254) ] Note how the 0 is repeated twice. P(0) = 2/256 P(1) = 1/256 ... P(254) = 1/256 Algorithms that extract entire words from the PRNG and use these words in the modulus computation will have more difficult to detect bias. For further investigation, set the S_T_NON_DETERMINISTIC environment variable to have it run with a random seed (will fail every now and again due to pure chance). The default alphabet, total number of characters to generate, and tolerance threshold can be controlled with S_T_ALPHABET, S_T_TOTAL, and S_T_TOLERANCE. Example: To see this test fail with the example used in L's mod bias example, run the tests with the alphabet set to "aabc": $ S_T_ALPHABET=aabc make test ... t/no-mod-bias.t .... Not within tolerance: a (97): 0.99636 > 0.01 at t/no-mod-bias.t line 95. =cut use strict; use Test::More tests => 1; my $seed = $ENV{S_T_NON_DETERMINISTIC} ? undef : ("\x00" x 1024), my $alphabet = $ENV{S_T_ALPHABET} || 'abc', #my $alphabet = $ENV{S_T_ALPHABET} || (join "", ( map { chr } (255, 0 .. 254) )); my $total = $ENV{S_T_TOTAL} || 100000; my $tolerance = $ENV{S_T_TOLERANCE} || 0.01; my $ideal_per_bucket = $total / length($alphabet); my $ctx = Session::Token->new( alphabet => $alphabet, length => $total, seed => $seed, ); my $token = $ctx->get; my $counts = {}; $token =~ s/(.)/$counts->{$1}++/seg; my $total_verification = 0; foreach my $c (split //, $alphabet) { $total_verification += $counts->{$c}; verify_tolerance($c); } is($total_verification, $total, 'no bias detected'); done_testing(); sub verify_tolerance { my $c = shift; my $deviation = abs(1 - ($counts->{$c} / $ideal_per_bucket)); my $printable_c = (ord($c) >= 32 && ord($c) <= 126) ? $c : '*unprintable*'; print "Deviation of $printable_c (" . ord($c) . "): $deviation\n"; die "Not within tolerance: $printable_c (" . ord($c) . "): $deviation > $tolerance" if $deviation > $tolerance; } Session-Token-1.503/MANIFEST.SKIP0000644000175000017500000000022712413466012014577 0ustar dougdougMANIFEST.bak Makefile.old Makefile$ README.pod .gitignore .git/ blib/ pm_to_blib Token.o Token.c Token.bs randport.o MYMETA.* Session-Token.*tar[.]gz$