View Javadoc
1   /*
2    * Copyright (c) 2002-2025 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.htmlunit.javascript.host.crypto;
16  
17  import java.security.SecureRandom;
18  import java.util.Locale;
19  
20  import org.htmlunit.corejs.javascript.typedarrays.NativeTypedArrayView;
21  import org.htmlunit.javascript.HtmlUnitScriptable;
22  import org.htmlunit.javascript.JavaScriptEngine;
23  import org.htmlunit.javascript.configuration.JsxClass;
24  import org.htmlunit.javascript.configuration.JsxConstructor;
25  import org.htmlunit.javascript.configuration.JsxFunction;
26  import org.htmlunit.javascript.configuration.JsxGetter;
27  import org.htmlunit.javascript.host.Window;
28  import org.htmlunit.javascript.host.dom.DOMException;
29  
30  /**
31   * A JavaScript object for {@code Crypto}.
32   *
33   * @author Ahmed Ashour
34   * @author Marc Guillemot
35   * @author Ronald Brill
36   */
37  @JsxClass
38  public class Crypto extends HtmlUnitScriptable {
39  
40      static final SecureRandom RANDOM = new SecureRandom();
41  
42      /**
43       * Creates an instance.
44       */
45      public Crypto() {
46          super();
47      }
48  
49      /**
50       * Creates an instance.
51       */
52      @JsxConstructor
53      public void jsConstructor() {
54          throw JavaScriptEngine.typeErrorIllegalConstructor();
55      }
56  
57      /**
58       * Facility constructor.
59       * @param window the owning window
60       */
61      public Crypto(final Window window) {
62          this();
63          setParentScope(window);
64          setPrototype(window.getPrototype(Crypto.class));
65      }
66  
67      /**
68       * Fills array with random values.
69       * @param array the array to fill
70       * @return the modified array
71       * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/RandomSource/getRandomValues">MDN Doc</a>
72       */
73      @JsxFunction
74      public NativeTypedArrayView<?> getRandomValues(final NativeTypedArrayView<?> array) {
75          if (array == null) {
76              throw JavaScriptEngine.typeError("Argument 1 of Crypto.getRandomValues is not an object.");
77          }
78          if (array.getByteLength() > 65_536) {
79              throw JavaScriptEngine.asJavaScriptException(
80                      getWindow(),
81                      "Error: Failed to execute 'getRandomValues' on 'Crypto': "
82                              + "The ArrayBufferView's byte length "
83                              + "(" + array.getByteLength() + ") exceeds the number of bytes "
84                              + "of entropy available via this API (65536).",
85                      DOMException.QUOTA_EXCEEDED_ERR);
86          }
87  
88          for (int i = 0; i < array.getByteLength() / array.getBytesPerElement(); i++) {
89              array.put(i, array, RANDOM.nextInt());
90          }
91          return array;
92      }
93  
94      /**
95       * Returns the {@code subtle} property.
96       * @return the {@code stuble} property
97       */
98      @JsxGetter
99      public SubtleCrypto getSubtle() {
100         final SubtleCrypto stuble = new SubtleCrypto();
101         final Window window = getWindow();
102         stuble.setParentScope(window);
103         stuble.setPrototype(window.getPrototype(SubtleCrypto.class));
104         return stuble;
105     }
106 
107     /**
108      * @return a v4 UUID generated using a cryptographically secure random number generator
109      */
110     @JsxFunction
111     public String randomUUID() {
112         // Let bytes be a byte sequence of length 16.
113         // Fill bytes with cryptographically secure random bytes.
114         final byte[] bytes = new byte[16];
115         RANDOM.nextBytes(bytes);
116 
117         // Set the 4 most significant bits of bytes[6], which represent the UUID version, to 0100.
118         bytes[6] = (byte) (bytes[6] | 0b01000000);
119         bytes[6] = (byte) (bytes[6] & 0b01001111);
120         // Set the 2 most significant bits of bytes[8], which represent the UUID variant, to 10.
121         bytes[8] = (byte) (bytes[8] | 0b10000000);
122         bytes[8] = (byte) (bytes[6] & 0b10111111);
123 
124         final StringBuilder result = new StringBuilder()
125                                             .append(toHex(bytes[0]))
126                                             .append(toHex(bytes[1]))
127                                             .append(toHex(bytes[2]))
128                                             .append(toHex(bytes[3]))
129                                             .append('-')
130                                             .append(toHex(bytes[4]))
131                                             .append(toHex(bytes[5]))
132                                             .append('-')
133                                             .append(toHex(bytes[6]))
134                                             .append(toHex(bytes[7]))
135                                             .append('-')
136                                             .append(toHex(bytes[8]))
137                                             .append(toHex(bytes[9]))
138                                             .append('-')
139                                             .append(toHex(bytes[10]))
140                                             .append(toHex(bytes[11]))
141                                             .append(toHex(bytes[12]))
142                                             .append(toHex(bytes[13]))
143                                             .append(toHex(bytes[14]))
144                                             .append(toHex(bytes[15]));
145         return result.toString();
146     }
147 
148     private static String toHex(final byte b) {
149         return String.format("%02X ", b).trim().toLowerCase(Locale.ROOT);
150     }
151 }