From ca1887a8b7efbbd64776641c2d0ac7976ea3496a Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 4 Nov 2020 16:55:12 +0800 Subject: [PATCH 01/64] close #10307(add testcase for #10307) --- tests/magics/t10307.nim | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/magics/t10307.nim diff --git a/tests/magics/t10307.nim b/tests/magics/t10307.nim new file mode 100644 index 000000000000..111b0d221d03 --- /dev/null +++ b/tests/magics/t10307.nim @@ -0,0 +1,23 @@ +discard """ + cmd: "nim c -d:useGcAssert $file" + output: '''running someProc(true) +res: yes +yes +running someProc(false) +res: +''' +""" + +proc someProc(x:bool):cstring = + var res:string = "" + if x: + res = "yes" + echo "res: ", res + GC_ref(res) + result = res + +echo "running someProc(true)" +echo someProc(true) + +echo "running someProc(false)" +echo someProc(false) \ No newline at end of file From 469d159cce1b870af8a2c7d6ed23093910593a3c Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 24 Dec 2020 22:30:16 +0800 Subject: [PATCH 02/64] add secrets to stdlib --- lib/std/secrets.nim | 98 ++++++++++++++++++++++++++++++++++++++++ tests/stdlib/secrets.nim | 11 +++++ 2 files changed, 109 insertions(+) create mode 100644 lib/std/secrets.nim create mode 100644 tests/stdlib/secrets.nim diff --git a/lib/std/secrets.nim b/lib/std/secrets.nim new file mode 100644 index 000000000000..51cad56fc30e --- /dev/null +++ b/lib/std/secrets.nim @@ -0,0 +1,98 @@ +import os + +runnableExamples: + doAssert urandom(0).len == 0 + doAssert urandom(10).len == 10 + doAssert urandom(20).len == 20 + doAssert urandom(120).len == 120 + + +when defined(posix): + import posix + + template processReadBytes(readBytes: int, p: pointer) = + if readBytes == 0: + break + elif readBytes > 0: + inc(result, readBytes) + cast[ptr pointer](p)[] = cast[pointer](cast[ByteAddress](p) + readBytes) + else: + if osLastError().int in {EINTR, EAGAIN}: + discard + else: + result = -1 + break + + proc getDevUrandom(p: pointer, size: int): int = + let fd = posix.open("/dev/urandom", O_RDONLY) + + if fd > 0: + var stat: Stat + if fstat(fd, stat) != -1 and S_ISCHR(stat.st_mode): + while result < size: + let readBytes = posix.read(fd, p, size - result) + processReadBytes(readBytes, p) + + discard posix.close(fd) + +when defined(windows): + import winlean + + type + PVOID = pointer + BCRYPT_ALG_HANDLE = PVOID + PUCHAR = ptr cuchar + NTSTATUS = clong + + const + STATUS_SUCCESS = 0x00000000 + BCRYPT_USE_SYSTEM_PREFERRED_RNG = 0x00000002 + + proc bCryptGenRandom( + hAlgorithm: BCRYPT_ALG_HANDLE, + pbBuffer: PUCHAR, + cbBuffer: ULONG, + dwFlags: ULONG + ): NTSTATUS {.stdcall, importc: "BCryptGenRandom", dynlib: "Bcrypt.dll".} + + + proc randomBytes(pbBuffer: pointer, cbBuffer: ULONG): int = + bCryptGenRandom(nil, cast[PUCHAR](pbBuffer), cbBuffer, + BCRYPT_USE_SYSTEM_PREFERRED_RNG) + + proc urandom*(p: pointer, size: int): int = + result = randomBytes(p, ULONG(size)) + + proc urandom*(size: int): string = + result = newString(size) + if urandom(result.cstring, ULONG(size)) != STATUS_SUCCESS: + raiseOsError(osLastError()) + +elif defined(linux): + let SYS_getrandom {.importc: "SYS_getrandom", header: "".}: clong + + proc syscall(n: clong, buf: pointer, bufLen: cint, flags: cuint): int {.importc: "syscall", header: "syscall.h".} + + proc randomBytes(p: pointer, size: int): int = + while result < size: + let readBytes = syscall(SYS_getrandom, p, cint(size - result), 0) + processReadBytes(readBytes, p) + + proc urandom*(p: pointer, size: int): int = + result = randomBytes(p, size) + if result < 0: + result = getDevUrandom(p, size) + + proc urandom*(size: int): string = + result = newString(size) + if urandom(result.cstring, size) < 0: + raiseOsError(osLastError()) + +else: + proc urandom*(p: pointer, size: int): int = + result = getDevUrandom(p, size) + + proc urandom*(size: int): string = + result = newString(size) + if urandom(result.cstring, size) < 0: + raiseOsError(osLastError()) diff --git a/tests/stdlib/secrets.nim b/tests/stdlib/secrets.nim new file mode 100644 index 000000000000..6e16caec8701 --- /dev/null +++ b/tests/stdlib/secrets.nim @@ -0,0 +1,11 @@ +discard """ + targets: "c cpp" +""" + +import std/secrets + + +doAssert urandom(0).len == 0 +doAssert urandom(10).len == 10 +doAssert urandom(20).len == 20 +doAssert urandom(120).len == 120 From 58567299a903c69b07f595f4fb89d67f82e5fec5 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 24 Dec 2020 22:30:42 +0800 Subject: [PATCH 03/64] rename --- tests/stdlib/{secrets.nim => tsecrets.nim} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/stdlib/{secrets.nim => tsecrets.nim} (100%) diff --git a/tests/stdlib/secrets.nim b/tests/stdlib/tsecrets.nim similarity index 100% rename from tests/stdlib/secrets.nim rename to tests/stdlib/tsecrets.nim From 335427b30ceacf2d9602e4d9250ad8213c16beb2 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 24 Dec 2020 22:40:12 +0800 Subject: [PATCH 04/64] fix --- tests/magics/t10307.nim | 9 --------- 1 file changed, 9 deletions(-) diff --git a/tests/magics/t10307.nim b/tests/magics/t10307.nim index 51ef9e4361c5..ec3c352de3c1 100644 --- a/tests/magics/t10307.nim +++ b/tests/magics/t10307.nim @@ -4,12 +4,7 @@ discard """ res: yes yes running someProc(false) -<<<<<<< HEAD res: -======= -res: - ->>>>>>> upstream/devel ''' """ @@ -25,8 +20,4 @@ echo "running someProc(true)" echo someProc(true) echo "running someProc(false)" -<<<<<<< HEAD -echo someProc(false) -======= echo someProc(false) ->>>>>>> upstream/devel From ff1153d3c6662567921cb31bebb52e8770cc2d38 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 24 Dec 2020 22:40:55 +0800 Subject: [PATCH 05/64] fix --- tests/magics/t10307.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/magics/t10307.nim b/tests/magics/t10307.nim index ec3c352de3c1..9e78bf25df62 100644 --- a/tests/magics/t10307.nim +++ b/tests/magics/t10307.nim @@ -5,6 +5,7 @@ res: yes yes running someProc(false) res: + ''' """ From cfa81bfbf1d61670869fc72e710d5c27f6720931 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 24 Dec 2020 08:40:36 -0600 Subject: [PATCH 06/64] Update tests/magics/t10307.nim --- tests/magics/t10307.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/magics/t10307.nim b/tests/magics/t10307.nim index 9e78bf25df62..b5bbfdfa80b5 100644 --- a/tests/magics/t10307.nim +++ b/tests/magics/t10307.nim @@ -4,7 +4,7 @@ discard """ res: yes yes running someProc(false) -res: +res: ''' """ From 39b6aaba0707b96a0e13c3ff7e4e8e298b99be58 Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 23 Jan 2021 16:57:19 +0800 Subject: [PATCH 07/64] update secrets --- lib/std/secrets.nim | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/lib/std/secrets.nim b/lib/std/secrets.nim index 51cad56fc30e..a3cf6afeecba 100644 --- a/lib/std/secrets.nim +++ b/lib/std/secrets.nim @@ -1,4 +1,4 @@ -import os +import std/os runnableExamples: doAssert urandom(0).len == 0 @@ -8,7 +8,7 @@ runnableExamples: when defined(posix): - import posix + import std/posix template processReadBytes(readBytes: int, p: pointer) = if readBytes == 0: @@ -36,7 +36,7 @@ when defined(posix): discard posix.close(fd) when defined(windows): - import winlean + import std/winlean type PVOID = pointer @@ -56,16 +56,18 @@ when defined(windows): ): NTSTATUS {.stdcall, importc: "BCryptGenRandom", dynlib: "Bcrypt.dll".} - proc randomBytes(pbBuffer: pointer, cbBuffer: ULONG): int = - bCryptGenRandom(nil, cast[PUCHAR](pbBuffer), cbBuffer, + proc randomBytes(pbBuffer: pointer, cbBuffer: int): int = + bCryptGenRandom(nil, cast[PUCHAR](pbBuffer), ULONG(cbBuffer), BCRYPT_USE_SYSTEM_PREFERRED_RNG) - proc urandom*(p: pointer, size: int): int = - result = randomBytes(p, ULONG(size)) + proc urandom*[T: byte | char](p: var openArray[T]): int = + let size = p.len + if size > 0: + result = randomBytes(addr p[0], size) - proc urandom*(size: int): string = + proc urandom*(size: Natural): string = result = newString(size) - if urandom(result.cstring, ULONG(size)) != STATUS_SUCCESS: + if urandom(result) != STATUS_SUCCESS: raiseOsError(osLastError()) elif defined(linux): @@ -78,21 +80,25 @@ elif defined(linux): let readBytes = syscall(SYS_getrandom, p, cint(size - result), 0) processReadBytes(readBytes, p) - proc urandom*(p: pointer, size: int): int = - result = randomBytes(p, size) - if result < 0: - result = getDevUrandom(p, size) + proc urandom*[T: byte | char](p: var openArray[T]): int = + let size = p.len + if size > 0: + result = randomBytes(p, size) + if result < 0: + result = getDevUrandom(p, size) - proc urandom*(size: int): string = + proc urandom*(size: Natural): string = result = newString(size) - if urandom(result.cstring, size) < 0: + if urandom(result) < 0: raiseOsError(osLastError()) else: - proc urandom*(p: pointer, size: int): int = - result = getDevUrandom(p, size) + proc urandom*[T: byte | char](p: var openArray[T]): int = + let size = p.len + if size > 0: + result = getDevUrandom(addr p[0], size) - proc urandom*(size: int): string = + proc urandom*(size: Natural): string = result = newString(size) - if urandom(result.cstring, size) < 0: + if urandom(result) < 0: raiseOsError(osLastError()) From 6cfe937ea505bc8363ab9495bf03fcf02f99926b Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 24 Jan 2021 10:10:49 +0800 Subject: [PATCH 08/64] fix --- lib/std/secrets.nim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/std/secrets.nim b/lib/std/secrets.nim index a3cf6afeecba..e877aa186e11 100644 --- a/lib/std/secrets.nim +++ b/lib/std/secrets.nim @@ -1,3 +1,5 @@ +## Cryptographically secure pseudorandom number generator. + import std/os runnableExamples: @@ -85,7 +87,7 @@ elif defined(linux): if size > 0: result = randomBytes(p, size) if result < 0: - result = getDevUrandom(p, size) + result = getDevUrandom(addr p[0], size) proc urandom*(size: Natural): string = result = newString(size) From 0f9fdc6d94581a7524c81609cfbcf2b96ba99e13 Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 24 Jan 2021 10:23:33 +0800 Subject: [PATCH 09/64] fix --- lib/std/secrets.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/secrets.nim b/lib/std/secrets.nim index e877aa186e11..d76fd3993472 100644 --- a/lib/std/secrets.nim +++ b/lib/std/secrets.nim @@ -85,7 +85,7 @@ elif defined(linux): proc urandom*[T: byte | char](p: var openArray[T]): int = let size = p.len if size > 0: - result = randomBytes(p, size) + result = randomBytes(addr p[0], size) if result < 0: result = getDevUrandom(addr p[0], size) From 170c61d8b388be36a2222c1b796d248ed9687343 Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 24 Jan 2021 10:39:16 +0800 Subject: [PATCH 10/64] add openbsd --- lib/std/secrets.nim | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/std/secrets.nim b/lib/std/secrets.nim index d76fd3993472..7b33954558c7 100644 --- a/lib/std/secrets.nim +++ b/lib/std/secrets.nim @@ -94,6 +94,17 @@ elif defined(linux): if urandom(result) < 0: raiseOsError(osLastError()) +elif defined(openbsd): + proc arc4random_buf(p: pointer, size: cint) {.importc: "arc4random_buf", header: "".} + + proc urandom*[T: byte | char](p: var openArray[T]): int = + result = p.len + arc4random_buf(addr p[0], result) + + proc urandom*(size: Natural): string = + result = newString(size) + discard urandom(result) + else: proc urandom*[T: byte | char](p: var openArray[T]): int = let size = p.len From e60ac45225e0e6412770d22abd3bafa54192d2e3 Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 24 Jan 2021 10:52:07 +0800 Subject: [PATCH 11/64] openbsd --- lib/std/secrets.nim | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/std/secrets.nim b/lib/std/secrets.nim index 7b33954558c7..f2e12d80832f 100644 --- a/lib/std/secrets.nim +++ b/lib/std/secrets.nim @@ -95,15 +95,24 @@ elif defined(linux): raiseOsError(osLastError()) elif defined(openbsd): - proc arc4random_buf(p: pointer, size: cint) {.importc: "arc4random_buf", header: "".} + proc getentropy(p: pointer, size: cint): cint {.importc: "getentropy", header: "".} + + proc randomBytes(p: pointer, size: int): int = + while result < size: + let readBytes = getentropy(p, cint(size - result)) + processReadBytes(readBytes, p) proc urandom*[T: byte | char](p: var openArray[T]): int = - result = p.len - arc4random_buf(addr p[0], result) + let size = p.len + if size > 0: + result = randomBytes(addr p[0], size) + if result < 0: + result = getDevUrandom(addr p[0], size) proc urandom*(size: Natural): string = result = newString(size) - discard urandom(result) + if urandom(result) < 0: + raiseOsError(osLastError()) else: proc urandom*[T: byte | char](p: var openArray[T]): int = From 2766827014f7c023f9dedad02b064b1d99aa837b Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 24 Jan 2021 12:08:00 +0800 Subject: [PATCH 12/64] add macosx --- lib/std/secrets.nim | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lib/std/secrets.nim b/lib/std/secrets.nim index f2e12d80832f..908e4092e2af 100644 --- a/lib/std/secrets.nim +++ b/lib/std/secrets.nim @@ -114,6 +114,30 @@ elif defined(openbsd): if urandom(result) < 0: raiseOsError(osLastError()) +elif defined(macosx): + const errSecSuccess = 0 + type + SecRandom {.importc: "struct __SecRandom".} = object + + SecRandomRef = ptr SecRandom + ## An abstract Core Foundation-type object containing information about a random number generator. + + proc secRandomCopyBytes( + rnd: SecRandomRef, count: csize_t, bytes: pointer + ): cint {.importc: "SecRandomCopyBytes", header: "".} + + proc urandom*[T: byte | char](p: var openArray[T]): int = + let size = p.len + if size > 0: + result = secRandomCopyBytes(nil, size, addr p[0]) + if result < 0: + result = getDevUrandom(addr p[0], size) + + proc urandom*(size: Natural): string = + result = newString(size) + if urandom(result) != errSecSuccess: + raiseOsError(osLastError()) + else: proc urandom*[T: byte | char](p: var openArray[T]): int = let size = p.len From d52702c1a88603baee81691d0ec3b175a9a656c5 Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 24 Jan 2021 12:12:04 +0800 Subject: [PATCH 13/64] better --- lib/std/secrets.nim | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/lib/std/secrets.nim b/lib/std/secrets.nim index 908e4092e2af..d944b7e6a75f 100644 --- a/lib/std/secrets.nim +++ b/lib/std/secrets.nim @@ -67,11 +67,6 @@ when defined(windows): if size > 0: result = randomBytes(addr p[0], size) - proc urandom*(size: Natural): string = - result = newString(size) - if urandom(result) != STATUS_SUCCESS: - raiseOsError(osLastError()) - elif defined(linux): let SYS_getrandom {.importc: "SYS_getrandom", header: "".}: clong @@ -89,11 +84,6 @@ elif defined(linux): if result < 0: result = getDevUrandom(addr p[0], size) - proc urandom*(size: Natural): string = - result = newString(size) - if urandom(result) < 0: - raiseOsError(osLastError()) - elif defined(openbsd): proc getentropy(p: pointer, size: cint): cint {.importc: "getentropy", header: "".} @@ -109,11 +99,6 @@ elif defined(openbsd): if result < 0: result = getDevUrandom(addr p[0], size) - proc urandom*(size: Natural): string = - result = newString(size) - if urandom(result) < 0: - raiseOsError(osLastError()) - elif defined(macosx): const errSecSuccess = 0 type @@ -130,20 +115,22 @@ elif defined(macosx): let size = p.len if size > 0: result = secRandomCopyBytes(nil, size, addr p[0]) - if result < 0: + if result != errSecSuccess: result = getDevUrandom(addr p[0], size) - proc urandom*(size: Natural): string = - result = newString(size) - if urandom(result) != errSecSuccess: - raiseOsError(osLastError()) - else: proc urandom*[T: byte | char](p: var openArray[T]): int = let size = p.len if size > 0: result = getDevUrandom(addr p[0], size) + +when defined(windows): + proc urandom*(size: Natural): string = + result = newString(size) + if urandom(result) != STATUS_SUCCESS: + raiseOsError(osLastError()) +else: proc urandom*(size: Natural): string = result = newString(size) if urandom(result) < 0: From 66758ca033edd433c566fb36862ed7099d1d1012 Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 24 Jan 2021 12:35:49 +0800 Subject: [PATCH 14/64] fix --- lib/std/secrets.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/secrets.nim b/lib/std/secrets.nim index d944b7e6a75f..c8ca2545e0ae 100644 --- a/lib/std/secrets.nim +++ b/lib/std/secrets.nim @@ -114,7 +114,7 @@ elif defined(macosx): proc urandom*[T: byte | char](p: var openArray[T]): int = let size = p.len if size > 0: - result = secRandomCopyBytes(nil, size, addr p[0]) + result = secRandomCopyBytes(nil, csize_t(size), addr p[0]) if result != errSecSuccess: result = getDevUrandom(addr p[0], size) From 410dfc8190b537ab47cd66fd372fcc8a74c777e9 Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 24 Jan 2021 12:43:00 +0800 Subject: [PATCH 15/64] fix --- lib/std/secrets.nim | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/std/secrets.nim b/lib/std/secrets.nim index c8ca2545e0ae..b452875b77b2 100644 --- a/lib/std/secrets.nim +++ b/lib/std/secrets.nim @@ -38,13 +38,12 @@ when defined(posix): discard posix.close(fd) when defined(windows): - import std/winlean - type PVOID = pointer BCRYPT_ALG_HANDLE = PVOID PUCHAR = ptr cuchar NTSTATUS = clong + ULONG = culong const STATUS_SUCCESS = 0x00000000 From cf0eea586e8f4659581ea1307d7853a188676a88 Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 24 Jan 2021 13:28:24 +0800 Subject: [PATCH 16/64] better interface --- lib/std/secrets.nim | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/std/secrets.nim b/lib/std/secrets.nim index b452875b77b2..761a925ba174 100644 --- a/lib/std/secrets.nim +++ b/lib/std/secrets.nim @@ -61,7 +61,7 @@ when defined(windows): bCryptGenRandom(nil, cast[PUCHAR](pbBuffer), ULONG(cbBuffer), BCRYPT_USE_SYSTEM_PREFERRED_RNG) - proc urandom*[T: byte | char](p: var openArray[T]): int = + proc urandom*(p: var openArray[byte]): int = let size = p.len if size > 0: result = randomBytes(addr p[0], size) @@ -76,7 +76,7 @@ elif defined(linux): let readBytes = syscall(SYS_getrandom, p, cint(size - result), 0) processReadBytes(readBytes, p) - proc urandom*[T: byte | char](p: var openArray[T]): int = + proc urandom*(p: var openArray[byte]): int = let size = p.len if size > 0: result = randomBytes(addr p[0], size) @@ -91,7 +91,7 @@ elif defined(openbsd): let readBytes = getentropy(p, cint(size - result)) processReadBytes(readBytes, p) - proc urandom*[T: byte | char](p: var openArray[T]): int = + proc urandom*(p: var openArray[byte]): int = let size = p.len if size > 0: result = randomBytes(addr p[0], size) @@ -110,7 +110,7 @@ elif defined(macosx): rnd: SecRandomRef, count: csize_t, bytes: pointer ): cint {.importc: "SecRandomCopyBytes", header: "".} - proc urandom*[T: byte | char](p: var openArray[T]): int = + proc urandom*(p: var openArray[byte]): int = let size = p.len if size > 0: result = secRandomCopyBytes(nil, csize_t(size), addr p[0]) @@ -118,19 +118,19 @@ elif defined(macosx): result = getDevUrandom(addr p[0], size) else: - proc urandom*[T: byte | char](p: var openArray[T]): int = + proc urandom*(p: var openArray[byte]): int = let size = p.len if size > 0: result = getDevUrandom(addr p[0], size) when defined(windows): - proc urandom*(size: Natural): string = - result = newString(size) + proc urandom*(size: Natural): seq[byte] = + result = newSeq[byte](size) if urandom(result) != STATUS_SUCCESS: raiseOsError(osLastError()) else: - proc urandom*(size: Natural): string = - result = newString(size) + proc urandom*(size: Natural): seq[byte] = + result = newSeq[byte](size) if urandom(result) < 0: raiseOsError(osLastError()) From 430a9cf8a15248ee4cba2767b3686c8da72e32ea Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 24 Jan 2021 13:29:17 +0800 Subject: [PATCH 17/64] fix --- lib/std/secrets.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/std/secrets.nim b/lib/std/secrets.nim index 761a925ba174..7d3594401f37 100644 --- a/lib/std/secrets.nim +++ b/lib/std/secrets.nim @@ -99,6 +99,8 @@ elif defined(openbsd): result = getDevUrandom(addr p[0], size) elif defined(macosx): + {.passL: "-framework Security".} + const errSecSuccess = 0 type SecRandom {.importc: "struct __SecRandom".} = object From 94d90e8fd7c074e19ceca433a8c4dccfec475713 Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 24 Jan 2021 17:21:04 +0800 Subject: [PATCH 18/64] add js support --- lib/std/private/jsutils.nim | 5 +++++ lib/std/secrets.nim | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/lib/std/private/jsutils.nim b/lib/std/private/jsutils.nim index cc463ac7404a..1fbffe4947e1 100644 --- a/lib/std/private/jsutils.nim +++ b/lib/std/private/jsutils.nim @@ -3,10 +3,15 @@ when defined(js): ArrayBuffer* = ref object of JsRoot Float64Array* = ref object of JsRoot Uint32Array* = ref object of JsRoot + Uint8Array* = ref object of JsRoot func newArrayBuffer*(n: int): ArrayBuffer {.importjs: "new ArrayBuffer(#)".} func newFloat64Array*(buffer: ArrayBuffer): Float64Array {.importjs: "new Float64Array(#)".} func newUint32Array*(buffer: ArrayBuffer): Uint32Array {.importjs: "new Uint32Array(#)".} + + func newUint8Array*(n: int): Uint8Array {.importjs: "new Uint8Array(#)".} + func `[]`*(arr: Uint32Array, i: int): uint32 {.importjs: "#[#]".} + func `[]`*(arr: Uint8Array, i: int): uint8 {.importjs: "#[#]".} func `[]=`*(arr: Float64Array, i: int, v: float) {.importjs: "#[#] = #".} diff --git a/lib/std/secrets.nim b/lib/std/secrets.nim index 7d3594401f37..4c89ddfc8916 100644 --- a/lib/std/secrets.nim +++ b/lib/std/secrets.nim @@ -37,7 +37,34 @@ when defined(posix): discard posix.close(fd) -when defined(windows): +when defined(js): + import std/private/jsutils + + proc getRandomValues(arr: Uint8Array) {.importjs: "window.crypto.getRandomValues(#)".} + + const + maxBrowserCryptoBufferSize = 256 + + proc urandom*(p: var openArray[byte]): int = + let size = p.len + if size > 0: + let chunks = (size - 1) div maxBrowserCryptoBufferSize + var base = 0 + for i in 0 ..< chunks: + for j in 0 ..< maxBrowserCryptoBufferSize: + var src = newUint8Array(maxBrowserCryptoBufferSize) + getRandomValues(src) + p[base + j] = src[j] + + inc(base, maxBrowserCryptoBufferSize) + + let left = size - chunks * maxBrowserCryptoBufferSize + var src = newUint8Array(left) + getRandomValues(src) + for i in 0 ..< left: + p[base + i] = src[i] + +elif defined(windows): type PVOID = pointer BCRYPT_ALG_HANDLE = PVOID @@ -126,7 +153,11 @@ else: result = getDevUrandom(addr p[0], size) -when defined(windows): +when defined(js): + proc urandom*(size: Natural): seq[byte] = + result = newSeq[byte](size) + discard urandom(result) +elif defined(windows): proc urandom*(size: Natural): seq[byte] = result = newSeq[byte](size) if urandom(result) != STATUS_SUCCESS: From a93d360a5bd7055208f94f0f44cb22b6ce393c39 Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 24 Jan 2021 18:05:31 +0800 Subject: [PATCH 19/64] add notes --- lib/std/secrets.nim | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/std/secrets.nim b/lib/std/secrets.nim index 4c89ddfc8916..f554ed40b5f6 100644 --- a/lib/std/secrets.nim +++ b/lib/std/secrets.nim @@ -1,4 +1,21 @@ ## Cryptographically secure pseudorandom number generator. +## +## | Targets | Implementation| +## | :--- | ----: | +## | Windows| `BCryptGenRandom`_ | +## | Linux| `getrandom`_ system call when available, otherwise `/dev/urandom`_ will be used| +## | Macosx| `SecRandomCopyBytes`_ system call when available, otherwise `/dev/urandom`_ will be used| +## | OpenBSD| `getentropy`_ system call when available, otherwise `/dev/urandom`_ will be used| +## | JS(Web Browser)| `getRandomValues`_| +## | Other 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 +## .. _/dev/urandom: https://en.wikipedia.org/wiki//dev/random +## .. _SecRandomCopyBytes: https://developer.apple.com/documentation/security/1399291-secrandomcopybytes?language=objc +## .. _getentropy: https://man.openbsd.org/getentropy.2 +## .. _getRandomValues: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues +## import std/os From 153c44221b8d07ff867ebc12cab7c4e5af0b3b64 Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 24 Jan 2021 18:47:20 +0800 Subject: [PATCH 20/64] add freebsd --- lib/std/secrets.nim | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/std/secrets.nim b/lib/std/secrets.nim index f554ed40b5f6..9127a5e5257e 100644 --- a/lib/std/secrets.nim +++ b/lib/std/secrets.nim @@ -4,8 +4,9 @@ ## | :--- | ----: | ## | Windows| `BCryptGenRandom`_ | ## | Linux| `getrandom`_ system call when available, otherwise `/dev/urandom`_ will be used| -## | Macosx| `SecRandomCopyBytes`_ system call when available, otherwise `/dev/urandom`_ will be used| +## | MacOS| `SecRandomCopyBytes`_ system call when available, otherwise `/dev/urandom`_ will be used| ## | OpenBSD| `getentropy`_ system call when available, otherwise `/dev/urandom`_ will be used| +## | FreeBSD| `getrandom freebsd`_ system call when available, otherwise `/dev/urandom`_ will be used| ## | JS(Web Browser)| `getRandomValues`_| ## | Other platforms| `/dev/urandom`_| ## @@ -14,8 +15,9 @@ ## .. _/dev/urandom: https://en.wikipedia.org/wiki//dev/random ## .. _SecRandomCopyBytes: https://developer.apple.com/documentation/security/1399291-secrandomcopybytes?language=objc ## .. _getentropy: https://man.openbsd.org/getentropy.2 +## .. _getrandom freebsd: https://www.freebsd.org/cgi/man.cgi?query=getrandom&manpath=FreeBSD+12.0-stable ## .. _getRandomValues: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues -## +## import std/os @@ -142,6 +144,20 @@ elif defined(openbsd): if result < 0: result = getDevUrandom(addr p[0], size) +elif defined(freebsd): + proc getrandom(p: pointer, size: csize_t, flags: cuint): int {.importc: "getrandom", header: "".} + + proc randomBytes(p: pointer, size: int): int = + while result < size: + let readBytes = getrandom(p, csize_t(size - result), 0) + processReadBytes(readBytes, p) + proc urandom*(p: var openArray[byte]): int = + let size = p.len + if size > 0: + result = randomBytes(addr p[0], size) + if result < 0: + result = getDevUrandom(addr p[0], size) + elif defined(macosx): {.passL: "-framework Security".} From 88e86f0e4810d3b230a83d925648a66392bf379f Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 24 Jan 2021 19:45:06 +0800 Subject: [PATCH 21/64] better --- lib/std/secrets.nim | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/std/secrets.nim b/lib/std/secrets.nim index 9127a5e5257e..bdbddf6f8775 100644 --- a/lib/std/secrets.nim +++ b/lib/std/secrets.nim @@ -28,10 +28,11 @@ runnableExamples: doAssert urandom(120).len == 120 + when defined(posix): import std/posix - template processReadBytes(readBytes: int, p: pointer) = + template processReadBytes(readBytes: int, p: pointer, result: var int) = if readBytes == 0: break elif readBytes > 0: @@ -52,7 +53,7 @@ when defined(posix): if fstat(fd, stat) != -1 and S_ISCHR(stat.st_mode): while result < size: let readBytes = posix.read(fd, p, size - result) - processReadBytes(readBytes, p) + processReadBytes(readBytes, p, result) discard posix.close(fd) @@ -120,7 +121,7 @@ elif defined(linux): proc randomBytes(p: pointer, size: int): int = while result < size: let readBytes = syscall(SYS_getrandom, p, cint(size - result), 0) - processReadBytes(readBytes, p) + processReadBytes(readBytes, p, result) proc urandom*(p: var openArray[byte]): int = let size = p.len @@ -135,7 +136,7 @@ elif defined(openbsd): proc randomBytes(p: pointer, size: int): int = while result < size: let readBytes = getentropy(p, cint(size - result)) - processReadBytes(readBytes, p) + processReadBytes(readBytes, p, result) proc urandom*(p: var openArray[byte]): int = let size = p.len @@ -150,7 +151,7 @@ elif defined(freebsd): proc randomBytes(p: pointer, size: int): int = while result < size: let readBytes = getrandom(p, csize_t(size - result), 0) - processReadBytes(readBytes, p) + processReadBytes(readBytes, p, result) proc urandom*(p: var openArray[byte]): int = let size = p.len if size > 0: From a67e2a852e7fee4187cb8c37b91140b51d9a7a54 Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 24 Jan 2021 21:32:38 +0800 Subject: [PATCH 22/64] better --- lib/std/secrets.nim | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/lib/std/secrets.nim b/lib/std/secrets.nim index bdbddf6f8775..604bdb7c9f7c 100644 --- a/lib/std/secrets.nim +++ b/lib/std/secrets.nim @@ -32,29 +32,40 @@ runnableExamples: when defined(posix): import std/posix - template processReadBytes(readBytes: int, p: pointer, result: var int) = + const batchSize = 256 + + template processReadBytes(readBytes: int, p: pointer, res: var int) = if readBytes == 0: break elif readBytes > 0: - inc(result, readBytes) + inc(res, readBytes) cast[ptr pointer](p)[] = cast[pointer](cast[ByteAddress](p) + readBytes) else: if osLastError().int in {EINTR, EAGAIN}: discard else: - result = -1 + res = -1 break - proc getDevUrandom(p: pointer, size: int): int = + proc getDevUrandom(p: var openArray[byte], size: int): int = let fd = posix.open("/dev/urandom", O_RDONLY) if fd > 0: var stat: Stat if fstat(fd, stat) != -1 and S_ISCHR(stat.st_mode): - while result < size: - let readBytes = posix.read(fd, p, size - result) - processReadBytes(readBytes, p, result) - + let + size = p.len + chunks = (size - 1) div batchSize + left = size - chunk * batchSize + + var base = 0 + for i in 0 ..< chunks: + let readBytes = posix.read(fd, addr p[base], batchSize) + if readBytes < 0: + return readBytes + inc(base, batchSize) + + result = posix.read(fd, addr p[base], left) discard posix.close(fd) when defined(js): @@ -128,7 +139,7 @@ elif defined(linux): if size > 0: result = randomBytes(addr p[0], size) if result < 0: - result = getDevUrandom(addr p[0], size) + result = getDevUrandom(p, size) elif defined(openbsd): proc getentropy(p: pointer, size: cint): cint {.importc: "getentropy", header: "".} @@ -143,7 +154,7 @@ elif defined(openbsd): if size > 0: result = randomBytes(addr p[0], size) if result < 0: - result = getDevUrandom(addr p[0], size) + result = getDevUrandom(p, size) elif defined(freebsd): proc getrandom(p: pointer, size: csize_t, flags: cuint): int {.importc: "getrandom", header: "".} @@ -152,12 +163,13 @@ elif defined(freebsd): while result < size: let readBytes = getrandom(p, csize_t(size - result), 0) processReadBytes(readBytes, p, result) + proc urandom*(p: var openArray[byte]): int = let size = p.len if size > 0: result = randomBytes(addr p[0], size) if result < 0: - result = getDevUrandom(addr p[0], size) + result = getDevUrandom(p, size) elif defined(macosx): {.passL: "-framework Security".} @@ -178,13 +190,13 @@ elif defined(macosx): if size > 0: result = secRandomCopyBytes(nil, csize_t(size), addr p[0]) if result != errSecSuccess: - result = getDevUrandom(addr p[0], size) + result = getDevUrandom(p, size) else: proc urandom*(p: var openArray[byte]): int = let size = p.len if size > 0: - result = getDevUrandom(addr p[0], size) + result = getDevUrandom(p, size) when defined(js): From 1e1d01cf8bcfb0e71ef160b13322eb4c6ff423b5 Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 24 Jan 2021 23:44:12 +0800 Subject: [PATCH 23/64] fix --- lib/std/secrets.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/secrets.nim b/lib/std/secrets.nim index 604bdb7c9f7c..db969ff4b5bc 100644 --- a/lib/std/secrets.nim +++ b/lib/std/secrets.nim @@ -56,7 +56,7 @@ when defined(posix): let size = p.len chunks = (size - 1) div batchSize - left = size - chunk * batchSize + left = size - chunks * batchSize var base = 0 for i in 0 ..< chunks: From af90ab7b839b9f37951e68b1f3681ff6b5414324 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 25 Jan 2021 11:17:48 +0800 Subject: [PATCH 24/64] add tests --- tests/stdlib/tsecrets.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/stdlib/tsecrets.nim b/tests/stdlib/tsecrets.nim index 6e16caec8701..6903b2b9290e 100644 --- a/tests/stdlib/tsecrets.nim +++ b/tests/stdlib/tsecrets.nim @@ -9,3 +9,5 @@ doAssert urandom(0).len == 0 doAssert urandom(10).len == 10 doAssert urandom(20).len == 20 doAssert urandom(120).len == 120 +doAssert urandom(113).len == 113 +doAssert urandom(1234) != urandom(1234) # unlikely to fail in practice From 918ddb121f271914163f50de7d57651515521c68 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 25 Jan 2021 12:10:16 +0800 Subject: [PATCH 25/64] minor clean --- lib/std/secrets.nim | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/lib/std/secrets.nim b/lib/std/secrets.nim index db969ff4b5bc..10b2541d2a82 100644 --- a/lib/std/secrets.nim +++ b/lib/std/secrets.nim @@ -138,8 +138,6 @@ elif defined(linux): let size = p.len if size > 0: result = randomBytes(addr p[0], size) - if result < 0: - result = getDevUrandom(p, size) elif defined(openbsd): proc getentropy(p: pointer, size: cint): cint {.importc: "getentropy", header: "".} @@ -153,8 +151,6 @@ elif defined(openbsd): let size = p.len if size > 0: result = randomBytes(addr p[0], size) - if result < 0: - result = getDevUrandom(p, size) elif defined(freebsd): proc getrandom(p: pointer, size: csize_t, flags: cuint): int {.importc: "getrandom", header: "".} @@ -168,10 +164,8 @@ elif defined(freebsd): let size = p.len if size > 0: result = randomBytes(addr p[0], size) - if result < 0: - result = getDevUrandom(p, size) -elif defined(macosx): +elif defined(ios): {.passL: "-framework Security".} const errSecSuccess = 0 @@ -188,10 +182,11 @@ elif defined(macosx): proc urandom*(p: var openArray[byte]): int = let size = p.len if size > 0: + # `kSecRandomDefault` is a synonym for NULL. result = secRandomCopyBytes(nil, csize_t(size), addr p[0]) - if result != errSecSuccess: - result = getDevUrandom(p, size) +elif defined(macosx): + discard else: proc urandom*(p: var openArray[byte]): int = let size = p.len From 4ee409b576713495d15364dd7353bec7a738ddf7 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 25 Jan 2021 12:20:01 +0800 Subject: [PATCH 26/64] slight better --- lib/std/secrets.nim | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/std/secrets.nim b/lib/std/secrets.nim index 10b2541d2a82..d9ecb39bc72d 100644 --- a/lib/std/secrets.nim +++ b/lib/std/secrets.nim @@ -47,14 +47,17 @@ when defined(posix): res = -1 break - proc getDevUrandom(p: var openArray[byte], size: int): int = + proc getDevUrandom(p: var openArray[byte], size: Natural): int = + let size = p.len + if size == 0: + return + let fd = posix.open("/dev/urandom", O_RDONLY) if fd > 0: var stat: Stat if fstat(fd, stat) != -1 and S_ISCHR(stat.st_mode): let - size = p.len chunks = (size - 1) div batchSize left = size - chunks * batchSize @@ -153,7 +156,8 @@ elif defined(openbsd): result = randomBytes(addr p[0], size) elif defined(freebsd): - proc getrandom(p: pointer, size: csize_t, flags: cuint): int {.importc: "getrandom", header: "".} + type ssize_t = int + proc getrandom(p: pointer, size: csize_t, flags: cuint): ssize_t {.importc: "getrandom", header: "".} proc randomBytes(p: pointer, size: int): int = while result < size: @@ -186,7 +190,17 @@ elif defined(ios): result = secRandomCopyBytes(nil, csize_t(size), addr p[0]) elif defined(macosx): - discard + proc getentropy(p: pointer, size: csize_t): cint {.importc: "getentropy", header: "".} + + proc randomBytes(p: pointer, size: int): int = + while result < size: + let readBytes = getentropy(p, csize_t(size - result)) + processReadBytes(readBytes, p, result) + + proc urandom*(p: var openArray[byte]): int = + let size = p.len + if size > 0: + result = randomBytes(addr p[0], size) else: proc urandom*(p: var openArray[byte]): int = let size = p.len From a856a3313af4bfc661908cc5b6576a6fd48625fa Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 25 Jan 2021 12:22:41 +0800 Subject: [PATCH 27/64] better --- lib/std/secrets.nim | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/std/secrets.nim b/lib/std/secrets.nim index d9ecb39bc72d..ef215d2fe2d6 100644 --- a/lib/std/secrets.nim +++ b/lib/std/secrets.nim @@ -4,8 +4,9 @@ ## | :--- | ----: | ## | Windows| `BCryptGenRandom`_ | ## | Linux| `getrandom`_ system call when available, otherwise `/dev/urandom`_ will be used| -## | MacOS| `SecRandomCopyBytes`_ system call when available, otherwise `/dev/urandom`_ will be used| -## | OpenBSD| `getentropy`_ system call when available, otherwise `/dev/urandom`_ will be used| +## | MacOS| `getentropy`_ system call when available, otherwise `/dev/urandom`_ will be used| +## | IOS | `SecRandomCopyBytes`_| +## | OpenBSD| `getentropy openbsd`_ system call when available, otherwise `/dev/urandom`_ will be used| ## | FreeBSD| `getrandom freebsd`_ system call when available, otherwise `/dev/urandom`_ will be used| ## | JS(Web Browser)| `getRandomValues`_| ## | Other platforms| `/dev/urandom`_| @@ -13,8 +14,9 @@ ## .. _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 ## .. _/dev/urandom: https://en.wikipedia.org/wiki//dev/random +## .. _getentropy: https://www.unix.com/man-page/mojave/2/getentropy ## .. _SecRandomCopyBytes: https://developer.apple.com/documentation/security/1399291-secrandomcopybytes?language=objc -## .. _getentropy: https://man.openbsd.org/getentropy.2 +## .. _getentropy openbsd: https://man.openbsd.org/getentropy.2 ## .. _getrandom freebsd: https://www.freebsd.org/cgi/man.cgi?query=getrandom&manpath=FreeBSD+12.0-stable ## .. _getRandomValues: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues ## From 9da336b1e6dd580d1822e8b2fa1a570b6d39b6cb Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 25 Jan 2021 14:14:55 +0800 Subject: [PATCH 28/64] fix --- lib/std/secrets.nim | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/lib/std/secrets.nim b/lib/std/secrets.nim index ef215d2fe2d6..6c3b0791a3b3 100644 --- a/lib/std/secrets.nim +++ b/lib/std/secrets.nim @@ -4,7 +4,7 @@ ## | :--- | ----: | ## | Windows| `BCryptGenRandom`_ | ## | Linux| `getrandom`_ system call when available, otherwise `/dev/urandom`_ will be used| -## | MacOS| `getentropy`_ system call when available, otherwise `/dev/urandom`_ will be used| +## | MacOSX| `getentropy`_ system call when available, otherwise `/dev/urandom`_ will be used| ## | IOS | `SecRandomCopyBytes`_| ## | OpenBSD| `getentropy openbsd`_ system call when available, otherwise `/dev/urandom`_ will be used| ## | FreeBSD| `getrandom freebsd`_ system call when available, otherwise `/dev/urandom`_ will be used| @@ -25,9 +25,8 @@ import std/os runnableExamples: doAssert urandom(0).len == 0 - doAssert urandom(10).len == 10 - doAssert urandom(20).len == 20 - doAssert urandom(120).len == 120 + doAssert urandom(113).len == 113 + doAssert urandom(1234) != urandom(1234) # unlikely to fail in practice @@ -120,7 +119,7 @@ elif defined(windows): ): NTSTATUS {.stdcall, importc: "BCryptGenRandom", dynlib: "Bcrypt.dll".} - proc randomBytes(pbBuffer: pointer, cbBuffer: int): int = + proc randomBytes(pbBuffer: pointer, cbBuffer: Natural): int = bCryptGenRandom(nil, cast[PUCHAR](pbBuffer), ULONG(cbBuffer), BCRYPT_USE_SYSTEM_PREFERRED_RNG) @@ -130,11 +129,11 @@ elif defined(windows): result = randomBytes(addr p[0], size) elif defined(linux): - let SYS_getrandom {.importc: "SYS_getrandom", header: "".}: clong + let SYS_getrandom {.importc: "SYS_getrandom", header: "".}: clong - proc syscall(n: clong, buf: pointer, bufLen: cint, flags: cuint): int {.importc: "syscall", header: "syscall.h".} + proc syscall(n: clong, buf: pointer, bufLen: cint, flags: cuint): int {.importc: "syscall", header: "".} - proc randomBytes(p: pointer, size: int): int = + proc randomBytes(p: pointer, size: Natural): int = while result < size: let readBytes = syscall(SYS_getrandom, p, cint(size - result), 0) processReadBytes(readBytes, p, result) @@ -147,7 +146,7 @@ elif defined(linux): elif defined(openbsd): proc getentropy(p: pointer, size: cint): cint {.importc: "getentropy", header: "".} - proc randomBytes(p: pointer, size: int): int = + proc randomBytes(p: pointer, size: Natural): int = while result < size: let readBytes = getentropy(p, cint(size - result)) processReadBytes(readBytes, p, result) @@ -194,7 +193,7 @@ elif defined(ios): elif defined(macosx): proc getentropy(p: pointer, size: csize_t): cint {.importc: "getentropy", header: "".} - proc randomBytes(p: pointer, size: int): int = + proc randomBytes(p: pointer, size: Natural): int = while result < size: let readBytes = getentropy(p, csize_t(size - result)) processReadBytes(readBytes, p, result) @@ -209,18 +208,13 @@ else: if size > 0: result = getDevUrandom(p, size) - -when defined(js): - proc urandom*(size: Natural): seq[byte] = - result = newSeq[byte](size) - discard urandom(result) -elif defined(windows): - proc urandom*(size: Natural): seq[byte] = - result = newSeq[byte](size) - if urandom(result) != STATUS_SUCCESS: +proc urandom*(size: Natural): seq[byte] = + result = newSeq[byte](size) + let ret = urandom(result) + when defined(js): discard ret + elif defined(windows): + if ret != STATUS_SUCCESS: raiseOsError(osLastError()) -else: - proc urandom*(size: Natural): seq[byte] = - result = newSeq[byte](size) - if urandom(result) < 0: + else: + if ret < 0: raiseOsError(osLastError()) From 67600a13f1495faf89bf805cc25b7b4c9c0d0303 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 25 Jan 2021 17:31:33 +0800 Subject: [PATCH 29/64] better --- lib/std/secrets.nim | 57 +++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/lib/std/secrets.nim b/lib/std/secrets.nim index 6c3b0791a3b3..b8ca9567adb7 100644 --- a/lib/std/secrets.nim +++ b/lib/std/secrets.nim @@ -146,29 +146,37 @@ elif defined(linux): elif defined(openbsd): proc getentropy(p: pointer, size: cint): cint {.importc: "getentropy", header: "".} - proc randomBytes(p: pointer, size: Natural): int = - while result < size: - let readBytes = getentropy(p, cint(size - result)) - processReadBytes(readBytes, p, result) - proc urandom*(p: var openArray[byte]): int = let size = p.len if size > 0: - result = randomBytes(addr p[0], size) + let + chunks = (size - 1) div batchSize + left = size - chunks * batchSize + var base = 0 + for i in 0 ..< chunks: + let readBytes = getentropy(addr p[base], cint(batchSize)) + if readBytes < 0: + return readBytes + inc(base, batchSize) + + result = getentropy(addr p[base], cint(left)) elif defined(freebsd): type ssize_t = int proc getrandom(p: pointer, size: csize_t, flags: cuint): ssize_t {.importc: "getrandom", header: "".} - proc randomBytes(p: pointer, size: int): int = - while result < size: - let readBytes = getrandom(p, csize_t(size - result), 0) - processReadBytes(readBytes, p, result) - proc urandom*(p: var openArray[byte]): int = - let size = p.len - if size > 0: - result = randomBytes(addr p[0], size) + let + chunks = (size - 1) div batchSize + left = size - chunks * batchSize + var base = 0 + for i in 0 ..< chunks: + let readBytes = getrandom(addr p[base], csize_t(batchSize), 0) + if readBytes < 0: + return readBytes + inc(base, batchSize) + + result = getrandom(addr p[base], csize_t(left), 0) elif defined(ios): {.passL: "-framework Security".} @@ -193,20 +201,23 @@ elif defined(ios): elif defined(macosx): proc getentropy(p: pointer, size: csize_t): cint {.importc: "getentropy", header: "".} - proc randomBytes(p: pointer, size: Natural): int = - while result < size: - let readBytes = getentropy(p, csize_t(size - result)) - processReadBytes(readBytes, p, result) - proc urandom*(p: var openArray[byte]): int = let size = p.len if size > 0: - result = randomBytes(addr p[0], size) + let + chunks = (size - 1) div batchSize + left = size - chunks * batchSize + var base = 0 + for i in 0 ..< chunks: + let readBytes = getentropy(addr p[base], csize_t(batchSize)) + if readBytes < 0: + return readBytes + inc(base, batchSize) + + result = getentropy(addr p[base], csize_t(left)) else: proc urandom*(p: var openArray[byte]): int = - let size = p.len - if size > 0: - result = getDevUrandom(p, size) + result = getDevUrandom(p, size) proc urandom*(size: Natural): seq[byte] = result = newSeq[byte](size) From 66560b004152ff8db2504933794bddd6cbdc3b6b Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 25 Jan 2021 17:44:05 +0800 Subject: [PATCH 30/64] rename --- lib/std/{secrets.nim => sysrand.nim} | 21 ++++++++++++++++++--- tests/stdlib/{tsecrets.nim => tsysrand.nim} | 2 +- 2 files changed, 19 insertions(+), 4 deletions(-) rename lib/std/{secrets.nim => sysrand.nim} (96%) rename tests/stdlib/{tsecrets.nim => tsysrand.nim} (93%) diff --git a/lib/std/secrets.nim b/lib/std/sysrand.nim similarity index 96% rename from lib/std/secrets.nim rename to lib/std/sysrand.nim index b8ca9567adb7..3035d0d98d74 100644 --- a/lib/std/secrets.nim +++ b/lib/std/sysrand.nim @@ -1,3 +1,12 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2021 Nim contributors +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + ## Cryptographically secure pseudorandom number generator. ## ## | Targets | Implementation| @@ -19,15 +28,21 @@ ## .. _getentropy openbsd: https://man.openbsd.org/getentropy.2 ## .. _getrandom freebsd: https://www.freebsd.org/cgi/man.cgi?query=getrandom&manpath=FreeBSD+12.0-stable ## .. _getRandomValues: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues -## - -import std/os +## runnableExamples: doAssert urandom(0).len == 0 doAssert urandom(113).len == 113 doAssert urandom(1234) != urandom(1234) # unlikely to fail in practice +## +## See also +## ======== +## * `random module `_ +## + +import std/os + when defined(posix): diff --git a/tests/stdlib/tsecrets.nim b/tests/stdlib/tsysrand.nim similarity index 93% rename from tests/stdlib/tsecrets.nim rename to tests/stdlib/tsysrand.nim index 6903b2b9290e..b5e6dba2be57 100644 --- a/tests/stdlib/tsecrets.nim +++ b/tests/stdlib/tsysrand.nim @@ -2,7 +2,7 @@ discard """ targets: "c cpp" """ -import std/secrets +import std/sysrand doAssert urandom(0).len == 0 From 3a9affe4a69ecf2b291e977bb3967b6812763287 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 25 Jan 2021 17:45:47 +0800 Subject: [PATCH 31/64] better type --- lib/std/sysrand.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index 3035d0d98d74..2553318849be 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -177,8 +177,9 @@ elif defined(openbsd): result = getentropy(addr p[base], cint(left)) elif defined(freebsd): - type ssize_t = int - proc getrandom(p: pointer, size: csize_t, flags: cuint): ssize_t {.importc: "getrandom", header: "".} + type cssize_t {.importc, header: "".} = int + + proc getrandom(p: pointer, size: csize_t, flags: cuint): cssize_t {.importc: "getrandom", header: "".} proc urandom*(p: var openArray[byte]): int = let From 4c638a2a4db31b2cc673cdf8d2ea2491969e3e8b Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 25 Jan 2021 17:50:42 +0800 Subject: [PATCH 32/64] add comments --- lib/std/sysrand.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index 2553318849be..360f7524e9ea 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -18,7 +18,7 @@ ## | OpenBSD| `getentropy openbsd`_ system call when available, otherwise `/dev/urandom`_ will be used| ## | FreeBSD| `getrandom freebsd`_ system call when available, otherwise `/dev/urandom`_ will be used| ## | JS(Web Browser)| `getRandomValues`_| -## | Other platforms| `/dev/urandom`_| +## | 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 @@ -236,6 +236,7 @@ else: result = getDevUrandom(p, size) proc urandom*(size: Natural): seq[byte] = + ## Returns random bytes suitable for cryptographic use. result = newSeq[byte](size) let ret = urandom(result) when defined(js): discard ret From b3517795fe5d9a59f8fba3403b158b698c29bcaf Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 25 Jan 2021 19:20:10 +0800 Subject: [PATCH 33/64] fix header --- lib/std/sysrand.nim | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index 360f7524e9ea..5556b08d11ab 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -145,8 +145,12 @@ elif defined(windows): elif defined(linux): let SYS_getrandom {.importc: "SYS_getrandom", header: "".}: clong + const syscallHeader = """#include +#include """ - proc syscall(n: clong, buf: pointer, bufLen: cint, flags: cuint): int {.importc: "syscall", header: "".} + proc syscall( + n: clong, buf: pointer, bufLen: cint, flags: cuint + ): int {.importc: "syscall", header: syscallHeader.} proc randomBytes(p: pointer, size: Natural): int = while result < size: From 08971f8e1063e7114b094445bd25bd1cd4a137b3 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 25 Jan 2021 21:15:24 +0800 Subject: [PATCH 34/64] fix --- lib/std/sysrand.nim | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index 5556b08d11ab..207827ca0d0c 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -186,17 +186,19 @@ elif defined(freebsd): proc getrandom(p: pointer, size: csize_t, flags: cuint): cssize_t {.importc: "getrandom", header: "".} proc urandom*(p: var openArray[byte]): int = - let - chunks = (size - 1) div batchSize - left = size - chunks * batchSize - var base = 0 - for i in 0 ..< chunks: - let readBytes = getrandom(addr p[base], csize_t(batchSize), 0) - if readBytes < 0: - return readBytes - inc(base, batchSize) - - result = getrandom(addr p[base], csize_t(left), 0) + let size = p.len + if size > 0: + let + chunks = (size - 1) div batchSize + left = size - chunks * batchSize + var base = 0 + for i in 0 ..< chunks: + let readBytes = getrandom(addr p[base], csize_t(batchSize), 0) + if readBytes < 0: + return readBytes + inc(base, batchSize) + + result = getrandom(addr p[base], csize_t(left), 0) elif defined(ios): {.passL: "-framework Security".} From 05c5d16b80dbc8bb986e5b08d90a5e1847644805 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 25 Jan 2021 21:53:03 +0800 Subject: [PATCH 35/64] fix name --- lib/std/sysrand.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index 207827ca0d0c..880acd3d9a04 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -181,7 +181,7 @@ elif defined(openbsd): result = getentropy(addr p[base], cint(left)) elif defined(freebsd): - type cssize_t {.importc, header: "".} = int + type cssize_t {.importc: "ssize_t", header: "".} = int proc getrandom(p: pointer, size: csize_t, flags: cuint): cssize_t {.importc: "getrandom", header: "".} From 53a9aca8c0b362fd078083e9ec1e77570e5e85be Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 25 Jan 2021 22:11:29 +0800 Subject: [PATCH 36/64] fix header --- lib/std/sysrand.nim | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index 880acd3d9a04..66926bcfc082 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -221,7 +221,11 @@ elif defined(ios): result = secRandomCopyBytes(nil, csize_t(size), addr p[0]) elif defined(macosx): - proc getentropy(p: pointer, size: csize_t): cint {.importc: "getentropy", header: "".} + const sysrandomHeader = """#include +#include +""" + + proc getentropy(p: pointer, size: csize_t): cint {.importc: "getentropy", header: sysrandomHeader.} proc urandom*(p: var openArray[byte]): int = let size = p.len From 2dd08fd58452ad6d4efab760526f882d8649137c Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 26 Jan 2021 14:32:33 +0800 Subject: [PATCH 37/64] a bit better --- lib/std/sysrand.nim | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index 66926bcfc082..f3fcc9c966e6 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -50,19 +50,6 @@ when defined(posix): const batchSize = 256 - template processReadBytes(readBytes: int, p: pointer, res: var int) = - if readBytes == 0: - break - elif readBytes > 0: - inc(res, readBytes) - cast[ptr pointer](p)[] = cast[pointer](cast[ByteAddress](p) + readBytes) - else: - if osLastError().int in {EINTR, EAGAIN}: - discard - else: - res = -1 - break - proc getDevUrandom(p: var openArray[byte], size: Natural): int = let size = p.len if size == 0: @@ -155,7 +142,17 @@ elif defined(linux): proc randomBytes(p: pointer, size: Natural): int = while result < size: let readBytes = syscall(SYS_getrandom, p, cint(size - result), 0) - processReadBytes(readBytes, p, result) + if readBytes == 0: + break + elif readBytes > 0: + inc(res, readBytes) + cast[ptr pointer](p)[] = cast[pointer](cast[ByteAddress](p) + readBytes) + else: + if osLastError().int in {EINTR, EAGAIN}: + discard + else: + res = -1 + break proc urandom*(p: var openArray[byte]): int = let size = p.len From 84889b9b1233bc2b4894e4af4d9d1a36cc39014c Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 26 Jan 2021 14:33:32 +0800 Subject: [PATCH 38/64] fix --- lib/std/sysrand.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index f3fcc9c966e6..9caebf4fca3c 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -145,13 +145,13 @@ elif defined(linux): if readBytes == 0: break elif readBytes > 0: - inc(res, readBytes) + inc(result, readBytes) cast[ptr pointer](p)[] = cast[pointer](cast[ByteAddress](p) + readBytes) else: if osLastError().int in {EINTR, EAGAIN}: discard else: - res = -1 + result = -1 break proc urandom*(p: var openArray[byte]): int = From f2b02dbd8539c7654f1eb454e8cbe4ea5c5a4a1c Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 26 Jan 2021 15:14:46 +0800 Subject: [PATCH 39/64] freebsd fallback to dev/urandom --- lib/std/sysrand.nim | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index 9caebf4fca3c..c1f2b1e9716e 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -44,13 +44,12 @@ runnableExamples: import std/os - when defined(posix): import std/posix const batchSize = 256 - proc getDevUrandom(p: var openArray[byte], size: Natural): int = + template getDevUrandom(p: var openArray[byte]): int = let size = p.len if size == 0: return @@ -178,24 +177,29 @@ elif defined(openbsd): result = getentropy(addr p[base], cint(left)) elif defined(freebsd): + let freebsdVersion {.importc: "__FreeBSD_version", header: "".}: int + type cssize_t {.importc: "ssize_t", header: "".} = int proc getrandom(p: pointer, size: csize_t, flags: cuint): cssize_t {.importc: "getrandom", header: "".} proc urandom*(p: var openArray[byte]): int = - let size = p.len - if size > 0: - let - chunks = (size - 1) div batchSize - left = size - chunks * batchSize - var base = 0 - for i in 0 ..< chunks: - let readBytes = getrandom(addr p[base], csize_t(batchSize), 0) - if readBytes < 0: - return readBytes - inc(base, batchSize) + if freebsdVersion >= 1200000: + let size = p.len + if size > 0: + let + chunks = (size - 1) div batchSize + left = size - chunks * batchSize + var base = 0 + for i in 0 ..< chunks: + let readBytes = getrandom(addr p[base], csize_t(batchSize), 0) + if readBytes < 0: + return readBytes + inc(base, batchSize) - result = getrandom(addr p[base], csize_t(left), 0) + result = getrandom(addr p[base], csize_t(left), 0) + else: + result = getDevUrandom(p) elif defined(ios): {.passL: "-framework Security".} @@ -240,7 +244,7 @@ elif defined(macosx): result = getentropy(addr p[base], csize_t(left)) else: proc urandom*(p: var openArray[byte]): int = - result = getDevUrandom(p, size) + result = getDevUrandom(p) proc urandom*(size: Natural): seq[byte] = ## Returns random bytes suitable for cryptographic use. From 9972b31ea3f049342aff33ce156937b5f1c99c8f Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 26 Jan 2021 15:33:54 +0800 Subject: [PATCH 40/64] restore --- lib/std/sysrand.nim | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index c1f2b1e9716e..f1b51602d499 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -177,29 +177,24 @@ elif defined(openbsd): result = getentropy(addr p[base], cint(left)) elif defined(freebsd): - let freebsdVersion {.importc: "__FreeBSD_version", header: "".}: int - type cssize_t {.importc: "ssize_t", header: "".} = int proc getrandom(p: pointer, size: csize_t, flags: cuint): cssize_t {.importc: "getrandom", header: "".} proc urandom*(p: var openArray[byte]): int = - if freebsdVersion >= 1200000: - let size = p.len - if size > 0: - let - chunks = (size - 1) div batchSize - left = size - chunks * batchSize - var base = 0 - for i in 0 ..< chunks: - let readBytes = getrandom(addr p[base], csize_t(batchSize), 0) - if readBytes < 0: - return readBytes - inc(base, batchSize) + let size = p.len + if size > 0: + let + chunks = (size - 1) div batchSize + left = size - chunks * batchSize + var base = 0 + for i in 0 ..< chunks: + let readBytes = getrandom(addr p[base], csize_t(batchSize), 0) + if readBytes < 0: + return readBytes + inc(base, batchSize) - result = getrandom(addr p[base], csize_t(left), 0) - else: - result = getDevUrandom(p) + result = getrandom(addr p[base], csize_t(left), 0) elif defined(ios): {.passL: "-framework Security".} From 5f1aa7418fb2e4ceb81d7035ebb4f4e45abcbfaa Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 26 Jan 2021 17:18:21 +0800 Subject: [PATCH 41/64] better --- lib/std/sysrand.nim | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index f1b51602d499..caafaacf75c4 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -138,14 +138,17 @@ elif defined(linux): n: clong, buf: pointer, bufLen: cint, flags: cuint ): int {.importc: "syscall", header: syscallHeader.} - proc randomBytes(p: pointer, size: Natural): int = + proc urandom*(p: var openArray[byte]): int = + let size = p.len + if size == 0: + return + while result < size: - let readBytes = syscall(SYS_getrandom, p, cint(size - result), 0) + let readBytes = syscall(SYS_getrandom, addr p[result], cint(size - result), 0) if readBytes == 0: break elif readBytes > 0: inc(result, readBytes) - cast[ptr pointer](p)[] = cast[pointer](cast[ByteAddress](p) + readBytes) else: if osLastError().int in {EINTR, EAGAIN}: discard @@ -153,11 +156,6 @@ elif defined(linux): result = -1 break - proc urandom*(p: var openArray[byte]): int = - let size = p.len - if size > 0: - result = randomBytes(addr p[0], size) - elif defined(openbsd): proc getentropy(p: pointer, size: cint): cint {.importc: "getentropy", header: "".} From 5e258e7aa1ed17dfd056d2c21cf0a50a3edac3fa Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 26 Jan 2021 17:23:46 +0800 Subject: [PATCH 42/64] better --- lib/std/sysrand.nim | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index caafaacf75c4..4ec65eee2ea7 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -76,25 +76,24 @@ when defined(posix): when defined(js): import std/private/jsutils - proc getRandomValues(arr: Uint8Array) {.importjs: "window.crypto.getRandomValues(#)".} + const batchSize = 256 - const - maxBrowserCryptoBufferSize = 256 + proc getRandomValues(arr: Uint8Array) {.importjs: "window.crypto.getRandomValues(#)".} proc urandom*(p: var openArray[byte]): int = let size = p.len if size > 0: - let chunks = (size - 1) div maxBrowserCryptoBufferSize + let chunks = (size - 1) div batchSize var base = 0 for i in 0 ..< chunks: - for j in 0 ..< maxBrowserCryptoBufferSize: - var src = newUint8Array(maxBrowserCryptoBufferSize) + for j in 0 ..< batchSize: + var src = newUint8Array(batchSize) getRandomValues(src) p[base + j] = src[j] - inc(base, maxBrowserCryptoBufferSize) + inc(base, batchSize) - let left = size - chunks * maxBrowserCryptoBufferSize + let left = size - chunks * batchSize var src = newUint8Array(left) getRandomValues(src) for i in 0 ..< left: @@ -239,7 +238,7 @@ else: proc urandom*(p: var openArray[byte]): int = result = getDevUrandom(p) -proc urandom*(size: Natural): seq[byte] = +proc urandom*(size: Natural): seq[byte] {.inline.} = ## Returns random bytes suitable for cryptographic use. result = newSeq[byte](size) let ret = urandom(result) From 6a34803df6d86ae5797371aac6cab2a74faa31c8 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 26 Jan 2021 20:16:00 +0800 Subject: [PATCH 43/64] better --- lib/std/sysrand.nim | 67 ++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index 4ec65eee2ea7..f7e04784b703 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -12,11 +12,11 @@ ## | Targets | Implementation| ## | :--- | ----: | ## | Windows| `BCryptGenRandom`_ | -## | Linux| `getrandom`_ system call when available, otherwise `/dev/urandom`_ will be used| -## | MacOSX| `getentropy`_ system call when available, otherwise `/dev/urandom`_ will be used| -## | IOS | `SecRandomCopyBytes`_| -## | OpenBSD| `getentropy openbsd`_ system call when available, otherwise `/dev/urandom`_ will be used| -## | FreeBSD| `getrandom freebsd`_ system call when available, otherwise `/dev/urandom`_ will be used| +## | Linux| `getrandom`_ | +## | MacOSX| `getentropy`_ | +## | IOS | `SecRandomCopyBytes`_ | +## | OpenBSD| `getentropy openbsd`_ | +## | FreeBSD| `getrandom freebsd`_ | ## | JS(Web Browser)| `getRandomValues`_| ## | Other Unix platforms| `/dev/urandom`_| ## @@ -47,31 +47,8 @@ import std/os when defined(posix): import std/posix - const batchSize = 256 - - template getDevUrandom(p: var openArray[byte]): int = - let size = p.len - if size == 0: - return - - let fd = posix.open("/dev/urandom", O_RDONLY) - - if fd > 0: - var stat: Stat - if fstat(fd, stat) != -1 and S_ISCHR(stat.st_mode): - let - chunks = (size - 1) div batchSize - left = size - chunks * batchSize - - var base = 0 - for i in 0 ..< chunks: - let readBytes = posix.read(fd, addr p[base], batchSize) - if readBytes < 0: - return readBytes - inc(base, batchSize) - - result = posix.read(fd, addr p[base], left) - discard posix.close(fd) + when not defined(linux): + const batchSize = 256 when defined(js): import std/private/jsutils @@ -84,20 +61,19 @@ when defined(js): let size = p.len if size > 0: let chunks = (size - 1) div batchSize - var base = 0 for i in 0 ..< chunks: for j in 0 ..< batchSize: var src = newUint8Array(batchSize) getRandomValues(src) - p[base + j] = src[j] + p[result + j] = src[j] - inc(base, batchSize) + inc(result, batchSize) let left = size - chunks * batchSize var src = newUint8Array(left) getRandomValues(src) for i in 0 ..< left: - p[base + i] = src[i] + p[result + i] = src[i] elif defined(windows): type @@ -236,7 +212,28 @@ elif defined(macosx): result = getentropy(addr p[base], csize_t(left)) else: proc urandom*(p: var openArray[byte]): int = - result = getDevUrandom(p) + let size = p.len + if size == 0: + return + + let fd = posix.open("/dev/urandom", O_RDONLY) + + if fd > 0: + var stat: Stat + if fstat(fd, stat) != -1 and S_ISCHR(stat.st_mode): + let + chunks = (size - 1) div batchSize + left = size - chunks * batchSize + + var base = 0 + for i in 0 ..< chunks: + let readBytes = posix.read(fd, addr p[base], batchSize) + if readBytes < 0: + return readBytes + inc(base, batchSize) + + result = posix.read(fd, addr p[base], left) + discard posix.close(fd) proc urandom*(size: Natural): seq[byte] {.inline.} = ## Returns random bytes suitable for cryptographic use. From f0b5fda8549730d210fceb6764f1421e3d502881 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 26 Jan 2021 22:25:47 +0800 Subject: [PATCH 44/64] add nodejs support --- tests/stdlib/tsysrand.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/stdlib/tsysrand.nim b/tests/stdlib/tsysrand.nim index b5e6dba2be57..d248849f8401 100644 --- a/tests/stdlib/tsysrand.nim +++ b/tests/stdlib/tsysrand.nim @@ -1,5 +1,5 @@ discard """ - targets: "c cpp" + targets: "c cpp js" """ import std/sysrand From 90ed94f6071c87a429d478c3ac3858ae7285b642 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 26 Jan 2021 22:25:54 +0800 Subject: [PATCH 45/64] done --- lib/std/sysrand.nim | 73 ++++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 27 deletions(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index f7e04784b703..e997c278e60e 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -11,23 +11,25 @@ ## ## | Targets | Implementation| ## | :--- | ----: | -## | Windows| `BCryptGenRandom`_ | -## | Linux| `getrandom`_ | -## | MacOSX| `getentropy`_ | -## | IOS | `SecRandomCopyBytes`_ | -## | OpenBSD| `getentropy openbsd`_ | -## | FreeBSD| `getrandom freebsd`_ | -## | JS(Web Browser)| `getRandomValues`_| -## | Other Unix platforms| `/dev/urandom`_| +## | 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`_ | ## ## .. _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 -## .. _/dev/urandom: https://en.wikipedia.org/wiki//dev/random ## .. _getentropy: https://www.unix.com/man-page/mojave/2/getentropy ## .. _SecRandomCopyBytes: https://developer.apple.com/documentation/security/1399291-secrandomcopybytes?language=objc ## .. _getentropy openbsd: https://man.openbsd.org/getentropy.2 ## .. _getrandom freebsd: https://www.freebsd.org/cgi/man.cgi?query=getrandom&manpath=FreeBSD+12.0-stable ## .. _getRandomValues: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues +## .. _randomFillSync: https://nodejs.org/api/crypto.html#crypto_crypto_randomfillsync_buffer_offset_size +## .. _/dev/urandom: https://en.wikipedia.org/wiki//dev/random ## runnableExamples: @@ -39,7 +41,7 @@ runnableExamples: ## See also ## ======== ## * `random module `_ -## +## import std/os @@ -53,27 +55,41 @@ when defined(posix): when defined(js): import std/private/jsutils - const batchSize = 256 + when defined(nodejs): + {.emit: "const _nim_nodejs_crypto = require('crypto');".} - proc getRandomValues(arr: Uint8Array) {.importjs: "window.crypto.getRandomValues(#)".} + proc randomFillSync(x: Uint8Array) {.importjs: "_nim_nodejs_crypto.randomFillSync(#)".} - proc urandom*(p: var openArray[byte]): int = - let size = p.len - if size > 0: - let chunks = (size - 1) div batchSize - for i in 0 ..< chunks: - for j in 0 ..< batchSize: - var src = newUint8Array(batchSize) - getRandomValues(src) - p[result + j] = src[j] + proc urandom(x: var openArray[byte]): int = + let size = x.len + if size > 0: + var t = newUint8Array(size) + randomFillSync(t) + for i in 0 ..< size: + x[i] = t[i] + + else: + const batchSize = 256 + + proc getRandomValues(arr: Uint8Array) {.importjs: "window.crypto.getRandomValues(#)".} + + proc urandom*(p: var openArray[byte]): int = + let size = p.len + if size > 0: + let chunks = (size - 1) div batchSize + for i in 0 ..< chunks: + for j in 0 ..< batchSize: + var src = newUint8Array(batchSize) + getRandomValues(src) + p[result + j] = src[j] - inc(result, batchSize) + inc(result, batchSize) - let left = size - chunks * batchSize - var src = newUint8Array(left) - getRandomValues(src) - for i in 0 ..< left: - p[result + i] = src[i] + let left = size - chunks * batchSize + var src = newUint8Array(left) + getRandomValues(src) + for i in 0 ..< left: + p[result + i] = src[i] elif defined(windows): type @@ -133,6 +149,9 @@ elif defined(linux): elif defined(openbsd): proc getentropy(p: pointer, size: cint): cint {.importc: "getentropy", header: "".} + # 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 urandom*(p: var openArray[byte]): int = let size = p.len From edf1293626f59f35a0c1c17e86528f412656e1c1 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 26 Jan 2021 22:32:26 +0800 Subject: [PATCH 46/64] better style --- lib/std/sysrand.nim | 172 ++++++++++++++++++++++++-------------------- 1 file changed, 93 insertions(+), 79 deletions(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index e997c278e60e..e6c9bf5d4aa8 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -58,38 +58,42 @@ when defined(js): when defined(nodejs): {.emit: "const _nim_nodejs_crypto = require('crypto');".} - proc randomFillSync(x: Uint8Array) {.importjs: "_nim_nodejs_crypto.randomFillSync(#)".} + proc randomFillSync(p: Uint8Array) {.importjs: "_nim_nodejs_crypto.randomFillSync(#)".} - proc urandom(x: var openArray[byte]): int = - let size = x.len - if size > 0: - var t = newUint8Array(size) - randomFillSync(t) - for i in 0 ..< size: - x[i] = t[i] + proc urandom(dest: var openArray[byte]): int = + let size = dest.len + if size == 0: + return + + var src = newUint8Array(size) + randomFillSync(src) + for i in 0 ..< size: + dest[i] = src[i] else: const batchSize = 256 - proc getRandomValues(arr: Uint8Array) {.importjs: "window.crypto.getRandomValues(#)".} + proc getRandomValues(p: Uint8Array) {.importjs: "window.crypto.getRandomValues(#)".} - proc urandom*(p: var openArray[byte]): int = - let size = p.len - if size > 0: - let chunks = (size - 1) div batchSize - for i in 0 ..< chunks: - for j in 0 ..< batchSize: - var src = newUint8Array(batchSize) - getRandomValues(src) - p[result + j] = src[j] + proc urandom*(dest: var openArray[byte]): int = + let size = dest.len + if size == 0: + return + + let chunks = (size - 1) div batchSize + for i in 0 ..< chunks: + for j in 0 ..< batchSize: + var src = newUint8Array(batchSize) + getRandomValues(src) + dest[result + j] = src[j] - inc(result, batchSize) + inc(result, batchSize) - let left = size - chunks * batchSize - var src = newUint8Array(left) - getRandomValues(src) - for i in 0 ..< left: - p[result + i] = src[i] + let left = size - chunks * batchSize + var src = newUint8Array(left) + getRandomValues(src) + for i in 0 ..< left: + dest[result + i] = src[i] elif defined(windows): type @@ -115,10 +119,12 @@ elif defined(windows): bCryptGenRandom(nil, cast[PUCHAR](pbBuffer), ULONG(cbBuffer), BCRYPT_USE_SYSTEM_PREFERRED_RNG) - proc urandom*(p: var openArray[byte]): int = - let size = p.len - if size > 0: - result = randomBytes(addr p[0], size) + proc urandom*(dest: var openArray[byte]): int = + let size = dest.len + if size == 0: + return + + result = randomBytes(addr dest[0], size) elif defined(linux): let SYS_getrandom {.importc: "SYS_getrandom", header: "".}: clong @@ -129,13 +135,13 @@ elif defined(linux): n: clong, buf: pointer, bufLen: cint, flags: cuint ): int {.importc: "syscall", header: syscallHeader.} - proc urandom*(p: var openArray[byte]): int = - let size = p.len + proc urandom*(dest: var openArray[byte]): int = + let size = dests.len if size == 0: return while result < size: - let readBytes = syscall(SYS_getrandom, addr p[result], cint(size - result), 0) + let readBytes = syscall(SYS_getrandom, addr dest[result], cint(size - result), 0) if readBytes == 0: break elif readBytes > 0: @@ -153,40 +159,44 @@ elif defined(openbsd): # which can be used as input for process-context pseudorandom generators like `arc4random`. # The maximum buffer size permitted is 256 bytes. - proc urandom*(p: var openArray[byte]): int = - let size = p.len - if size > 0: - let - chunks = (size - 1) div batchSize - left = size - chunks * batchSize - var base = 0 - for i in 0 ..< chunks: - let readBytes = getentropy(addr p[base], cint(batchSize)) - if readBytes < 0: - return readBytes - inc(base, batchSize) + proc urandom*(dest: var openArray[byte]): int = + let size = dest.len + if size == 0: + return + + let + chunks = (size - 1) div batchSize + left = size - chunks * batchSize + var base = 0 + for i in 0 ..< chunks: + let readBytes = getentropy(addr dest[base], cint(batchSize)) + if readBytes < 0: + return readBytes + inc(base, batchSize) - result = getentropy(addr p[base], cint(left)) + result = getentropy(addr dest[base], cint(left)) elif defined(freebsd): type cssize_t {.importc: "ssize_t", header: "".} = int proc getrandom(p: pointer, size: csize_t, flags: cuint): cssize_t {.importc: "getrandom", header: "".} - proc urandom*(p: var openArray[byte]): int = - let size = p.len - if size > 0: - let - chunks = (size - 1) div batchSize - left = size - chunks * batchSize - var base = 0 - for i in 0 ..< chunks: - let readBytes = getrandom(addr p[base], csize_t(batchSize), 0) - if readBytes < 0: - return readBytes - inc(base, batchSize) + proc urandom*(dest: var openArray[byte]): int = + let size = dest.len + if size == 0: + return - result = getrandom(addr p[base], csize_t(left), 0) + let + chunks = (size - 1) div batchSize + left = size - chunks * batchSize + var base = 0 + for i in 0 ..< chunks: + let readBytes = getrandom(addr dest[base], csize_t(batchSize), 0) + if readBytes < 0: + return readBytes + inc(base, batchSize) + + result = getrandom(addr dest[base], csize_t(left), 0) elif defined(ios): {.passL: "-framework Security".} @@ -202,11 +212,13 @@ elif defined(ios): rnd: SecRandomRef, count: csize_t, bytes: pointer ): cint {.importc: "SecRandomCopyBytes", header: "".} - proc urandom*(p: var openArray[byte]): int = - let size = p.len - if size > 0: - # `kSecRandomDefault` is a synonym for NULL. - result = secRandomCopyBytes(nil, csize_t(size), addr p[0]) + proc urandom*(dest: var openArray[byte]): int = + let size = dest.len + if size == 0: + return + + # `kSecRandomDefault` is a synonym for NULL. + result = secRandomCopyBytes(nil, csize_t(size), addr dest[0]) elif defined(macosx): const sysrandomHeader = """#include @@ -215,23 +227,25 @@ elif defined(macosx): proc getentropy(p: pointer, size: csize_t): cint {.importc: "getentropy", header: sysrandomHeader.} - proc urandom*(p: var openArray[byte]): int = - let size = p.len - if size > 0: - let - chunks = (size - 1) div batchSize - left = size - chunks * batchSize - var base = 0 - for i in 0 ..< chunks: - let readBytes = getentropy(addr p[base], csize_t(batchSize)) - if readBytes < 0: - return readBytes - inc(base, batchSize) + proc urandom*(dest: var openArray[byte]): int = + let size = dest.len + if size == 0: + return - result = getentropy(addr p[base], csize_t(left)) + let + chunks = (size - 1) div batchSize + left = size - chunks * batchSize + var base = 0 + for i in 0 ..< chunks: + let readBytes = getentropy(addr dest[base], csize_t(batchSize)) + if readBytes < 0: + return readBytes + inc(base, batchSize) + + result = getentropy(addr dest[base], csize_t(left)) else: - proc urandom*(p: var openArray[byte]): int = - let size = p.len + proc urandom*(dest: var openArray[byte]): int = + let size = dest.len if size == 0: return @@ -246,12 +260,12 @@ else: var base = 0 for i in 0 ..< chunks: - let readBytes = posix.read(fd, addr p[base], batchSize) + let readBytes = posix.read(fd, addr dest[base], batchSize) if readBytes < 0: return readBytes inc(base, batchSize) - result = posix.read(fd, addr p[base], left) + result = posix.read(fd, addr dest[base], left) discard posix.close(fd) proc urandom*(size: Natural): seq[byte] {.inline.} = From e376ea4882201f6c738dae6d21d71a87a3d3b442 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 26 Jan 2021 22:53:21 +0800 Subject: [PATCH 47/64] fix --- lib/std/sysrand.nim | 91 +++++++++++++++++++-------------------------- 1 file changed, 39 insertions(+), 52 deletions(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index e6c9bf5d4aa8..ab28be1b8c0f 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -52,6 +52,23 @@ when defined(posix): when not defined(linux): const batchSize = 256 + template batchImpl(result: var int, dest: var openArray[byte], getRandomImpl) = + let size = dest.len + if size == 0: + return + + let + chunks = (size - 1) div batchSize + left = size - chunks * batchSize + var base = 0 + for i in 0 ..< chunks: + let readBytes = getRandomImpl(addr dest[base], batchSize) + if readBytes < 0: + return readBytes + inc(base, batchSize) + + result = getRandomImpl(addr dest[base], left) + when defined(js): import std/private/jsutils @@ -60,7 +77,7 @@ when defined(js): proc randomFillSync(p: Uint8Array) {.importjs: "_nim_nodejs_crypto.randomFillSync(#)".} - proc urandom(dest: var openArray[byte]): int = + template urandomImpl(dest: var openArray[byte]): int = let size = dest.len if size == 0: return @@ -75,7 +92,7 @@ when defined(js): proc getRandomValues(p: Uint8Array) {.importjs: "window.crypto.getRandomValues(#)".} - proc urandom*(dest: var openArray[byte]): int = + template urandomImpl(dest: var openArray[byte]): int = let size = dest.len if size == 0: return @@ -119,7 +136,7 @@ elif defined(windows): bCryptGenRandom(nil, cast[PUCHAR](pbBuffer), ULONG(cbBuffer), BCRYPT_USE_SYSTEM_PREFERRED_RNG) - proc urandom*(dest: var openArray[byte]): int = + proc urandomImpl(dest: var openArray[byte]): int {.inline.} = let size = dest.len if size == 0: return @@ -135,8 +152,8 @@ elif defined(linux): n: clong, buf: pointer, bufLen: cint, flags: cuint ): int {.importc: "syscall", header: syscallHeader.} - proc urandom*(dest: var openArray[byte]): int = - let size = dests.len + proc urandomImpl(dest: var openArray[byte]): int {.inline.} = + let size = dest.len if size == 0: return @@ -159,44 +176,22 @@ elif defined(openbsd): # which can be used as input for process-context pseudorandom generators like `arc4random`. # The maximum buffer size permitted is 256 bytes. - proc urandom*(dest: var openArray[byte]): int = - let size = dest.len - if size == 0: - return - - let - chunks = (size - 1) div batchSize - left = size - chunks * batchSize - var base = 0 - for i in 0 ..< chunks: - let readBytes = getentropy(addr dest[base], cint(batchSize)) - if readBytes < 0: - return readBytes - inc(base, batchSize) + proc getRandomImpl(p: pointer, size: int): int {.inline.} = + result = getentropy(p, cint(size)).int - result = getentropy(addr dest[base], cint(left)) + proc urandomImpl(dest: var openArray[byte]): int {.inline.} = + batchImpl(result, dest, getRandomImpl) elif defined(freebsd): type cssize_t {.importc: "ssize_t", header: "".} = int proc getrandom(p: pointer, size: csize_t, flags: cuint): cssize_t {.importc: "getrandom", header: "".} - proc urandom*(dest: var openArray[byte]): int = - let size = dest.len - if size == 0: - return - - let - chunks = (size - 1) div batchSize - left = size - chunks * batchSize - var base = 0 - for i in 0 ..< chunks: - let readBytes = getrandom(addr dest[base], csize_t(batchSize), 0) - if readBytes < 0: - return readBytes - inc(base, batchSize) + proc getRandomImpl(p: pointer, size: int): int {.inline.} = + result = getrandom(p, csize_t(batchSize), 0) - result = getrandom(addr dest[base], csize_t(left), 0) + proc urandomImpl(dest: var openArray[byte]): int {.inline.} = + batchImpl(result, dest, getRandomImpl) elif defined(ios): {.passL: "-framework Security".} @@ -212,7 +207,7 @@ elif defined(ios): rnd: SecRandomRef, count: csize_t, bytes: pointer ): cint {.importc: "SecRandomCopyBytes", header: "".} - proc urandom*(dest: var openArray[byte]): int = + proc urandomImpl(dest: var openArray[byte]): int {.inline.} = let size = dest.len if size == 0: return @@ -227,24 +222,13 @@ elif defined(macosx): proc getentropy(p: pointer, size: csize_t): cint {.importc: "getentropy", header: sysrandomHeader.} - proc urandom*(dest: var openArray[byte]): int = - let size = dest.len - if size == 0: - return + proc getRandomImpl(p: pointer, size: int): int {.inline.} = + result = getentropy(p, csize_t(size)).int - let - chunks = (size - 1) div batchSize - left = size - chunks * batchSize - var base = 0 - for i in 0 ..< chunks: - let readBytes = getentropy(addr dest[base], csize_t(batchSize)) - if readBytes < 0: - return readBytes - inc(base, batchSize) - - result = getentropy(addr dest[base], csize_t(left)) + proc urandomImpl(dest: var openArray[byte]): int {.inline.} = + batchImpl(result, dest, getRandomImpl) else: - proc urandom*(dest: var openArray[byte]): int = + proc urandomImpl(dest: var openArray[byte]): int {.inline.} = let size = dest.len if size == 0: return @@ -268,6 +252,9 @@ else: result = posix.read(fd, addr dest[base], left) discard posix.close(fd) +proc urandom*(dest: var openArray[byte]): int = + result = urandomImpl(dest) + proc urandom*(size: Natural): seq[byte] {.inline.} = ## Returns random bytes suitable for cryptographic use. result = newSeq[byte](size) From 4c86fa583d6eee8d2d0986484caa6657ed33d238 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 26 Jan 2021 22:59:04 +0800 Subject: [PATCH 48/64] better --- lib/std/sysrand.nim | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index ab28be1b8c0f..2a01c01c8a4a 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -77,7 +77,7 @@ when defined(js): proc randomFillSync(p: Uint8Array) {.importjs: "_nim_nodejs_crypto.randomFillSync(#)".} - template urandomImpl(dest: var openArray[byte]): int = + template urandomImpl(dest: var openArray[byte]) = let size = dest.len if size == 0: return @@ -92,7 +92,7 @@ when defined(js): proc getRandomValues(p: Uint8Array) {.importjs: "window.crypto.getRandomValues(#)".} - template urandomImpl(dest: var openArray[byte]): int = + template urandomImpl(dest: var openArray[byte]) = let size = dest.len if size == 0: return @@ -136,7 +136,7 @@ elif defined(windows): bCryptGenRandom(nil, cast[PUCHAR](pbBuffer), ULONG(cbBuffer), BCRYPT_USE_SYSTEM_PREFERRED_RNG) - proc urandomImpl(dest: var openArray[byte]): int {.inline.} = + template urandomImpl(dest: var openArray[byte]) = let size = dest.len if size == 0: return @@ -152,8 +152,8 @@ elif defined(linux): n: clong, buf: pointer, bufLen: cint, flags: cuint ): int {.importc: "syscall", header: syscallHeader.} - proc urandomImpl(dest: var openArray[byte]): int {.inline.} = - let size = dest.len + template urandomImpl(dest: var openArray[byte]) = + let size = dests.len if size == 0: return @@ -179,7 +179,7 @@ elif defined(openbsd): proc getRandomImpl(p: pointer, size: int): int {.inline.} = result = getentropy(p, cint(size)).int - proc urandomImpl(dest: var openArray[byte]): int {.inline.} = + template urandomImpl(dest: var openArray[byte]) = batchImpl(result, dest, getRandomImpl) elif defined(freebsd): @@ -190,7 +190,7 @@ elif defined(freebsd): proc getRandomImpl(p: pointer, size: int): int {.inline.} = result = getrandom(p, csize_t(batchSize), 0) - proc urandomImpl(dest: var openArray[byte]): int {.inline.} = + template urandomImpl(dest: var openArray[byte]) = batchImpl(result, dest, getRandomImpl) elif defined(ios): @@ -207,7 +207,7 @@ elif defined(ios): rnd: SecRandomRef, count: csize_t, bytes: pointer ): cint {.importc: "SecRandomCopyBytes", header: "".} - proc urandomImpl(dest: var openArray[byte]): int {.inline.} = + template urandomImpl(dest: var openArray[byte]) = let size = dest.len if size == 0: return @@ -225,10 +225,10 @@ elif defined(macosx): proc getRandomImpl(p: pointer, size: int): int {.inline.} = result = getentropy(p, csize_t(size)).int - proc urandomImpl(dest: var openArray[byte]): int {.inline.} = + template urandomImpl(dest: var openArray[byte]) = batchImpl(result, dest, getRandomImpl) else: - proc urandomImpl(dest: var openArray[byte]): int {.inline.} = + template urandomImpl(dest: var openArray[byte]) = let size = dest.len if size == 0: return @@ -253,7 +253,8 @@ else: discard posix.close(fd) proc urandom*(dest: var openArray[byte]): int = - result = urandomImpl(dest) + ## Fills `dest` with random bytes suitable for cryptographic use. + urandomImpl(dest) proc urandom*(size: Natural): seq[byte] {.inline.} = ## Returns random bytes suitable for cryptographic use. From 7bffe1eb73bfcd126bcf55d2fbb658c8b9654bd8 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 26 Jan 2021 23:49:22 +0800 Subject: [PATCH 49/64] fix --- lib/std/sysrand.nim | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index 2a01c01c8a4a..a49758877d67 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -151,6 +151,12 @@ elif defined(linux): proc syscall( n: clong, buf: pointer, bufLen: cint, flags: cuint ): int {.importc: "syscall", 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 + # request is made to read a large number of bytes (more than 256), + # getrandom() will block until those bytes have been generated and + # transferred from kernel memory to buf. template urandomImpl(dest: var openArray[byte]) = let size = dests.len @@ -186,6 +192,10 @@ elif defined(freebsd): type cssize_t {.importc: "ssize_t", header: "".} = int proc getrandom(p: pointer, size: csize_t, flags: cuint): cssize_t {.importc: "getrandom", header: "".} + # Upon successful completion, the number of bytes which were actually read + # is returned. For requests larger than 256 bytes, this can be fewer bytes + # than were requested. Otherwise, -1 is returned and the global variable + # errno is set to indicate the error. proc getRandomImpl(p: pointer, size: int): int {.inline.} = result = getrandom(p, csize_t(batchSize), 0) @@ -196,7 +206,8 @@ elif defined(freebsd): elif defined(ios): {.passL: "-framework Security".} - const errSecSuccess = 0 + const errSecSuccess = 0 ## No error. + type SecRandom {.importc: "struct __SecRandom".} = object @@ -206,6 +217,7 @@ elif defined(ios): proc secRandomCopyBytes( rnd: SecRandomRef, count: csize_t, bytes: pointer ): cint {.importc: "SecRandomCopyBytes", header: "".} + ## Generates an array of cryptographically secure random bytes. template urandomImpl(dest: var openArray[byte]) = let size = dest.len @@ -221,6 +233,9 @@ elif defined(macosx): """ 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 @@ -254,6 +269,9 @@ else: proc urandom*(dest: var openArray[byte]): int = ## Fills `dest` with random bytes suitable for cryptographic use. + ## + ## If `dest` is empty, `urandom` immediately returns success, + ## without calling underlying operating system api. urandomImpl(dest) proc urandom*(size: Natural): seq[byte] {.inline.} = From dd61e99a1b23f9f3b624916dcb3ea5a1a4171960 Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 27 Jan 2021 00:09:17 +0800 Subject: [PATCH 50/64] typo --- lib/std/sysrand.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index a49758877d67..ccb148a06d1c 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -8,7 +8,7 @@ # ## Cryptographically secure pseudorandom number generator. -## +## ## | Targets | Implementation| ## | :--- | ----: | ## | Windows | `BCryptGenRandom`_ | @@ -159,7 +159,7 @@ elif defined(linux): # transferred from kernel memory to buf. template urandomImpl(dest: var openArray[byte]) = - let size = dests.len + let size = dest.len if size == 0: return From 1fc359a1742c3938d1287317fc446c32534f802d Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 27 Jan 2021 10:48:23 +0800 Subject: [PATCH 51/64] resolve comments --- lib/std/sysrand.nim | 48 +++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index ccb148a06d1c..5e3accb5e7f2 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -49,25 +49,25 @@ import std/os when defined(posix): import std/posix - when not defined(linux): - const batchSize = 256 +when defined(freebsd) or defined(openbsd) or (defined(macosx) and not defined(ios)): + const batchSize = 256 - template batchImpl(result: var int, dest: var openArray[byte], getRandomImpl) = - let size = dest.len - if size == 0: - return + template batchImpl(result: var int, dest: var openArray[byte], getRandomImpl) = + let size = dest.len + if size == 0: + return - let - chunks = (size - 1) div batchSize - left = size - chunks * batchSize - var base = 0 - for i in 0 ..< chunks: - let readBytes = getRandomImpl(addr dest[base], batchSize) - if readBytes < 0: - return readBytes - inc(base, batchSize) + let + chunks = (size - 1) div batchSize + left = size - chunks * batchSize + var base = 0 + for i in 0 ..< chunks: + let readBytes = getRandomImpl(addr dest[base], batchSize) + if readBytes < 0: + return readBytes + inc(base, batchSize) - result = getRandomImpl(addr dest[base], left) + result = getRandomImpl(addr dest[base], left) when defined(js): import std/private/jsutils @@ -97,7 +97,9 @@ when defined(js): if size == 0: return - let chunks = (size - 1) div batchSize + let + chunks = (size - 1) div batchSize + left = size - chunks * batchSize for i in 0 ..< chunks: for j in 0 ..< batchSize: var src = newUint8Array(batchSize) @@ -106,7 +108,6 @@ when defined(js): inc(result, batchSize) - let left = size - chunks * batchSize var src = newUint8Array(left) getRandomValues(src) for i in 0 ..< left: @@ -150,7 +151,7 @@ elif defined(linux): proc syscall( n: clong, buf: pointer, bufLen: cint, flags: cuint - ): int {.importc: "syscall", header: syscallHeader.} + ): clong {.importc: "syscall", 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 @@ -164,7 +165,7 @@ elif defined(linux): return while result < size: - let readBytes = syscall(SYS_getrandom, addr dest[result], cint(size - result), 0) + let readBytes = syscall(SYS_getrandom, addr dest[result], cint(size - result), 0).int if readBytes == 0: break elif readBytes > 0: @@ -192,8 +193,8 @@ elif defined(freebsd): type cssize_t {.importc: "ssize_t", header: "".} = int proc getrandom(p: pointer, size: csize_t, flags: cuint): cssize_t {.importc: "getrandom", header: "".} - # Upon successful completion, the number of bytes which were actually read - # is returned. For requests larger than 256 bytes, this can be fewer bytes + # Upon successful completion, the number of bytes which were actually read + # is returned. For requests larger than 256 bytes, this can be fewer bytes # than were requested. Otherwise, -1 is returned and the global variable # errno is set to indicate the error. @@ -248,7 +249,9 @@ else: if size == 0: return + # see: https://www.2uo.de/myths-about-urandom/ which justifies using urandom instead of random let fd = posix.open("/dev/urandom", O_RDONLY) + defer: discard posix.close(fd) if fd > 0: var stat: Stat @@ -265,7 +268,6 @@ else: inc(base, batchSize) result = posix.read(fd, addr dest[base], left) - discard posix.close(fd) proc urandom*(dest: var openArray[byte]): int = ## Fills `dest` with random bytes suitable for cryptographic use. From bf592f6b35a0358ff3254182910398b71fa64b15 Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 27 Jan 2021 11:00:02 +0800 Subject: [PATCH 52/64] better --- lib/std/sysrand.nim | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index 5e3accb5e7f2..65dd351261cb 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -269,21 +269,29 @@ else: result = posix.read(fd, addr dest[base], left) -proc urandom*(dest: var openArray[byte]): int = +proc urandomInternalImpl(dest: var openArray[byte]): int {.inline.} = + urandomImpl(dest) + +proc urandom*(dest: var openArray[byte]): bool = ## Fills `dest` with random bytes suitable for cryptographic use. ## ## If `dest` is empty, `urandom` immediately returns success, ## without calling underlying operating system api. - urandomImpl(dest) + result = true + let ret = urandomInternalImpl(dest) + when defined(js): discard + elif defined(windows): + if ret != STATUS_SUCCESS: + result = false + else: + if ret < 0: + result = false proc urandom*(size: Natural): seq[byte] {.inline.} = ## Returns random bytes suitable for cryptographic use. result = newSeq[byte](size) let ret = urandom(result) - when defined(js): discard ret - elif defined(windows): - if ret != STATUS_SUCCESS: - raiseOsError(osLastError()) + when defined(js): discard else: - if ret < 0: + if not ret: raiseOsError(osLastError()) From 2445403a372bcad135af7ea1683a68a11cf11057 Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 27 Jan 2021 11:49:45 +0800 Subject: [PATCH 53/64] better comments --- lib/std/sysrand.nim | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index 65dd351261cb..f30d27becc2c 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -91,6 +91,7 @@ when defined(js): const batchSize = 256 proc getRandomValues(p: Uint8Array) {.importjs: "window.crypto.getRandomValues(#)".} + # The requested length of `p` must not be more than 65536. template urandomImpl(dest: var openArray[byte]) = let size = dest.len @@ -133,7 +134,7 @@ elif defined(windows): ): NTSTATUS {.stdcall, importc: "BCryptGenRandom", dynlib: "Bcrypt.dll".} - proc randomBytes(pbBuffer: pointer, cbBuffer: Natural): int = + proc randomBytes(pbBuffer: pointer, cbBuffer: Natural): int {.inline.} = bCryptGenRandom(nil, cast[PUCHAR](pbBuffer), ULONG(cbBuffer), BCRYPT_USE_SYSTEM_PREFERRED_RNG) @@ -274,7 +275,8 @@ proc urandomInternalImpl(dest: var openArray[byte]): int {.inline.} = proc urandom*(dest: var openArray[byte]): bool = ## Fills `dest` with random bytes suitable for cryptographic use. - ## + ## If succeed, returns `true`. + ## ## If `dest` is empty, `urandom` immediately returns success, ## without calling underlying operating system api. result = true From 8fffc5d32d8677dd0355db65a535d75cef9cb83c Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 27 Jan 2021 12:24:11 +0800 Subject: [PATCH 54/64] fix errors --- lib/std/sysrand.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index f30d27becc2c..8d24bfbcc6f2 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -101,10 +101,11 @@ when defined(js): let chunks = (size - 1) div batchSize left = size - chunks * batchSize + + var src = newUint8Array(batchSize) for i in 0 ..< chunks: + getRandomValues(src) for j in 0 ..< batchSize: - var src = newUint8Array(batchSize) - getRandomValues(src) dest[result + j] = src[j] inc(result, batchSize) From 99f6a1ae0fc1fe85a6e67a56c369cbde7015e69c Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 27 Jan 2021 12:43:39 +0800 Subject: [PATCH 55/64] fix --- lib/std/sysrand.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index 8d24bfbcc6f2..76c2514ebb05 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -110,7 +110,6 @@ when defined(js): inc(result, batchSize) - var src = newUint8Array(left) getRandomValues(src) for i in 0 ..< left: dest[result + i] = src[i] From 1b88b84e9a82c858ca42c0c1f58ec33636406106 Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 27 Jan 2021 13:00:51 +0800 Subject: [PATCH 56/64] better js --- lib/std/sysrand.nim | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index 76c2514ebb05..94c1bfc17ee1 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -98,21 +98,29 @@ when defined(js): if size == 0: return + if size <= 256: + var src = newUint8Array(size) + getRandomValues(src) + for i in 0 ..< size: + dest[i] = src[i] + return + let chunks = (size - 1) div batchSize left = size - chunks * batchSize - var src = newUint8Array(batchSize) + var srcArray = newUint8Array(batchSize) for i in 0 ..< chunks: - getRandomValues(src) + getRandomValues(srcArray) for j in 0 ..< batchSize: - dest[result + j] = src[j] + dest[result + j] = srcArray[j] inc(result, batchSize) - getRandomValues(src) + var leftArray = newUint8Array(left) + getRandomValues(leftArray) for i in 0 ..< left: - dest[result + i] = src[i] + dest[result + i] = leftArray[i] elif defined(windows): type From ec67a1a2d302d897c9d950acba4af8b63a8c539f Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 27 Jan 2021 13:12:08 +0800 Subject: [PATCH 57/64] reduce size --- lib/std/sysrand.nim | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index 94c1bfc17ee1..fa208b96eed6 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -93,6 +93,11 @@ when defined(js): proc getRandomValues(p: Uint8Array) {.importjs: "window.crypto.getRandomValues(#)".} # The requested length of `p` must not be more than 65536. + proc assign(dest: var openArray[byte], src: Uint8Array, base: int, size: int) = + getRandomValues(src) + for j in 0 ..< size: + dest[base + j] = src[j] + template urandomImpl(dest: var openArray[byte]) = let size = dest.len if size == 0: @@ -100,9 +105,7 @@ when defined(js): if size <= 256: var src = newUint8Array(size) - getRandomValues(src) - for i in 0 ..< size: - dest[i] = src[i] + assign(dest, src, 0, size) return let @@ -111,16 +114,11 @@ when defined(js): var srcArray = newUint8Array(batchSize) for i in 0 ..< chunks: - getRandomValues(srcArray) - for j in 0 ..< batchSize: - dest[result + j] = srcArray[j] - + assign(dest, srcArray, result, batchSize) inc(result, batchSize) var leftArray = newUint8Array(left) - getRandomValues(leftArray) - for i in 0 ..< left: - dest[result + i] = leftArray[i] + assign(dest, leftArray, result, left) elif defined(windows): type From 9836c5be5dbbb78c265cccc882bafcddb14153f6 Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 27 Jan 2021 13:24:49 +0800 Subject: [PATCH 58/64] better --- lib/std/sysrand.nim | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index fa208b96eed6..cafc7beddcfa 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -60,14 +60,14 @@ when defined(freebsd) or defined(openbsd) or (defined(macosx) and not defined(io let chunks = (size - 1) div batchSize left = size - chunks * batchSize - var base = 0 + for i in 0 ..< chunks: - let readBytes = getRandomImpl(addr dest[base], batchSize) + let readBytes = getRandomImpl(addr dest[result], batchSize) if readBytes < 0: return readBytes - inc(base, batchSize) + inc(result, batchSize) - result = getRandomImpl(addr dest[base], left) + result = getRandomImpl(addr dest[result], left) when defined(js): import std/private/jsutils @@ -267,14 +267,13 @@ else: chunks = (size - 1) div batchSize left = size - chunks * batchSize - var base = 0 for i in 0 ..< chunks: - let readBytes = posix.read(fd, addr dest[base], batchSize) + let readBytes = posix.read(fd, addr dest[result], batchSize) if readBytes < 0: return readBytes - inc(base, batchSize) + inc(result, batchSize) - result = posix.read(fd, addr dest[base], left) + result = posix.read(fd, addr dest[result], left) proc urandomInternalImpl(dest: var openArray[byte]): int {.inline.} = urandomImpl(dest) From 8ee923eb9c2c37801b8b4247679ca8520712246a Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 27 Jan 2021 13:32:45 +0800 Subject: [PATCH 59/64] better impl --- lib/std/sysrand.nim | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index cafc7beddcfa..bacb530b8400 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -49,7 +49,9 @@ import std/os when defined(posix): import std/posix -when defined(freebsd) or defined(openbsd) or (defined(macosx) and not defined(ios)): +const batchImplOS = defined(freebsd) or defined(openbsd) or (defined(macosx) and not defined(ios)) + +when batchImplOS: const batchSize = 256 template batchImpl(result: var int, dest: var openArray[byte], getRandomImpl) = @@ -77,7 +79,7 @@ when defined(js): proc randomFillSync(p: Uint8Array) {.importjs: "_nim_nodejs_crypto.randomFillSync(#)".} - template urandomImpl(dest: var openArray[byte]) = + template urandomImpl(result: var int, dest: var openArray[byte]) = let size = dest.len if size == 0: return @@ -98,7 +100,7 @@ when defined(js): for j in 0 ..< size: dest[base + j] = src[j] - template urandomImpl(dest: var openArray[byte]) = + template urandomImpl(result: var int, dest: var openArray[byte]) = let size = dest.len if size == 0: return @@ -144,7 +146,7 @@ elif defined(windows): bCryptGenRandom(nil, cast[PUCHAR](pbBuffer), ULONG(cbBuffer), BCRYPT_USE_SYSTEM_PREFERRED_RNG) - template urandomImpl(dest: var openArray[byte]) = + template urandomImpl(result: var int, dest: var openArray[byte]) = let size = dest.len if size == 0: return @@ -166,7 +168,7 @@ elif defined(linux): # getrandom() will block until those bytes have been generated and # transferred from kernel memory to buf. - template urandomImpl(dest: var openArray[byte]) = + template urandomImpl(result: var int, dest: var openArray[byte]) = let size = dest.len if size == 0: return @@ -193,9 +195,6 @@ elif defined(openbsd): proc getRandomImpl(p: pointer, size: int): int {.inline.} = result = getentropy(p, cint(size)).int - template urandomImpl(dest: var openArray[byte]) = - batchImpl(result, dest, getRandomImpl) - elif defined(freebsd): type cssize_t {.importc: "ssize_t", header: "".} = int @@ -208,9 +207,6 @@ elif defined(freebsd): proc getRandomImpl(p: pointer, size: int): int {.inline.} = result = getrandom(p, csize_t(batchSize), 0) - template urandomImpl(dest: var openArray[byte]) = - batchImpl(result, dest, getRandomImpl) - elif defined(ios): {.passL: "-framework Security".} @@ -227,7 +223,7 @@ elif defined(ios): ): cint {.importc: "SecRandomCopyBytes", header: "".} ## Generates an array of cryptographically secure random bytes. - template urandomImpl(dest: var openArray[byte]) = + template urandomImpl(result: var int, dest: var openArray[byte]) = let size = dest.len if size == 0: return @@ -248,10 +244,8 @@ elif defined(macosx): proc getRandomImpl(p: pointer, size: int): int {.inline.} = result = getentropy(p, csize_t(size)).int - template urandomImpl(dest: var openArray[byte]) = - batchImpl(result, dest, getRandomImpl) else: - template urandomImpl(dest: var openArray[byte]) = + template urandomImpl(result: var int, dest: var openArray[byte]) = let size = dest.len if size == 0: return @@ -276,7 +270,10 @@ else: result = posix.read(fd, addr dest[result], left) proc urandomInternalImpl(dest: var openArray[byte]): int {.inline.} = - urandomImpl(dest) + when batchImplOS: + batchImpl(result, dest, getRandomImpl) + else: + urandomImpl(result, dest) proc urandom*(dest: var openArray[byte]): bool = ## Fills `dest` with random bytes suitable for cryptographic use. From c1cec170ef867a773e444add47cb86d33b40179d Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 27 Jan 2021 13:36:12 +0800 Subject: [PATCH 60/64] small --- lib/std/sysrand.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index bacb530b8400..a9d7a0a4b045 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -43,8 +43,9 @@ runnableExamples: ## * `random module `_ ## -import std/os +when not defined(js): + import std/os when defined(posix): import std/posix @@ -105,7 +106,7 @@ when defined(js): if size == 0: return - if size <= 256: + if size <= batchSize: var src = newUint8Array(size) assign(dest, src, 0, size) return From 2f716fc26123091dc6bfaf44581aa87458bd3ce5 Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 27 Jan 2021 13:52:04 +0800 Subject: [PATCH 61/64] better --- lib/std/sysrand.nim | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index a9d7a0a4b045..7358659b62da 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -283,20 +283,20 @@ proc urandom*(dest: var openArray[byte]): bool = ## If `dest` is empty, `urandom` immediately returns success, ## without calling underlying operating system api. result = true - let ret = urandomInternalImpl(dest) - when defined(js): discard - elif defined(windows): - if ret != STATUS_SUCCESS: - result = false + when defined(js): discard urandomInternalImpl(dest) else: - if ret < 0: - result = false + let ret = urandomInternalImpl(dest) + when defined(windows): + if ret != STATUS_SUCCESS: + result = false + else: + if ret < 0: + result = false proc urandom*(size: Natural): seq[byte] {.inline.} = ## Returns random bytes suitable for cryptographic use. result = newSeq[byte](size) - let ret = urandom(result) - when defined(js): discard + when defined(js): discard urandomInternalImpl(result) else: - if not ret: + if not urandom(result): raiseOsError(osLastError()) From 4693e8fa123bc7f11c7abaed784395761173b3a6 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 28 Jan 2021 11:01:36 +0800 Subject: [PATCH 62/64] finish --- changelog.md | 3 +++ doc/lib.rst | 3 +++ lib/pure/random.nim | 1 + lib/std/sysrand.nim | 4 +++- 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index af979fc931cf..295be58e5878 100644 --- a/changelog.md +++ b/changelog.md @@ -111,6 +111,9 @@ with other backends. see #9125. Use `-d:nimLegacyJsRound` for previous behavior. - Removed the optional `longestMatch` parameter of the `critbits._WithPrefix` iterators (it never worked reliably) +- Added `std/sysrand` module to get random numbers from a secure source +provided by the operating system. + ## Language changes - `nimscript` now handles `except Exception as e`. diff --git a/doc/lib.rst b/doc/lib.rst index 3055a89a70b0..11b479902cee 100644 --- a/doc/lib.rst +++ b/doc/lib.rst @@ -264,6 +264,9 @@ Math libraries * `random `_ Fast and tiny random number generator. +* `std/sysrand `_ + Cryptographically secure pseudorandom number generator. + * `rationals `_ This module implements rational numbers and relevant mathematical operations. diff --git a/lib/pure/random.nim b/lib/pure/random.nim index d599727c3329..151231433ad3 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -70,6 +70,7 @@ ## ## See also ## ======== +## * `std/sysrand module`_ for cryptographically secure pseudorandom number generator ## * `math module`_ for basic math routines ## * `mersenne module`_ for the Mersenne Twister random number ## generator diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index 7358659b62da..b0b5645850d9 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -7,7 +7,9 @@ # distribution, for details about the copyright. # -## Cryptographically secure pseudorandom number generator. +## `std/sysrand` generates random numbers from a secure source provided by the operating system. +## It should be unpredictable enough for cryptographic applications, +## though its exact quality depends on the OS implementation. ## ## | Targets | Implementation| ## | :--- | ----: | From 12edec2205087cee7d5acf5b4356f1a6051f0fcb Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 5 Feb 2021 16:11:44 +0800 Subject: [PATCH 63/64] fix --- lib/std/sysrand.nim | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index b0b5645850d9..0f70535c88a6 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -8,6 +8,7 @@ # ## `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, ## though its exact quality depends on the OS implementation. ## @@ -224,14 +225,13 @@ elif defined(ios): proc secRandomCopyBytes( rnd: SecRandomRef, count: csize_t, bytes: pointer ): cint {.importc: "SecRandomCopyBytes", header: "".} - ## Generates an array of cryptographically secure random bytes. + ## https://developer.apple.com/documentation/security/1399291-secrandomcopybytes template urandomImpl(result: var int, dest: var openArray[byte]) = let size = dest.len if size == 0: return - # `kSecRandomDefault` is a synonym for NULL. result = secRandomCopyBytes(nil, csize_t(size), addr dest[0]) elif defined(macosx): @@ -271,6 +271,10 @@ else: inc(result, batchSize) result = posix.read(fd, addr dest[result], left) + else: + result = -1 + else: + result = -1 proc urandomInternalImpl(dest: var openArray[byte]): int {.inline.} = when batchImplOS: From 8d2e7e2b1b8f90210346ac2f2332f470a34b5842 Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 5 Feb 2021 19:56:26 +0800 Subject: [PATCH 64/64] fix comments --- lib/std/sysrand.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index 0f70535c88a6..9a143adb3973 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -180,7 +180,7 @@ elif defined(linux): while result < size: let readBytes = syscall(SYS_getrandom, addr dest[result], cint(size - result), 0).int if readBytes == 0: - break + doAssert false elif readBytes > 0: inc(result, readBytes) else: