4545 */
4646
4747import { sha1 } from './sha1'
48+ import { ByteStringBuffer } from './utils'
49+
50+ // Extended Error interface for PKCS1 specific errors
51+ interface PKCS1Error extends Error {
52+ length ?: number
53+ maxLength ?: number
54+ seedLength ?: number
55+ digestLength ?: number
56+ expectedLength ?: number
57+ }
58+
59+ // Type definitions for key and options
60+ interface RSAKey {
61+ n : {
62+ bitLength : ( ) => number
63+ }
64+ }
65+
66+ interface MessageDigest {
67+ start : ( ) => void
68+ update : ( msg : string , encoding ?: string ) => void
69+ digest : ( ) => ByteStringBuffer
70+ digestLength : number
71+ }
72+
73+ interface PKCS1Options {
74+ label ?: string
75+ seed ?: string
76+ md ?: MessageDigest
77+ mgf1 ?: {
78+ md ?: MessageDigest
79+ }
80+ }
81+
82+ // Utility functions that were previously from forge
83+ function getRandomBytes ( count : number ) : string {
84+ const bytes = new Uint8Array ( count )
85+ crypto . getRandomValues ( bytes )
86+ return Array . from ( bytes ) . map ( b => String . fromCharCode ( b ) ) . join ( '' )
87+ }
88+
89+ function xorBytes ( a : string , b : string , length : number ) : string {
90+ let result = ''
91+ for ( let i = 0 ; i < length ; i ++ ) {
92+ result += String . fromCharCode ( a . charCodeAt ( i ) ^ b . charCodeAt ( i ) )
93+ }
94+ return result
95+ }
96+
4897/**
4998 * Encode the given RSAES-OAEP message (M) using key, with optional label (L)
5099 * and seed.
@@ -63,25 +112,24 @@ import { sha1 } from './sha1'
63112 *
64113 * @return the encoded message bytes.
65114 */
66- export function encode_rsa_oaep ( key : any , message : any , options : any ) : string {
115+ export function encode_rsa_oaep ( key : RSAKey , message : string , options : PKCS1Options | string ) : string {
67116 // parse arguments
68- let label
69- let seed
70- let md
71- let mgf1Md
117+ let label : string | undefined
118+ let seed : string | undefined
119+ let md : MessageDigest | undefined
120+ let mgf1Md : MessageDigest | undefined
72121
73122 // legacy args (label, seed, md)
74123 if ( typeof options === 'string' ) {
75124 label = options
76- seed = arguments [ 3 ] || undefined
77- md = arguments [ 4 ] || undefined
125+ seed = arguments [ 3 ] as string
126+ md = arguments [ 4 ] as MessageDigest
78127 }
79128 else if ( options ) {
80- label = options . label || undefined
81- seed = options . seed || undefined
82- md = options . md || undefined
83-
84- if ( options . mgf1 && options . mgf1 . md ) {
129+ label = options . label
130+ seed = options . seed
131+ md = options . md
132+ if ( options . mgf1 ?. md ) {
85133 mgf1Md = options . mgf1 . md
86134 }
87135 }
@@ -103,7 +151,7 @@ export function encode_rsa_oaep(key: any, message: any, options: any): string {
103151 const keyLength = Math . ceil ( key . n . bitLength ( ) / 8 )
104152 const maxLength = keyLength - 2 * md . digestLength - 2
105153 if ( message . length > maxLength ) {
106- var error = new Error ( 'RSAES-OAEP input message length is too long.' )
154+ const error = new Error ( 'RSAES-OAEP input message length is too long.' ) as PKCS1Error
107155 error . length = message . length
108156 error . maxLength = maxLength
109157 throw error
@@ -124,25 +172,24 @@ export function encode_rsa_oaep(key: any, message: any, options: any): string {
124172 const DB = `${ lHash . getBytes ( ) + PS } \x01${ message } `
125173
126174 if ( ! seed ) {
127- seed = forge . random . getBytes ( md . digestLength )
175+ seed = getRandomBytes ( md . digestLength )
128176 }
129177 else if ( seed . length !== md . digestLength ) {
130- var error = new Error ( 'Invalid RSAES-OAEP seed. The seed length must '
131- + 'match the digest length.' )
178+ const error = new Error ( 'Invalid RSAES-OAEP seed. The seed length must match the digest length.' ) as PKCS1Error
132179 error . seedLength = seed . length
133180 error . digestLength = md . digestLength
134181 throw error
135182 }
136183
137184 const dbMask = rsa_mgf1 ( seed , keyLength - md . digestLength - 1 , mgf1Md )
138- const maskedDB = forge . util . xorBytes ( DB , dbMask , DB . length )
185+ const maskedDB = xorBytes ( DB , dbMask , DB . length )
139186
140187 const seedMask = rsa_mgf1 ( maskedDB , md . digestLength , mgf1Md )
141- const maskedSeed = forge . util . xorBytes ( seed , seedMask , seed . length )
188+ const maskedSeed = xorBytes ( seed , seedMask , seed . length )
142189
143190 // return encoded message
144191 return `\x00${ maskedSeed } ${ maskedDB } `
145- } ;
192+ }
146193
147194/**
148195 * Decode the given RSAES-OAEP encoded message (EM) using key, with optional
@@ -161,20 +208,21 @@ export function encode_rsa_oaep(key: any, message: any, options: any): string {
161208 *
162209 * @return the decoded message bytes.
163210 */
164- pkcs1 . decode_rsa_oaep = function ( key , em , options ) {
211+ export function decode_rsa_oaep ( key : RSAKey , em : string , options : PKCS1Options | string ) : string {
165212 // parse args
166- let label
167- let md
168- let mgf1Md
213+ let label : string | undefined
214+ let md : MessageDigest | undefined
215+ let mgf1Md : MessageDigest | undefined
216+
169217 // legacy args
170218 if ( typeof options === 'string' ) {
171219 label = options
172- md = arguments [ 3 ] || undefined
220+ md = arguments [ 3 ] as MessageDigest
173221 }
174222 else if ( options ) {
175- label = options . label || undefined
176- md = options . md || undefined
177- if ( options . mgf1 && options . mgf1 . md ) {
223+ label = options . label
224+ md = options . md
225+ if ( options . mgf1 ? .md ) {
178226 mgf1Md = options . mgf1 . md
179227 }
180228 }
@@ -183,7 +231,7 @@ pkcs1.decode_rsa_oaep = function (key, em, options) {
183231 const keyLength = Math . ceil ( key . n . bitLength ( ) / 8 )
184232
185233 if ( em . length !== keyLength ) {
186- var error = new Error ( 'RSAES-OAEP encoded message length is invalid.' )
234+ const error = new Error ( 'RSAES-OAEP encoded message length is invalid.' ) as PKCS1Error
187235 error . length = em . length
188236 error . expectedLength = keyLength
189237 throw error
@@ -209,6 +257,7 @@ pkcs1.decode_rsa_oaep = function (key, em, options) {
209257 if ( ! label ) {
210258 label = ''
211259 }
260+
212261 md . update ( label , 'raw' )
213262 const lHash = md . digest ( ) . getBytes ( )
214263
@@ -218,36 +267,31 @@ pkcs1.decode_rsa_oaep = function (key, em, options) {
218267 const maskedDB = em . substring ( 1 + md . digestLength )
219268
220269 const seedMask = rsa_mgf1 ( maskedDB , md . digestLength , mgf1Md )
221- const seed = forge . util . xorBytes ( maskedSeed , seedMask , maskedSeed . length )
270+ const seed = xorBytes ( maskedSeed , seedMask , maskedSeed . length )
222271
223272 const dbMask = rsa_mgf1 ( seed , keyLength - md . digestLength - 1 , mgf1Md )
224- const db = forge . util . xorBytes ( maskedDB , dbMask , maskedDB . length )
273+ const db = xorBytes ( maskedDB , dbMask , maskedDB . length )
225274
226275 const lHashPrime = db . substring ( 0 , md . digestLength )
227276
228277 // constant time check that all values match what is expected
229- var error = ( y !== '\x00' )
278+ let error = 0
279+
280+ // constant time check y is 0
281+ error |= y . charCodeAt ( 0 )
230282
231283 // constant time check lHash vs lHashPrime
232284 for ( let i = 0 ; i < md . digestLength ; ++ i ) {
233- error |= ( lHash . charAt ( i ) !== lHashPrime . charAt ( i ) )
285+ error |= lHash . charCodeAt ( i ) ^ lHashPrime . charCodeAt ( i )
234286 }
235287
236- // "constant time" find the 0x1 byte separating the padding (zeros) from the
237- // message
238- // TODO: It must be possible to do this in a better/smarter way?
288+ // "constant time" find the 0x1 byte separating the padding (zeros) from the message
239289 let in_ps = 1
240290 let index = md . digestLength
241291 for ( let j = md . digestLength ; j < db . length ; j ++ ) {
242292 const code = db . charCodeAt ( j )
243-
244293 const is_0 = ( code & 0x1 ) ^ 0x1
245-
246- // non-zero if not 0 or 1 in the ps section
247- const error_mask = in_ps ? 0xFFFE : 0x0000
248- error |= ( code & error_mask )
249-
250- // latch in_ps to zero after we find 0x1
294+ error |= in_ps & ( code & 0xFFFE )
251295 in_ps = in_ps & is_0
252296 index += in_ps
253297 }
@@ -259,7 +303,10 @@ pkcs1.decode_rsa_oaep = function (key, em, options) {
259303 return db . substring ( index + 1 )
260304}
261305
262- function rsa_mgf1 ( seed , maskLength , hash ) {
306+ /**
307+ * MGF1 using the given hash function to generate a mask of the specified length.
308+ */
309+ function rsa_mgf1 ( seed : string , maskLength : number , hash : MessageDigest ) : string {
263310 // default to SHA-1 message digest
264311 if ( ! hash ) {
265312 hash = sha1 . create ( )
@@ -280,7 +327,13 @@ function rsa_mgf1(seed, maskLength, hash) {
280327 return t . substring ( 0 , maskLength )
281328}
282329
283- export const pkcs1 = {
330+ export interface PKCS1 {
331+ encode_rsa_oaep : ( key : RSAKey , message : string , options : PKCS1Options | string ) => string
332+ decode_rsa_oaep : ( key : RSAKey , em : string , options : PKCS1Options | string ) => string
333+ rsa_mgf1 : ( seed : string , maskLength : number , hash : MessageDigest ) => string
334+ }
335+
336+ export const pkcs1 : PKCS1 = {
284337 encode_rsa_oaep,
285338 decode_rsa_oaep,
286339 rsa_mgf1,
0 commit comments