@@ -15,45 +15,149 @@ pub use self::imp::OsRng;
15
15
16
16
#[ cfg( all( unix, not( target_os = "ios" ) ) ) ]
17
17
mod imp {
18
+ extern crate libc;
19
+
18
20
use io:: { IoResult , File } ;
19
21
use path:: Path ;
20
22
use rand:: Rng ;
21
23
use rand:: reader:: ReaderRng ;
22
24
use result:: { Ok , Err } ;
25
+ use slice:: { ImmutableSlice , MutableSlice } ;
26
+ use mem;
27
+ use os:: errno;
28
+
29
+ #[ cfg( all( target_os = "linux" ,
30
+ any( target_arch = "x86_64" , target_arch = "x86" , target_arch = "arm" ) ) ) ]
31
+ fn getrandom ( buf : & mut [ u8 ] ) -> libc:: c_long {
32
+ extern "C" {
33
+ fn syscall ( number : libc:: c_long , ...) -> libc:: c_long ;
34
+ }
35
+
36
+ #[ cfg( target_arch = "x86_64" ) ]
37
+ const NR_GETRANDOM : libc:: c_long = 318 ;
38
+ #[ cfg( target_arch = "x86" ) ]
39
+ const NR_GETRANDOM : libc:: c_long = 355 ;
40
+ #[ cfg( target_arch = "arm" ) ]
41
+ const NR_GETRANDOM : libc:: c_long = 384 ;
42
+
43
+ unsafe {
44
+ syscall ( NR_GETRANDOM , buf. as_mut_ptr ( ) , buf. len ( ) , 0 u)
45
+ }
46
+ }
47
+
48
+ #[ cfg( not( all( target_os = "linux" ,
49
+ any( target_arch = "x86_64" , target_arch = "x86" , target_arch = "arm" ) ) ) ) ]
50
+ fn getrandom ( _buf : & mut [ u8 ] ) -> libc:: c_long { -1 }
51
+
52
+ fn getrandom_fill_bytes ( v : & mut [ u8 ] ) {
53
+ let mut read = 0 ;
54
+ let len = v. len ( ) ;
55
+ while read < len {
56
+ let result = getrandom ( v[ mut read..] ) ;
57
+ if result == -1 {
58
+ let err = errno ( ) as libc:: c_int ;
59
+ if err == libc:: EINTR {
60
+ continue ;
61
+ } else {
62
+ panic ! ( "unexpected getrandom error: {}" , err) ;
63
+ }
64
+ } else {
65
+ read += result as uint ;
66
+ }
67
+ }
68
+ }
69
+
70
+ fn getrandom_next_u32 ( ) -> u32 {
71
+ let mut buf: [ u8 , ..4 ] = [ 0u8 , ..4 ] ;
72
+ getrandom_fill_bytes ( & mut buf) ;
73
+ unsafe { mem:: transmute :: < [ u8 , ..4 ] , u32 > ( buf) }
74
+ }
75
+
76
+ fn getrandom_next_u64 ( ) -> u64 {
77
+ let mut buf: [ u8 , ..8 ] = [ 0u8 , ..8 ] ;
78
+ getrandom_fill_bytes ( & mut buf) ;
79
+ unsafe { mem:: transmute :: < [ u8 , ..8 ] , u64 > ( buf) }
80
+ }
81
+
82
+ #[ cfg( all( target_os = "linux" ,
83
+ any( target_arch = "x86_64" , target_arch = "x86" , target_arch = "arm" ) ) ) ]
84
+ fn is_getrandom_available ( ) -> bool {
85
+ use sync:: atomic:: { AtomicBool , INIT_ATOMIC_BOOL , Relaxed } ;
86
+
87
+ static GETRANDOM_CHECKED : AtomicBool = INIT_ATOMIC_BOOL ;
88
+ static GETRANDOM_AVAILABLE : AtomicBool = INIT_ATOMIC_BOOL ;
89
+
90
+ if !GETRANDOM_CHECKED . load ( Relaxed ) {
91
+ let mut buf: [ u8 , ..0 ] = [ ] ;
92
+ let result = getrandom ( & mut buf) ;
93
+ let available = if result == -1 {
94
+ let err = errno ( ) as libc:: c_int ;
95
+ err != libc:: ENOSYS
96
+ } else {
97
+ true
98
+ } ;
99
+ GETRANDOM_AVAILABLE . store ( available, Relaxed ) ;
100
+ GETRANDOM_CHECKED . store ( true , Relaxed ) ;
101
+ available
102
+ } else {
103
+ GETRANDOM_AVAILABLE . load ( Relaxed )
104
+ }
105
+ }
106
+
107
+ #[ cfg( not( all( target_os = "linux" ,
108
+ any( target_arch = "x86_64" , target_arch = "x86" , target_arch = "arm" ) ) ) ) ]
109
+ fn is_getrandom_available ( ) -> bool { false }
23
110
24
111
/// A random number generator that retrieves randomness straight from
25
112
/// the operating system. Platform sources:
26
113
///
27
114
/// - Unix-like systems (Linux, Android, Mac OSX): read directly from
28
- /// `/dev/urandom`.
115
+ /// `/dev/urandom`, or from `getrandom(2)` system call if available .
29
116
/// - Windows: calls `CryptGenRandom`, using the default cryptographic
30
117
/// service provider with the `PROV_RSA_FULL` type.
31
118
/// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed
32
119
/// This does not block.
33
- #[ cfg( unix) ]
34
120
pub struct OsRng {
35
- inner : ReaderRng < File >
121
+ inner : OsRngInner ,
122
+ }
123
+
124
+ enum OsRngInner {
125
+ OsGetrandomRng ,
126
+ OsReaderRng ( ReaderRng < File > ) ,
36
127
}
37
128
38
129
impl OsRng {
39
130
/// Create a new `OsRng`.
40
131
pub fn new ( ) -> IoResult < OsRng > {
132
+ if is_getrandom_available ( ) {
133
+ return Ok ( OsRng { inner : OsGetrandomRng } ) ;
134
+ }
135
+
41
136
let reader = try!( File :: open ( & Path :: new ( "/dev/urandom" ) ) ) ;
42
137
let reader_rng = ReaderRng :: new ( reader) ;
43
138
44
- Ok ( OsRng { inner : reader_rng } )
139
+ Ok ( OsRng { inner : OsReaderRng ( reader_rng) } )
45
140
}
46
141
}
47
142
48
143
impl Rng for OsRng {
49
144
fn next_u32 ( & mut self ) -> u32 {
50
- self . inner . next_u32 ( )
145
+ match self . inner {
146
+ OsGetrandomRng => getrandom_next_u32 ( ) ,
147
+ OsReaderRng ( ref mut rng) => rng. next_u32 ( ) ,
148
+ }
51
149
}
52
150
fn next_u64 ( & mut self ) -> u64 {
53
- self . inner . next_u64 ( )
151
+ match self . inner {
152
+ OsGetrandomRng => getrandom_next_u64 ( ) ,
153
+ OsReaderRng ( ref mut rng) => rng. next_u64 ( ) ,
154
+ }
54
155
}
55
156
fn fill_bytes ( & mut self , v : & mut [ u8 ] ) {
56
- self . inner . fill_bytes ( v)
157
+ match self . inner {
158
+ OsGetrandomRng => getrandom_fill_bytes ( v) ,
159
+ OsReaderRng ( ref mut rng) => rng. fill_bytes ( v)
160
+ }
57
161
}
58
162
}
59
163
}
@@ -75,7 +179,7 @@ mod imp {
75
179
/// the operating system. Platform sources:
76
180
///
77
181
/// - Unix-like systems (Linux, Android, Mac OSX): read directly from
78
- /// `/dev/urandom`.
182
+ /// `/dev/urandom`, or from `getrandom(2)` system call if available .
79
183
/// - Windows: calls `CryptGenRandom`, using the default cryptographic
80
184
/// service provider with the `PROV_RSA_FULL` type.
81
185
/// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed
@@ -145,10 +249,10 @@ mod imp {
145
249
/// the operating system. Platform sources:
146
250
///
147
251
/// - Unix-like systems (Linux, Android, Mac OSX): read directly from
148
- /// `/dev/urandom`.
252
+ /// `/dev/urandom`, or from `getrandom(2)` system call if available .
149
253
/// - Windows: calls `CryptGenRandom`, using the default cryptographic
150
254
/// service provider with the `PROV_RSA_FULL` type.
151
- ///
255
+ /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed
152
256
/// This does not block.
153
257
pub struct OsRng {
154
258
hcryptprov : HCRYPTPROV
0 commit comments