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.math.BigInteger;
18 import java.nio.ByteBuffer;
19 import java.util.Set;
20
21 import org.htmlunit.corejs.javascript.Scriptable;
22 import org.htmlunit.corejs.javascript.ScriptableObject;
23 import org.htmlunit.corejs.javascript.VarScope;
24 import org.htmlunit.corejs.javascript.typedarrays.NativeArrayBuffer;
25 import org.htmlunit.corejs.javascript.typedarrays.NativeArrayBufferView;
26 import org.htmlunit.corejs.javascript.typedarrays.NativeUint8Array;
27 import org.htmlunit.javascript.JavaScriptEngine;
28
29
30
31
32
33
34
35
36 final class RsaHashedKeyAlgorithm {
37
38 static final Set<String> SUPPORTED_NAMES = Set.of("RSASSA-PKCS1-v1_5", "RSA-PSS", "RSA-OAEP");
39 static final Set<String> SUPPORTED_HASH_ALGORITHMS = Set.of("SHA-1", "SHA-256", "SHA-384", "SHA-512");
40
41 private final String name_;
42 private final int modulusLength_;
43 private final byte[] publicExponent_;
44 private final String hash_;
45
46 RsaHashedKeyAlgorithm(final String name, final int modulusLength,
47 final byte[] publicExponent, final String hash) {
48 if (!SUPPORTED_NAMES.contains(name)) {
49 throw new UnsupportedOperationException("RSA " + name);
50 }
51 name_ = name;
52
53 if (modulusLength <= 0) {
54 throw new IllegalArgumentException("Data provided to an operation does not meet requirements");
55 }
56 modulusLength_ = modulusLength;
57
58 if (publicExponent == null || publicExponent.length == 0) {
59 throw new IllegalArgumentException("Data provided to an operation does not meet requirements");
60 }
61 publicExponent_ = publicExponent.clone();
62
63 if (!SUPPORTED_HASH_ALGORITHMS.contains(hash)) {
64 throw new UnsupportedOperationException("RSA hash " + hash);
65 }
66 hash_ = hash;
67 }
68
69
70
71
72
73
74
75 static RsaHashedKeyAlgorithm from(final Scriptable keyGenParams) {
76 final Object nameProp = ScriptableObject.getProperty(keyGenParams, "name");
77 if (!(nameProp instanceof String name)) {
78 throw new IllegalArgumentException("An invalid or illegal string was specified");
79 }
80
81 final Object modulusLengthProp = ScriptableObject.getProperty(keyGenParams, "modulusLength");
82 if (!(modulusLengthProp instanceof Number numModulusLength)) {
83 throw new IllegalArgumentException("An invalid or illegal string was specified");
84 }
85
86 final Object publicExponentProp = ScriptableObject.getProperty(keyGenParams, "publicExponent");
87 final byte[] publicExponent = extractBytes(publicExponentProp);
88 if (publicExponent == null) {
89 throw new IllegalArgumentException("An invalid or illegal string was specified");
90 }
91
92 final Object hashProp = ScriptableObject.getProperty(keyGenParams, "hash");
93 final String hash = SubtleCrypto.resolveAlgorithmName(hashProp);
94
95 return new RsaHashedKeyAlgorithm(name, numModulusLength.intValue(), publicExponent, hash);
96 }
97
98 private static byte[] extractBytes(final Object value) {
99 if (value instanceof NativeArrayBufferView view) {
100 final NativeArrayBuffer buf = view.getBuffer();
101 if (buf != null) {
102 final byte[] result = new byte[view.getByteLength()];
103 final ByteBuffer bb = buf.getByteBuffer();
104 bb.position(view.getByteOffset());
105 bb.get(result, 0, result.length);
106 return result;
107 }
108 }
109 if (value instanceof NativeArrayBuffer buf) {
110 final ByteBuffer bb = buf.getByteBuffer();
111 final byte[] result = new byte[bb.remaining()];
112 bb.get(result);
113 return result;
114 }
115 return null;
116 }
117
118 static boolean isSupported(final String name) {
119 return SUPPORTED_NAMES.contains(name);
120 }
121
122 String getName() {
123 return name_;
124 }
125
126 int getModulusLength() {
127 return modulusLength_;
128 }
129
130 byte[] getPublicExponent() {
131 return publicExponent_.clone();
132 }
133
134
135
136
137
138
139 BigInteger getPublicExponentAsBigInteger() {
140 return new BigInteger(1, publicExponent_);
141 }
142
143 String getHash() {
144 return hash_;
145 }
146
147
148
149
150
151
152 String getJavaHash() {
153 return hash_.replace("-", "");
154 }
155
156
157
158
159
160
161
162
163 Scriptable toScriptableObject(final VarScope scope) {
164 final Scriptable hashObj = JavaScriptEngine.newObject(scope);
165 ScriptableObject.putProperty(hashObj, "name", getHash());
166
167 final NativeUint8Array uint8Array = JavaScriptEngine.newUint8Array(scope, publicExponent_);
168
169 final Scriptable algorithm = JavaScriptEngine.newObject(scope);
170 ScriptableObject.putProperty(algorithm, "name", getName());
171 ScriptableObject.putProperty(algorithm, "hash", hashObj);
172 ScriptableObject.putProperty(algorithm, "modulusLength", getModulusLength());
173 ScriptableObject.putProperty(algorithm, "publicExponent", uint8Array);
174 return algorithm;
175 }
176 }