Imported Upstream version 1.10.2
[platform/upstream/krb5.git] / src / lib / crypto / krb / prng.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * Copyright (C) 2001, 2002, 2004, 2007, 2008, 2010 by the Massachusetts Institute of Technology.
4  * All rights reserved.
5  *
6  *
7  * Export of this software from the United States of America may require
8  * a specific license from the United States Government.  It is the
9  * responsibility of any person or organization contemplating export to
10  * obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26
27 #include "crypto_int.h"
28
29 krb5_error_code KRB5_CALLCONV
30 krb5_c_random_seed(krb5_context context, krb5_data *data)
31 {
32     return krb5_c_random_add_entropy(context, KRB5_C_RANDSOURCE_OLDAPI, data);
33 }
34
35 /* Routines to get entropy from the OS. */
36 #if defined(_WIN32)
37
38 krb5_boolean
39 k5_get_os_entropy(unsigned char *buf, size_t len)
40 {
41     krb5_boolean result;
42     HCRYPTPROV provider;
43
44     if (!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL,
45                              CRYPT_VERIFYCONTEXT))
46         return FALSE;
47     result = CryptGenRandom(provider, len, buf);
48     (void)CryptReleaseContext(provider, 0);
49     return result;
50 }
51
52 krb5_error_code KRB5_CALLCONV
53 krb5_c_random_os_entropy(krb5_context context, int strong, int *success)
54 {
55     int oursuccess = 0;
56     char buf[1024];
57     krb5_data data = make_data(buf, sizeof(buf));
58
59     if (k5_get_os_entropy(buf, sizeof(buf)) &&
60         krb5_c_random_add_entropy(context, KRB5_C_RANDSOURCE_OSRAND,
61                                   &data) == 0)
62         oursuccess = 1;
63     if (success != NULL)
64         *success = oursuccess;
65     return 0;
66 }
67
68 #else /* not Windows */
69 #ifdef HAVE_UNISTD_H
70 #include <unistd.h>
71 #endif
72 #ifdef HAVE_SYS_STAT_H
73 #include <sys/stat.h>
74 #endif
75
76 /* Open device, ensure that it is not a regular file, and read entropy.  Return
77  * true on success, false on failure. */
78 static krb5_boolean
79 read_entropy_from_device(const char *device, unsigned char *buf, size_t len)
80 {
81     struct stat sb;
82     int fd;
83     unsigned char *bp;
84     size_t left;
85     ssize_t count;
86     krb5_boolean result = FALSE;
87
88     fd = open(device, O_RDONLY);
89     if (fd == -1)
90         return FALSE;
91     set_cloexec_fd(fd);
92     if (fstat(fd, &sb) == -1 || S_ISREG(sb.st_mode))
93         goto cleanup;
94
95     for (bp = buf, left = len; left > 0;) {
96         count = read(fd, bp, left);
97         if (count <= 0)
98             goto cleanup;
99         left -= count;
100         bp += count;
101     }
102     result = TRUE;
103
104 cleanup:
105     close(fd);
106     return result;
107 }
108
109 krb5_boolean
110 k5_get_os_entropy(unsigned char *buf, size_t len)
111 {
112     return read_entropy_from_device("/dev/urandom", buf, len);
113 }
114
115 /* Read entropy from device and contribute it to the PRNG.  Returns true on
116  * success. */
117 static krb5_boolean
118 add_entropy_from_device(krb5_context context, const char *device)
119 {
120     krb5_data data;
121     unsigned char buf[64];
122
123     if (!read_entropy_from_device(device, buf, sizeof(buf)))
124         return FALSE;
125     data = make_data(buf, sizeof(buf));
126     return (krb5_c_random_add_entropy(context, KRB5_C_RANDSOURCE_OSRAND,
127                                       &data) == 0);
128 }
129
130 krb5_error_code KRB5_CALLCONV
131 krb5_c_random_os_entropy(krb5_context context, int strong, int *success)
132 {
133     int unused;
134     int *oursuccess = (success != NULL) ? success : &unused;
135
136     *oursuccess = 0;
137     /* If we are getting strong data then try that first.  We are
138        guaranteed to cause a reseed of some kind if strong is true and
139        we have both /dev/random and /dev/urandom.  We want the strong
140        data included in the reseed so we get it first.*/
141     if (strong) {
142         if (add_entropy_from_device(context, "/dev/random"))
143             *oursuccess = 1;
144     }
145     if (add_entropy_from_device(context, "/dev/urandom"))
146         *oursuccess = 1;
147     return 0;
148 }
149
150 #endif /* not Windows */