1
2
3
4
5
6
7
8
9
10
11
12
13
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
32
33
34
35
36
37
38
39 @JsxClass
40 public class Crypto extends HtmlUnitScriptable {
41
42 static final SecureRandom RANDOM = new SecureRandom();
43 private SubtleCrypto subtle_;
44
45
46
47
48 public Crypto() {
49 super();
50 }
51
52
53
54
55 @JsxConstructor
56 public void jsConstructor() {
57 throw JavaScriptEngine.typeErrorIllegalConstructor();
58 }
59
60
61
62
63
64 public Crypto(final Window window) {
65 this();
66 setParentScope(getTopLevelScope(window.getParentScope()));
67 setPrototype(window.getPrototype(Crypto.class));
68 }
69
70
71
72
73
74
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
100
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
116
117
118 @JsxFunction
119 public String randomUUID() {
120
121
122 final byte[] bytes = new byte[16];
123 RANDOM.nextBytes(bytes);
124
125
126 bytes[6] = (byte) (bytes[6] | 0b01000000);
127 bytes[6] = (byte) (bytes[6] & 0b01001111);
128
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 }