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.UUID;
19  
20  import org.htmlunit.corejs.javascript.typedarrays.NativeArrayBufferView;
21  import org.htmlunit.corejs.javascript.typedarrays.NativeBigInt64Array;
22  import org.htmlunit.corejs.javascript.typedarrays.NativeBigUint64Array;
23  import org.htmlunit.corejs.javascript.typedarrays.NativeInt16Array;
24  import org.htmlunit.corejs.javascript.typedarrays.NativeInt32Array;
25  import org.htmlunit.corejs.javascript.typedarrays.NativeInt8Array;
26  import org.htmlunit.corejs.javascript.typedarrays.NativeUint16Array;
27  import org.htmlunit.corejs.javascript.typedarrays.NativeUint32Array;
28  import org.htmlunit.corejs.javascript.typedarrays.NativeUint8Array;
29  import org.htmlunit.corejs.javascript.typedarrays.NativeUint8ClampedArray;
30  import org.htmlunit.javascript.HtmlUnitScriptable;
31  import org.htmlunit.javascript.JavaScriptEngine;
32  import org.htmlunit.javascript.configuration.JsxClass;
33  import org.htmlunit.javascript.configuration.JsxConstructor;
34  import org.htmlunit.javascript.configuration.JsxFunction;
35  import org.htmlunit.javascript.configuration.JsxGetter;
36  import org.htmlunit.javascript.host.Window;
37  import org.htmlunit.javascript.host.dom.DOMException;
38  
39  /**
40   * A JavaScript object for {@code Crypto}.
41   *
42   * @author Ahmed Ashour
43   * @author Marc Guillemot
44   * @author Ronald Brill
45   *
46   * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Crypto">MDN Documentation</a>
47   */
48  @JsxClass
49  public class Crypto extends HtmlUnitScriptable {
50  
51      static final SecureRandom RANDOM = new SecureRandom();
52      private SubtleCrypto subtle_;
53  
54      /**
55       * Creates an instance.
56       */
57      public Crypto() {
58          super();
59      }
60  
61      /**
62       * Creates an instance.
63       */
64      @JsxConstructor
65      public void jsConstructor() {
66          throw JavaScriptEngine.typeErrorIllegalConstructor();
67      }
68  
69      /**
70       * Facility constructor.
71       * @param window the owning window
72       */
73      public Crypto(final Window window) {
74          this();
75          setParentScope(getTopLevelScope(window.getParentScope()));
76          setPrototype(window.getPrototype(Crypto.class));
77      }
78  
79      /**
80       * Fills array with random values.
81       * @param array the array to fill
82       * @return the modified array
83       * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/RandomSource/getRandomValues">MDN Doc</a>
84       */
85      @JsxFunction
86      public NativeArrayBufferView getRandomValues(final Object array) {
87          if (!(array instanceof NativeArrayBufferView view)) {
88              throw JavaScriptEngine.typeError(
89                      "Argument 1 of Crypto.getRandomValues does not implement interface ArrayBufferView.");
90          }
91          if (!isIntegerArray(view)) {
92              throw JavaScriptEngine.asJavaScriptException(
93                      getWindow(),
94                      "Failed to execute 'getRandomValues' on 'Crypto': "
95                              + "The provided ArrayBufferView is of type '" + view.getClassName()
96                              + "', which is not an integer array type.",
97                      DOMException.TYPE_MISMATCH_ERR);
98          }
99  
100         // a view on a detached buffer has a byte length of 0; there is nothing to fill
101         // (getByteLength() still reports the original length and getByteBuffer() is null)
102         if (view.getBuffer().isDetached()) {
103             return view;
104         }
105 
106         if (view.getByteLength() > 65_536) {
107             throw JavaScriptEngine.asJavaScriptException(
108                     getWindow(),
109                     "Error: Failed to execute 'getRandomValues' on 'Crypto': "
110                             + "The ArrayBufferView's byte length "
111                             + "(" + view.getByteLength() + ") exceeds the number of bytes "
112                             + "of entropy available via this API (65536).",
113                     DOMException.QUOTA_EXCEEDED_ERR);
114         }
115 
116         // fill the underlying bytes; writing the elements would convert (e.g. clamp) the values
117         final byte[] bytes = new byte[view.getByteLength()];
118         if (bytes.length > 0) {
119             RANDOM.nextBytes(bytes);
120             view.getBuffer().getByteBuffer().put(view.getByteOffset(), bytes);
121         }
122         return view;
123     }
124 
125     private static boolean isIntegerArray(final NativeArrayBufferView view) {
126         return view instanceof NativeInt8Array
127                 || view instanceof NativeUint8Array
128                 || view instanceof NativeUint8ClampedArray
129                 || view instanceof NativeInt16Array
130                 || view instanceof NativeUint16Array
131                 || view instanceof NativeInt32Array
132                 || view instanceof NativeUint32Array
133                 || view instanceof NativeBigInt64Array
134                 || view instanceof NativeBigUint64Array;
135     }
136 
137     /**
138      * Returns the {@code subtle} property.
139      * @return the {@code subtle} property
140      */
141     @JsxGetter
142     public SubtleCrypto getSubtle() {
143         if (subtle_ != null) {
144             return subtle_;
145         }
146         final SubtleCrypto subtle = new SubtleCrypto();
147         subtle.setParentScope(getParentScope());
148         subtle.setPrototype(getWindow().getPrototype(SubtleCrypto.class));
149         subtle_ = subtle;
150         return subtle_;
151     }
152 
153     /**
154      * Generates a random UUID.
155      * @return a v4 UUID generated using a cryptographically secure random number generator
156      */
157     @JsxFunction
158     public String randomUUID() {
159         // java.util.UUID creates a version 4 (random) UUID using a cryptographically
160         // secure random number generator, formatted with lowercase hex digits
161         return UUID.randomUUID().toString();
162     }
163 }