[m-rev.] for post-commit review: fix system RNG hanldes in the C# and Java grades

Julien Fischer jfischer at opturion.com
Fri Feb 19 23:19:19 AEDT 2021


Fix system RNG handles in the C# and Java grades.

Ensure it is not possible to use a system RNG handle after it has been closed
in the C# and Java grades. This brings the implementation for these backends
into line with the /dev/urandom implementation on C.  (The other C implementation
will be made to conform in a separate change.)

library/random.system_rng.m:
     Add wrapper classes around the C# and Java system RNGs that allow
     us to track if they have already been closed.

Julien.

diff --git a/library/random.system_rng.m b/library/random.system_rng.m
index a8d0843..7e7339f 100644
--- a/library/random.system_rng.m
+++ b/library/random.system_rng.m
@@ -203,7 +203,6 @@ extern ML_SystemRandomHandle ML_random_open(MR_Bool *succeeded,
  //
  extern MR_Bool ML_random_close(ML_SystemRandomHandle handle, MR_String *err_msg);

-
  // Fill buffer with len random bytes generated by the system RNG.
  // Returns MR_TRUE if len bytes were generated; err_msg will point to the empty
  // string.
@@ -215,12 +214,112 @@ extern MR_Bool ML_random_generate_bytes(ML_SystemRandomHandle handle,
      unsigned char *buffer, size_t len, MR_String *err_msg);
  ").

+:- pragma foreign_code("C#", "
+
+    public class ML_SystemRandomHandle {
+
+        private System.Security.Cryptography.RandomNumberGenerator handle;
+
+        public ML_SystemRandomHandle() {
+            handle =
+                System.Security.Cryptography.RandomNumberGenerator.Create();
+        }
+
+        public void close() {
+            handle = null;
+        }
+
+        public bool isClosed() {
+            return (handle == null);
+        }
+
+        public byte getByte() {
+            byte[] bytes = new byte[1];
+            handle.GetBytes(bytes);
+            return bytes[0];
+        }
+
+        public ushort getUShort() {
+            byte[] bytes = new byte[2];
+            handle.GetBytes(bytes);
+            return (ushort) (bytes[1] << 8 | (bytes[0] & 0x00ff));
+        }
+
+        public uint getUInt() {
+            byte[] bytes = new byte[4];
+            handle.GetBytes(bytes);
+            return (uint) (
+                bytes[3] << 24 |
+                bytes[2] << 16 |
+                bytes[1] << 8  |
+                bytes[0]);
+        }
+
+        public ulong getULong() {
+            byte[] bytes = new byte[8];
+            handle.GetBytes(bytes);
+            return (ulong) (
+                (ulong) bytes[7] << 56 |
+                (ulong) bytes[6] << 48 |
+                (ulong) bytes[5] << 40 |
+                (ulong) bytes[4] << 32 |
+                (ulong) bytes[3] << 24 |
+                (ulong) bytes[2] << 16 |
+                (ulong) bytes[1] << 8  |
+                (ulong) bytes[0]);
+        }
+    }
+").
+
+:- pragma foreign_code("Java", "
+
+    public static class ML_SystemRandomHandle {
+
+        private java.security.SecureRandom handle;
+
+        public ML_SystemRandomHandle() {
+            handle = new java.security.SecureRandom();
+        }
+
+        public void close() {
+            handle = null;
+        }
+
+        public boolean isClosed() {
+            return (handle == null);
+        }
+
+        public byte getByte() {
+            byte[] bytes = new byte[1];
+            handle.nextBytes(bytes);
+            return bytes[0];
+        }
+
+        public short getShort() {
+            byte[] bytes = new byte[2];
+            handle.nextBytes(bytes);
+            return (short)
+                (bytes[0] << java.lang.Byte.SIZE | (bytes[1] & 0x00ff));
+        }
+
+        public int getInt() {
+            return handle.nextInt();
+        }
+
+        public long getLong() {
+            return handle.nextLong();
+        }
+    }
+").
+
+%---------------------------------------------------------------------------%
+
  :- pragma foreign_type("C", system_rng,
      "ML_SystemRandomHandle", [can_pass_as_mercury_type]).
  :- pragma foreign_type("C#", system_rng,
-    "System.Security.Cryptography.RandomNumberGenerator").
+    "random__system_rng.ML_SystemRandomHandle").
  :- pragma foreign_type("Java", system_rng,
-    "java.security.SecureRandom").
+    "random__system_rng.ML_SystemRandomHandle").

  %---------------------------------------------------------------------------%

@@ -294,8 +393,7 @@ open_system_rng(Result, !IO) :-
          _IO0::di, _IO::uo),
      [will_not_call_mercury, promise_pure, thread_safe],
  "
-    Handle =
-        System.Security.Cryptography.RandomNumberGenerator.Create();
+    Handle = new random__system_rng.ML_SystemRandomHandle();
      IsOk = mr_bool.YES;
      ErrorMsg = \"\";
  ").
@@ -305,7 +403,7 @@ open_system_rng(Result, !IO) :-
          _IO0::di, _IO::uo),
      [will_not_call_mercury, promise_pure, thread_safe],
  "
-    Handle = new java.security.SecureRandom();
+    Handle = new random__system_rng.ML_SystemRandomHandle();
      IsOk = bool.YES;
      ErrorMsg = \"\";
  ").
@@ -337,9 +435,14 @@ close_system_rng(Handle, !IO) :-
          _IO0::di, _IO::uo),
      [will_not_call_mercury, promise_pure, thread_safe],
  "
-    // Handle
-    IsOk = mr_bool.YES;
-    ErrorMsg = \"\";
+    if (Handle.isClosed()) {
+        IsOk = mr_bool.NO;
+        ErrorMsg = \"system RNG handle already closed\";
+    } else {
+        Handle.close();
+        IsOk = mr_bool.YES;
+        ErrorMsg = \"\";
+    }
  ").

  :- pragma foreign_proc("Java",
@@ -347,9 +450,14 @@ close_system_rng(Handle, !IO) :-
          _IO0::di, _IO::uo),
      [will_not_call_mercury, promise_pure, thread_safe],
  "
-    // Handle
-    IsOk = bool.YES;
-    ErrorMsg = \"\";
+    if (Handle.isClosed()) {
+        IsOk = bool.NO;
+        ErrorMsg = \"system RNG handle already closed\";
+    } else {
+        Handle.close();
+        IsOk = bool.YES;
+        ErrorMsg = \"\";
+    }
  ").

  %---------------------------------------------------------------------------%
@@ -385,11 +493,15 @@ generate_uint8(Handle, U8, !IO) :-
          _IO0::di, _IO::uo),
      [will_not_call_mercury, promise_pure, thread_safe],
  "
-    byte[] bytes = new byte[1];
-    Handle.GetBytes(bytes);
-    U8 = bytes[0];
-    IsOk = mr_bool.YES;
-    ErrorMsg = \"\";
+    if (Handle.isClosed()) {
+        U8 = 0;
+        IsOk = mr_bool.NO;
+        ErrorMsg = \"system RNG handle is closed\";
+    } else {
+        U8 = Handle.getByte();
+        IsOk = mr_bool.NO;
+        ErrorMsg = \"\";
+    }
  ").

  :- pragma foreign_proc("Java",
@@ -397,11 +509,15 @@ generate_uint8(Handle, U8, !IO) :-
          _IO0::di, _IO::uo),
      [will_not_call_mercury, promise_pure, thread_safe],
  "
-    byte[] bytes = new byte[1];
-    Handle.nextBytes(bytes);
-    U8 = bytes[0];
-    IsOk = bool.YES;
-    ErrorMsg = \"\";
+    if (Handle.isClosed()) {
+        U8 = 0;
+        IsOk = bool.NO;
+        ErrorMsg = \"system RNG handle is closed\";
+    } else {
+        U8 = Handle.getByte();
+        IsOk = bool.YES;
+        ErrorMsg = \"\";
+    }
  ").

  %---------------------------------------------------------------------------%
@@ -437,11 +553,15 @@ generate_uint16(Handle, U16, !IO) :-
          _IO0::di, _IO::uo),
      [will_not_call_mercury, promise_pure, not_thread_safe],
  "
-    byte[] bytes = new byte[2];
-    Handle.GetBytes(bytes);
-    U16 = (ushort) (bytes[1] << 8 | (bytes[0] & 0x00ff));
-    IsOk = mr_bool.YES;
-    ErrorMsg = \"\";
+    if (Handle.isClosed()) {
+        U16 = 0;
+        IsOk = mr_bool.NO;
+        ErrorMsg = \"system RNG handle is closed\";
+    } else {
+        U16 = Handle.getUShort();
+        IsOk = mr_bool.YES;
+        ErrorMsg = \"\";
+    }
  ").

  :- pragma foreign_proc("Java",
@@ -449,11 +569,15 @@ generate_uint16(Handle, U16, !IO) :-
          _IO0::di, _IO::uo),
      [will_not_call_mercury, promise_pure, thread_safe],
  "
-    byte[] bytes = new byte[2];
-    Handle.nextBytes(bytes);
-    U16 = (short) (bytes[0] << java.lang.Byte.SIZE | (bytes[1] & 0x00ff));
-    IsOk = bool.YES;
-    ErrorMsg = \"\";
+    if (Handle.isClosed()) {
+        U16 = 0;
+        IsOk = bool.NO;
+        ErrorMsg = \"system RNG handle is closed\";
+    } else {
+        U16 = Handle.getShort();
+        IsOk = bool.YES;
+        ErrorMsg = \"\";
+    }
  ").

  %---------------------------------------------------------------------------%
@@ -489,11 +613,15 @@ generate_uint32(Handle, U32, !IO) :-
          _IO0::di, _IO::uo),
      [will_not_call_mercury, promise_pure, not_thread_safe],
  "
-    byte[] bytes = new byte[4];
-    Handle.GetBytes(bytes);
-    U32 = (uint) (bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0]);
-    IsOk = mr_bool.YES;
-    ErrorMsg = \"\";
+    if (Handle.isClosed()) {
+        U32 = 0;
+        IsOk = mr_bool.NO;
+        ErrorMsg = \"system RNG handle is closed\";
+    } else {
+        U32 = Handle.getUInt();
+        IsOk = mr_bool.YES;
+        ErrorMsg = \"\";
+    }
  ").

  :- pragma foreign_proc("Java",
@@ -501,9 +629,15 @@ generate_uint32(Handle, U32, !IO) :-
          _IO0::di, _IO::uo),
      [will_not_call_mercury, promise_pure, thread_safe],
  "
-    U32 = Handle.nextInt();
-    IsOk = bool.YES;
-    ErrorMsg = \"\";
+    if (Handle.isClosed()) {
+        U32 = 0;
+        IsOk = bool.NO;
+        ErrorMsg = \"system RNG handle is closed\";
+    } else {
+        U32 = Handle.getInt();
+        IsOk = bool.YES;
+        ErrorMsg = \"\";
+    }
  ").

  %---------------------------------------------------------------------------%
@@ -539,19 +673,15 @@ generate_uint64(Handle, U64, !IO) :-
          _IO0::di, _IO::uo),
      [will_not_call_mercury, promise_pure, not_thread_safe],
  "
-    byte[] bytes = new byte[8];
-    Handle.GetBytes(bytes);
-    U64 = (ulong) (
-        (ulong) bytes[7] << 56 |
-        (ulong) bytes[6] << 48 |
-        (ulong) bytes[5] << 40 |
-        (ulong) bytes[4] << 32 |
-        (ulong) bytes[3] << 24 |
-        (ulong) bytes[2] << 16 |
-        (ulong) bytes[1] << 8  |
-        (ulong) bytes[0]);
-    IsOk = mr_bool.YES;
-    ErrorMsg = \"\";
+    if (Handle.isClosed()) {
+        U64 = 0;
+        IsOk = mr_bool.NO;
+        ErrorMsg = \"system RNG handle is closed\";
+    } else {
+        U64 = Handle.getULong();
+        IsOk = mr_bool.YES;
+        ErrorMsg = \"\";
+    }
  ").

  :- pragma foreign_proc("Java",
@@ -559,9 +689,15 @@ generate_uint64(Handle, U64, !IO) :-
          _IO0::di, _IO::uo),
      [will_not_call_mercury, promise_pure, thread_safe],
  "
-    U64 = Handle.nextLong();
-    IsOk = bool.YES;
-    ErrorMsg = \"\";
+    if (Handle.isClosed()) {
+        U64 = 0;
+        IsOk = bool.NO;
+        ErrorMsg = \"system RNG handle is closed\";
+    } else {
+        U64 = Handle.getLong();
+        IsOk = bool.YES;
+        ErrorMsg = \"\";
+    }
  ").

  %---------------------------------------------------------------------------%


More information about the reviews mailing list