OSDN Git Service

4b6eac4ace58a7468d416a2c9d83762bfae145d0
[mingw/mingw-org-wsl.git] / mingwrt / mingwex / cryptnam.c
1 /*
2  * cryptnam.c
3  *
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.
9  *
10  * $Id$
11  *
12  * Written by Keith Marshall  <keithmarshall@users.sourceforge.net>
13  * Copyright (C) 2013, 2014, 2018, MinGW.org Project.
14  *
15  *
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:
22  *
23  * The above copyright notice, this permission notice, and the following
24  * disclaimer shall be included in all copies or substantial portions of
25  * the Software.
26  *
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.
34  *
35  */
36 #define WIN32_LEAN_AND_MEAN
37
38 #include <limits.h>
39 #include <windows.h>
40 #include <wincrypt.h>
41 #include <string.h>
42
43 #define CRYPTO_INLINE  static __inline__ __attribute__((__always_inline__))
44
45 CRYPTO_INLINE HCRYPTPROV crypto_provider( void )
46 #define RSA_MODE( OPT, FLAG )  PROV_RSA_##OPT, CRYPT_##FLAG
47 {
48   /* Helper to establish a cryptographic provider context for the
49    * cryptographically secure random number generator.
50    *
51    * At the outset, this provider requires initialization.
52    */
53   static HCRYPTPROV id = (HCRYPTPROV)(0);
54
55   /* On second, and subsequent calls, it should already have been
56    * initialized...
57    */
58   if( id != (HCRYPTPROV)(0) )
59     /*
60      * ...so, simply return the saved context handle...
61      */
62     return id;
63
64   /* If we're still here, this must be the first call, (or any
65    * preceding call failed to initialize the context); initialize
66    * it now, and if successful...
67    */
68   if( CryptAcquireContext( &id, NULL, NULL, RSA_MODE( FULL, VERIFYCONTEXT ) ) )
69     /*
70      * ...return the now-initialized context handle.
71      */
72     return id;
73
74   /* And finally, if we ever get to here, the context remains
75    * uninitialized; ensure that it remains marked as such, and
76    * return the uninitialized context handle.
77    */
78   return id = (HCRYPTPROV)(0);
79 }
80
81 CRYPTO_INLINE void *crypto_randomize( void *buf, size_t buflen )
82 {
83   /* Helper to fill a specified buffer, of specified length,
84    * with cryptographically secure random bytes...
85    */
86   if( CryptGenRandom( crypto_provider(), buflen, buf ) )
87     /*
88      * ...returning a pointer to the buffer, when successful...
89      */
90     return buf;
91
92   /* ...or nothing, otherwise.
93    */
94   return NULL;
95 }
96
97 CRYPTO_INLINE char *crypto_random_filename_char( char *caret )
98 {
99   /* Helper to generate a random sequence of characters, suitable for
100    * use in file names; although there are other valid possibilities, we
101    * restrict this to the set of lower case ASCII alpha-numerics, giving
102    * us 36 degrees of freedom for each character; (note that we cannot
103    * gain additional degrees of freedom by using mixed case, because
104    * the MS-Windows file system is case-insensitive).
105    */
106   const unsigned char span = 'z' - 'a' + 1 + '9' - '0' + 1;
107
108   /* We also wish to ensure that each of the possible 36 characters has
109    * an equal probability of selection; thus, of the UCHAR_MAX possible
110    * raw byte selections, we want to consider at most the largest even
111    * multiple of the 36 character span, which lies below the UCHAR_MAX
112    * limit, (which, since zero is a valid choice, is one less than the
113    * result of discounting the remainder from modulo division).
114    */
115   const unsigned char max = UCHAR_MAX - (UCHAR_MAX % span) - 1;
116
117   /* Deposit randomly selected characters at the "caret" location...
118    */
119   do { if( crypto_randomize( caret, sizeof( unsigned char ) ) == NULL )
120          /*
121           * ...bailing out, on any failure of the sequence generator...
122           */
123          return NULL;
124
125        /* ...until we get one which is within the largest possible
126         * subset which yields equal probabilty to each outcome, when
127         * reduced modulo the 36 available degrees of freedom.
128         */
129      } while( *caret > max );
130
131   /* Perform the modulo 36 reduction, and offset the result into the
132    * alpha-numeric character range...
133    */
134   *caret = '0' + (*caret % span);
135   /*
136    * ...while discounting those unsuitable characters which lie within
137    * the range, between '9' and 'a' exclusively.
138    */
139   if( *caret > '9' ) *caret += 'a' - '9' - 1;
140
141   /* Finally, return the "caret" location, indicating the successful
142    * transformation of the character in that position.
143    */
144   return caret;
145 }
146
147 char *__mingw_crypto_tmpname( char *template )
148 {
149   /* Helper function, based on Microsoft's wincrypt API, to construct
150    * the candidate names for temporary files, both in a less predictable
151    * manner than Microsoft's _mktemp() function, and without suffering
152    * its inherent limitation of allowing no more than 26 file names
153    * per template per process thread.
154    *
155    * We begin by locating the position, within the given template,
156    * where the string of six replaceable 'XXXXXX's should begin.
157    */
158   char *tail = template + strlen( template ) - 6;
159
160   /* Provided this appears sane -- i.e. it at least doesn't place the
161    * six character "tail" before the start of the template itself...
162    */
163   if( tail >= template )
164   {
165     /* ...then, walk over each of the six bytes of the "tail", until
166      * we reach the NUL terminator...
167      */
168     while( *tail )
169     {
170       /* ...checking that each byte is initially ASCII 'X', as POSIX
171        * requires them to be; (note that we don't consider that these
172        * may be MBCS trail bytes, since the required 'X' is a single
173        * byte in an MBCS representation anyway)...
174        */
175       if( (*tail != 'X') || (crypto_random_filename_char( tail++ ) == NULL) )
176         /*
177          * ...bailing out, and returning nothing, if not.
178          */
179         return NULL;
180     }
181   }
182   /* Finally, when we have successfully replaced all six 'XXXXXX's,
183    * we return the modified template, in place.
184    */
185   return template;
186 }
187
188 /* $RCSfile$: end of file */