diff options
Diffstat (limited to 'lib/std/sysrand.nim')
-rw-r--r-- | lib/std/sysrand.nim | 95 |
1 files changed, 50 insertions, 45 deletions
diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index c9908143c..6f2c6b0c1 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -10,23 +10,23 @@ ## .. warning:: This module was added in Nim 1.6. If you are using it for cryptographic purposes, ## keep in mind that so far this has not been audited by any security professionals, ## therefore may not be secure. -## +## ## `std/sysrand` generates random numbers from a secure source provided by the operating system. -## It is also called Cryptographically secure pseudorandom number generator. -## It should be unpredictable enough for cryptographic applications, +## It is a cryptographically secure pseudorandom number generator +## and should be unpredictable enough for cryptographic applications, ## though its exact quality depends on the OS implementation. ## -## | Targets | Implementation| -## | :--- | ----: | -## | Windows | `BCryptGenRandom`_ | -## | Linux | `getrandom`_ | -## | MacOSX | `getentropy`_ | -## | IOS | `SecRandomCopyBytes`_ | -## | OpenBSD | `getentropy openbsd`_ | -## | FreeBSD | `getrandom freebsd`_ | -## | JS(Web Browser) | `getRandomValues`_ | -## | Nodejs | `randomFillSync`_ | -## | Other Unix platforms | `/dev/urandom`_ | +## | Targets | Implementation | +## | :--- | ----: | +## | Windows | `BCryptGenRandom`_ | +## | Linux | `getrandom`_ | +## | MacOSX | `SecRandomCopyBytes`_ | +## | iOS | `SecRandomCopyBytes`_ | +## | OpenBSD | `getentropy openbsd`_ | +## | FreeBSD | `getrandom freebsd`_ | +## | JS (Web Browser) | `getRandomValues`_ | +## | Node.js | `randomFillSync`_ | +## | Other Unix platforms | `/dev/urandom`_ | ## ## .. _BCryptGenRandom: https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom ## .. _getrandom: https://man7.org/linux/man-pages/man2/getrandom.2.html @@ -38,6 +38,11 @@ ## .. _randomFillSync: https://nodejs.org/api/crypto.html#crypto_crypto_randomfillsync_buffer_offset_size ## .. _/dev/urandom: https://en.wikipedia.org/wiki//dev/random ## +## On a Linux target, a call to the `getrandom` syscall can be avoided (e.g. +## for targets running kernel version < 3.17) by passing a compile flag of +## `-d:nimNoGetRandom`. If this flag is passed, sysrand will use `/dev/urandom` +## as with any other POSIX compliant OS. +## runnableExamples: doAssert urandom(0).len == 0 @@ -52,13 +57,16 @@ runnableExamples: when not defined(js): - import std/os + import std/oserrors when defined(posix): import std/posix +when defined(nimPreviewSlimSystem): + import std/assertions + const - batchImplOS = defined(freebsd) or defined(openbsd) or (defined(macosx) and not defined(ios)) + batchImplOS = defined(freebsd) or defined(openbsd) or defined(zephyr) batchSize {.used.} = 256 when batchImplOS: @@ -132,7 +140,7 @@ elif defined(windows): type PVOID = pointer BCRYPT_ALG_HANDLE = PVOID - PUCHAR = ptr cuchar + PUCHAR = ptr uint8 NTSTATUS = clong ULONG = culong @@ -159,15 +167,16 @@ elif defined(windows): result = randomBytes(addr dest[0], size) -elif defined(linux): - # TODO using let, pending bootstrap >= 1.4.0 - var SYS_getrandom {.importc: "SYS_getrandom", header: "<sys/syscall.h>".}: clong +elif defined(linux) and not defined(nimNoGetRandom) and not defined(emscripten): + when (NimMajor, NimMinor) >= (1, 4): + let SYS_getrandom {.importc: "SYS_getrandom", header: "<sys/syscall.h>".}: clong + else: + var SYS_getrandom {.importc: "SYS_getrandom", header: "<sys/syscall.h>".}: clong const syscallHeader = """#include <unistd.h> #include <sys/syscall.h>""" - proc syscall( - n: clong, buf: pointer, bufLen: cint, flags: cuint - ): clong {.importc: "syscall", header: syscallHeader.} + proc syscall(n: clong): clong {. + importc: "syscall", varargs, header: syscallHeader.} # When reading from the urandom source (GRND_RANDOM is not set), # getrandom() will block until the entropy pool has been # initialized (unless the GRND_NONBLOCK flag was specified). If a @@ -183,25 +192,34 @@ elif defined(linux): while result < size: let readBytes = syscall(SYS_getrandom, addr dest[result], cint(size - result), 0).int if readBytes == 0: - doAssert false + raiseAssert "unreachable" elif readBytes > 0: inc(result, readBytes) else: - if osLastError().int in {EINTR, EAGAIN}: - discard + if osLastError().cint in [EINTR, EAGAIN]: discard else: result = -1 break elif defined(openbsd): proc getentropy(p: pointer, size: cint): cint {.importc: "getentropy", header: "<unistd.h>".} - # fills a buffer with high-quality entropy, + # Fills a buffer with high-quality entropy, # which can be used as input for process-context pseudorandom generators like `arc4random`. # The maximum buffer size permitted is 256 bytes. proc getRandomImpl(p: pointer, size: int): int {.inline.} = result = getentropy(p, cint(size)).int +elif defined(zephyr): + proc sys_csrand_get(dst: pointer, length: csize_t): cint {.importc: "sys_csrand_get", header: "<random/rand32.h>".} + # Fill the destination buffer with cryptographically secure + # random data values + # + + proc getRandomImpl(p: pointer, size: int): int {.inline.} = + # 0 if success, -EIO if entropy reseed error + result = sys_csrand_get(p, csize_t(size)).int + elif defined(freebsd): type cssize_t {.importc: "ssize_t", header: "<sys/types.h>".} = int @@ -214,8 +232,8 @@ elif defined(freebsd): proc getRandomImpl(p: pointer, size: int): int {.inline.} = result = getrandom(p, csize_t(size), 0) -elif defined(ios): - {.passL: "-framework Security".} +elif defined(ios) or defined(macosx): + {.passl: "-framework Security".} const errSecSuccess = 0 ## No error. @@ -227,7 +245,7 @@ elif defined(ios): proc secRandomCopyBytes( rnd: SecRandomRef, count: csize_t, bytes: pointer - ): cint {.importc: "SecRandomCopyBytes", header: "<Security/SecRandom.h>".} + ): cint {.importc: "SecRandomCopyBytes", header: "<Security/SecRandom.h>".} ## https://developer.apple.com/documentation/security/1399291-secrandomcopybytes template urandomImpl(result: var int, dest: var openArray[byte]) = @@ -237,19 +255,6 @@ elif defined(ios): result = secRandomCopyBytes(nil, csize_t(size), addr dest[0]) -elif defined(macosx): - const sysrandomHeader = """#include <Availability.h> -#include <sys/random.h> -""" - - proc getentropy(p: pointer, size: csize_t): cint {.importc: "getentropy", header: sysrandomHeader.} - # getentropy() fills a buffer with random data, which can be used as input - # for process-context pseudorandom generators like arc4random(3). - # The maximum buffer size permitted is 256 bytes. - - proc getRandomImpl(p: pointer, size: int): int {.inline.} = - result = getentropy(p, csize_t(size)).int - else: template urandomImpl(result: var int, dest: var openArray[byte]) = let size = dest.len @@ -292,7 +297,7 @@ proc urandom*(dest: var openArray[byte]): bool = ## If the call succeeds, returns `true`. ## ## If `dest` is empty, `urandom` immediately returns success, - ## without calling underlying operating system api. + ## without calling the underlying operating system API. ## ## .. warning:: The code hasn't been audited by cryptography experts and ## is provided as-is without guarantees. Use at your own risks. For production @@ -310,7 +315,7 @@ proc urandom*(dest: var openArray[byte]): bool = proc urandom*(size: Natural): seq[byte] {.inline.} = ## Returns random bytes suitable for cryptographic use. - ## + ## ## .. warning:: The code hasn't been audited by cryptography experts and ## is provided as-is without guarantees. Use at your own risks. For production ## systems we advise you to request an external audit. |