View Javadoc
1   /*
2    * Copyright (c) 2002-2026 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   * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Crypto">MDN Documentation</a>
38   */
39  @JsxClass
40  public class Crypto extends HtmlUnitScriptable {
41  
42      static final SecureRandom RANDOM = new SecureRandom();
43      private SubtleCrypto subtle_;
44  
45      /**
46       * Creates an instance.
47       */
48      public Crypto() {
49          super();
50      }
51  
52      /**
53       * Creates an instance.
54       */
55      @JsxConstructor
56      public void jsConstructor() {
57          throw JavaScriptEngine.typeErrorIllegalConstructor();
58      }
59  
60      /**
61       * Facility constructor.
62       * @param window the owning window
63       */
64      public Crypto(final Window window) {
65          this();
66          setParentScope(getTopLevelScope(window.getParentScope()));
67          setPrototype(window.getPrototype(Crypto.class));
68      }
69  
70      /**
71       * Fills array with random values.
72       * @param array the array to fill
73       * @return the modified array
74       * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/RandomSource/getRandomValues">MDN Doc</a>
75       */
76      @JsxFunction
77      public NativeTypedArrayView<?> getRandomValues(final NativeTypedArrayView<?> array) {
78          if (array == null) {
79              throw JavaScriptEngine.typeError("Argument 1 of Crypto.getRandomValues is not an object.");
80          }
81          if (array.getByteLength() > 65_536) {
82              throw JavaScriptEngine.asJavaScriptException(
83                      getWindow(),
84                      "Error: Failed to execute 'getRandomValues' on 'Crypto': "
85                              + "The ArrayBufferView's byte length "
86                              + "(" + array.getByteLength() + ") exceeds the number of bytes "
87                              + "of entropy available via this API (65536).",
88                      DOMException.QUOTA_EXCEEDED_ERR);
89          }
90  
91          final int length = array.getByteLength() / array.getBytesPerElement();
92          for (int i = 0; i < length; i++) {
93              array.put(i, array, RANDOM.nextInt());
94          }
95          return array;
96      }
97  
98      /**
99       * Returns the {@code subtle} property.
100      * @return the {@code subtle} property
101      */
102     @JsxGetter
103     public SubtleCrypto getSubtle() {
104         if (subtle_ != null) {
105             return subtle_;
106         }
107         final SubtleCrypto subtle = new SubtleCrypto();
108         subtle.setParentScope(getParentScope());
109         subtle.setPrototype(getWindow().getPrototype(SubtleCrypto.class));
110         subtle_ = subtle;
111         return subtle_;
112     }
113 
114     /**
115      * Generates a random UUID.
116      * @return a v4 UUID generated using a cryptographically secure random number generator
117      */
118     @JsxFunction
119     public String randomUUID() {
120         // Let bytes be a byte sequence of length 16.
121         // Fill bytes with cryptographically secure random bytes.
122         final byte[] bytes = new byte[16];
123         RANDOM.nextBytes(bytes);
124 
125         // Set the 4 most significant bits of bytes[6], which represent the UUID version, to 0100.
126         bytes[6] = (byte) (bytes[6] | 0b01000000);
127         bytes[6] = (byte) (bytes[6] & 0b01001111);
128         // Set the 2 most significant bits of bytes[8], which represent the UUID variant, to 10.
129         bytes[8] = (byte) (bytes[8] | 0b10000000);
130         bytes[8] = (byte) (bytes[8] & 0b10111111);
131 
132         final StringBuilder result = new StringBuilder()
133                                             .append(toHex(bytes[0]))
134                                             .append(toHex(bytes[1]))
135                                             .append(toHex(bytes[2]))
136                                             .append(toHex(bytes[3]))
137                                             .append('-')
138                                             .append(toHex(bytes[4]))
139                                             .append(toHex(bytes[5]))
140                                             .append('-')
141                                             .append(toHex(bytes[6]))
142                                             .append(toHex(bytes[7]))
143                                             .append('-')
144                                             .append(toHex(bytes[8]))
145                                             .append(toHex(bytes[9]))
146                                             .append('-')
147                                             .append(toHex(bytes[10]))
148                                             .append(toHex(bytes[11]))
149                                             .append(toHex(bytes[12]))
150                                             .append(toHex(bytes[13]))
151                                             .append(toHex(bytes[14]))
152                                             .append(toHex(bytes[15]));
153         return result.toString();
154     }
155 
156     private static String toHex(final byte b) {
157         return "%02X ".formatted(b).trim().toLowerCase(Locale.ROOT);
158     }
159 }