4 * Implementation of a cryptographically secure random character sequence
5 * generator; this is specifically tailored to satisfy the requirement for
6 * replacement of the sequence of six 'XXXXXX's, within the templates for
7 * the file name, or the directory name, in MinGW.org implementations of
8 * the mkstemp(3) and mkdtemp(3) functions, respectively.
10 * $Id: cryptnam.c,v 5f021e118870 2020/07/20 19:17:27 keith $
12 * Written by Keith Marshall <keith@users.osdn.me>
13 * Copyright (C) 2013, 2014, 2018-2020, MinGW.org Project.
16 * Permission is hereby granted, free of charge, to any person obtaining a
17 * copy of this software and associated documentation files (the "Software"),
18 * to deal in the Software without restriction, including without limitation
19 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
20 * and/or sell copies of the Software, and to permit persons to whom the
21 * Software is furnished to do so, subject to the following conditions:
23 * The above copyright notice, this permission notice, and the following
24 * disclaimer shall be included in all copies or substantial portions of
27 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
28 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
30 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
32 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OF OR OTHER
33 * DEALINGS IN THE SOFTWARE.
39 #define CRYPTO_INLINE static __inline__ __attribute__((__always_inline__))
41 void *__mingw_crypto_randomize( void *, size_t );
42 CRYPTO_INLINE void *crypto_randomize( void *buf, size_t buflen )
43 { return __mingw_crypto_randomize( buf, buflen ); }
46 unsigned char *crypto_random_filename_char( unsigned char *caret )
48 /* Helper to generate a random sequence of characters, suitable for
49 * use in file names; although there are other valid possibilities, we
50 * restrict this to the set of lower case ASCII alpha-numerics, giving
51 * us 36 degrees of freedom for each character; (note that we cannot
52 * gain additional degrees of freedom by using mixed case, because
53 * the MS-Windows file system is case-insensitive).
55 const unsigned char span = 'z' - 'a' + 1 + '9' - '0' + 1;
57 /* We also wish to ensure that each of the possible 36 characters has
58 * an equal probability of selection; thus, of the UCHAR_MAX possible
59 * raw byte selections, we want to consider at most the largest even
60 * multiple of the 36 character span, which lies below the UCHAR_MAX
61 * limit, (which, since zero is a valid choice, is one less than the
62 * result of discounting the remainder from modulo division).
64 const unsigned char max = UCHAR_MAX - (UCHAR_MAX % span) - 1;
66 /* Deposit randomly selected characters at the "caret" location...
68 do { if( crypto_randomize( caret, sizeof( unsigned char ) ) == NULL )
70 * ...bailing out, on any failure of the sequence generator...
74 /* ...until we get one which is within the largest possible
75 * subset which yields equal probabilty to each outcome, when
76 * reduced modulo the 36 available degrees of freedom.
78 } while( *caret > max );
80 /* Perform the modulo 36 reduction, and offset the result into the
81 * alpha-numeric character range...
83 *caret = '0' + (*caret % span);
85 * ...while discounting those unsuitable characters which lie within
86 * the range, between '9' and 'a' exclusively.
88 if( *caret > '9' ) *caret += 'a' - '9' - 1;
90 /* Finally, return the "caret" location, indicating the successful
91 * transformation of the character in that position.
96 char *__mingw_crypto_tmpname( char *template )
98 /* Helper function, based on Microsoft's wincrypt API, to construct
99 * the candidate names for temporary files, both in a less predictable
100 * manner than Microsoft's _mktemp() function, and without suffering
101 * its inherent limitation of allowing no more than 26 file names
102 * per template per process thread.
104 * We begin by locating the position, within the given template,
105 * where the string of six replaceable 'XXXXXX's should begin.
107 unsigned char *tail = (unsigned char *)(template) + strlen( template ) - 6;
109 /* Provided this appears sane -- i.e. it at least doesn't place the
110 * six character "tail" before the start of the template itself...
112 if( (char *)(tail) >= template )
114 /* ...then, walk over each of the six bytes of the "tail", until
115 * we reach the NUL terminator...
119 /* ...checking that each byte is initially ASCII 'X', as POSIX
120 * requires them to be; (note that we don't consider that these
121 * may be MBCS trail bytes, since the required 'X' is a single
122 * byte in an MBCS representation anyway)...
124 if( (*tail != 'X') || (crypto_random_filename_char( tail++ ) == NULL) )
126 * ...bailing out, and returning nothing, if not.
131 /* Finally, when we have successfully replaced all six 'XXXXXX's,
132 * we return the modified template, in place.
137 /* $RCSfile: cryptnam.c,v $: end of file */