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.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   * Internal helper representing RSA hashed key algorithm parameters.
31   * Used by {@link SubtleCrypto} for RSA key operations.
32   *
33   * @author Lai Quang Duong
34   * @author Ronald Brill
35   */
36  final class RsaHashedKeyAlgorithm implements KeyAlgorithm {
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          modulusLength_ = modulusLength;
53  
54          if (publicExponent == null) {
55              throw new IllegalArgumentException("Data provided to an operation does not meet requirements");
56          }
57          publicExponent_ = publicExponent.clone();
58  
59          if (!SUPPORTED_HASH_ALGORITHMS.contains(hash)) {
60              throw new UnsupportedOperationException("RSA hash " + hash);
61          }
62          hash_ = hash;
63      }
64  
65      /**
66       * Parses RSA hashed key algorithm parameters from a JS object.
67       *
68       * @param keyGenParams the JS algorithm parameters object
69       * @return the parsed RsaHashedKeyAlgorithm
70       */
71      static RsaHashedKeyAlgorithm from(final Scriptable keyGenParams) {
72          final String name = SubtleCrypto.resolveAlgorithmName(keyGenParams);
73  
74          final int modulusLength = (int) SubtleCrypto.getRequiredNumber(keyGenParams, "modulusLength");
75  
76          final byte[] publicExponent = extractBytes(SubtleCrypto.getRequiredMember(keyGenParams, "publicExponent"));
77          if (publicExponent == null) {
78              throw JavaScriptEngine.typeError("The member 'publicExponent' is not a Uint8Array");
79          }
80  
81          final String hash = SubtleCrypto.resolveAlgorithmName(SubtleCrypto.getRequiredMember(keyGenParams, "hash"));
82  
83          return new RsaHashedKeyAlgorithm(name, modulusLength, publicExponent, hash);
84      }
85  
86      private static byte[] extractBytes(final Object value) {
87          if (value instanceof NativeArrayBuffer || value instanceof NativeArrayBufferView) {
88              final ByteBuffer bb = SubtleCrypto.asByteBuffer(value);
89              final byte[] result = new byte[bb.remaining()];
90              bb.get(result);
91              return result;
92          }
93          return null;
94      }
95  
96      static boolean isSupported(final String name) {
97          return SUPPORTED_NAMES.contains(name);
98      }
99  
100     @Override
101     public String getName() {
102         return name_;
103     }
104 
105     int getModulusLength() {
106         return modulusLength_;
107     }
108 
109     byte[] getPublicExponent() {
110         return publicExponent_.clone();
111     }
112 
113     /**
114      * Returns the public exponent as a BigInteger.
115      *
116      * @return the public exponent as a BigInteger
117      */
118     BigInteger getPublicExponentAsBigInteger() {
119         return new BigInteger(1, publicExponent_);
120     }
121 
122     String getHash() {
123         return hash_;
124     }
125 
126     /**
127      * Returns the Java hash algorithm name (e.g. "SHA256" from "SHA-256").
128      *
129      * @return the Java hash algorithm name (e.g. "SHA256" from "SHA-256")
130      */
131     String getJavaHash() {
132         return hash_.replace("-", "");
133     }
134 
135     /**
136      * Converts to a JS object matching the {@code RsaHashedKeyAlgorithm} dictionary:
137      * {@code {name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: Uint8Array, hash: {name: "SHA-256"}}}.
138      *
139      * @param scope the JS scope for prototype/parent setup
140      * @return the JS algorithm object
141      */
142     @Override
143     public Scriptable toScriptableObject(final VarScope scope) {
144         final Scriptable hashObj = JavaScriptEngine.newObject(scope);
145         ScriptableObject.putProperty(hashObj, "name", getHash());
146 
147         final NativeUint8Array uint8Array = JavaScriptEngine.newUint8Array(scope, publicExponent_);
148 
149         final Scriptable algorithm = JavaScriptEngine.newObject(scope);
150         ScriptableObject.putProperty(algorithm, "name", getName());
151         ScriptableObject.putProperty(algorithm, "hash", hashObj);
152         ScriptableObject.putProperty(algorithm, "modulusLength", getModulusLength());
153         ScriptableObject.putProperty(algorithm, "publicExponent", uint8Array);
154         return algorithm;
155     }
156 }