char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
dbb5c40
main
1import { 2Array , 3ArrayBuffer , 4ArrayBufferPrototypeGetResizable as resizable , 5ArrayFrom , 6getter , 7Object , 8ObjectCreate , 9ObjectDefineProperty as define , 10ObjectFreeze as freeze , 11ReflectApply as apply , 12Set , 13SetPrototypeHas as has , 14String , 15SymbolIterator , 16TypedArrayPrototypeGetBuffer as typedBuffer , 17TypedArrayPrototypeGetByteLength as byteLength , 18TypedArrayPrototypeGetByteOffset as byteOffset , 19TypedArrayPrototypeGetTag as tag , 20TypeError , 21Uint8Array , 22Uint8ArrayPrototypeSet as setBytes , 23uncurryThis , 24WeakMap , 25WeakMapPrototypeSet as set , 26} from "ext:core/primordials" ; 27import { 28op_crypto_derive_key as deriveKeyBytes , 29op_crypto_digest as digestBytes , 30op_crypto_key_clone as cloneKey , 31op_crypto_key_deserialize as deserializeKey , 32op_crypto_key_dispose as disposeKey , 33op_crypto_key_export as exportKeyBytes , 34op_crypto_key_export_jwk as exportKeyJwk , 35op_crypto_key_generate as generateKeyBytes , 36op_crypto_key_import as importKeyBytes , 37op_crypto_key_import_asymmetric as importAsymmetric , 38op_crypto_key_import_jwk as importJwk , 39op_crypto_key_pair_generate as generateKeyPairBytes , 40op_crypto_key_serialize as serializeKey , 41op_crypto_random as randomBytes , 42op_crypto_run as runCrypto , 43op_crypto_unwrap_key as unwrapKeyBytes , 44op_crypto_uuid as uuid , 45op_crypto_wrap_key as wrapKeyBytes , 46} from "ext:core/ops" ; 47import { installInterface } from "ext:web/interface.js" ; 48import { createException } from "ext:web/events.js" ; 49import { array , dictionary , required , state , string } from "ext:web/idl.js" ; 50import { registerSerializable , registerWire } from "ext:web/serialization.js" ; 51import type { 52CryptoBuffer , 53CryptoKeyFormat , 54CryptoKeyType , 55CryptoKeyUsage , 56CryptoOperation , 57CryptoView , 58} from "./types.js" ; 59type KeyMeta = { 60name :string ; 61length ?:number ; 62hash ?:string ; 63usages :readonly string []; 64publicUsages ?:readonly string []; 65extractable :boolean ; 66type :CryptoKeyType ; 67curve ?:string ; 68exponent ?:CryptoBuffer ; 69}; 70type KeyState = { 71id :number ; 72meta :KeyMeta & { publicUsages :readonly string [] }; 73algorithm :Record < string , unknown >; 74}; 75type AlgorithmRecord = Record < string , unknown >; 76type RunParams = { 77hash ?:string ; 78iv ?:CryptoBuffer ; 79counter ?:CryptoBuffer ; 80additionalData ?:CryptoBuffer ; 81tagLength ?:number ; 82iterations ?:number ; 83salt ?:CryptoBuffer ; 84info ?:CryptoBuffer ; 85peerId ?:number ; 86}; 87type CryptoStates = { subtle :SubtleCrypto }; 88const cryptoStates = new WeakMap < object , CryptoStates >(), 89subtleStates = new WeakMap < object , true >(), 90keyStates = new WeakMap < object , KeyState >(); 91const charCodeAt = uncurryThis ( String . prototype . charCodeAt ); 92const fromCharCode = String . fromCharCode ; 93const ceil = Math . ceil ; 94const subarray = uncurryThis ( Uint8Array . prototype . subarray ); 95const registerFinalizer = uncurryThis ( FinalizationRegistry . prototype . register ); 96type Secret = { readonly __secret :unique symbol }; 97const secret = ObjectCreate ( null ) as Secret ; 98function exception ( message :string , name :string ) :Error { 99return createException ({ message, name}); 100} 101const isView = ArrayBuffer . isView ; 102const getArrayBufferLength = getter ( ArrayBuffer . prototype , "byteLength" ); 103const dataViewBuffer = getter ( DataView . prototype , "buffer" ); 104const dataViewOffset = getter ( DataView . prototype , "byteOffset" ); 105const dataViewLength = getter ( DataView . prototype , "byteLength" ); 106const arrayPush :< T > ( array :T [], ...items :T []) => number = uncurryThis ( 107Array . prototype . push , 108); 109const arrayIncludes :< T > ( 110array :readonly T [], 111value :unknown , 112fromIndex ?:number , 113) => boolean = uncurryThis ( Array . prototype . includes ); 114function arraySlice < T > ( value :readonly T []) :T [] { 115const copy :T [] = array (); 116for ( let i = 0 ; i < value . length ; i ++ ) copy [ i ] = value [ i ]; 117return copy ; 118} 119const arraySome :< T > ( 120array :readonly T [], 121callback :( value :T , index :number , array :readonly T []) => unknown , 122) => boolean = uncurryThis ( Array . prototype . some ); 123const arrayFilter :< T > ( 124array :readonly T [], 125callback :( value :T , index :number , array :readonly T []) => unknown , 126) => T [] = uncurryThis ( Array . prototype . filter ); 127const stringSlice = uncurryThis ( String . prototype . slice ); 128const stringIndexOf = uncurryThis ( String . prototype . indexOf ); 129const stringIncludes = uncurryThis ( String . prototype . includes ); 130const startsWith = uncurryThis ( String . prototype . startsWith ); 131const Number = globalThis . Number ; 132const numberIsInteger = Number . isInteger , 133numberIsFinite = Number . isFinite , 134trunc = Math . trunc ; 135const Boolean = globalThis . Boolean ; 136const integerTags = new Set ([ 137"Int8Array" , 138"Uint8Array" , 139"Uint8ClampedArray" , 140"Int16Array" , 141"Uint16Array" , 142"Int32Array" , 143"Uint32Array" , 144"BigInt64Array" , 145"BigUint64Array" , 146]); 147const digestNames = new Set ([ "SHA-1" , "SHA-256" , "SHA-384" , "SHA-512" ]); 148const aesNames = new Set ([ "AES-CBC" , "AES-CTR" , "AES-GCM" , "AES-KW" ]); 149const asymmetricNames = new Set ([ 150"RSA-OAEP" , 151"RSA-PSS" , 152"RSASSA-PKCS1-V1_5" , 153"ECDSA" , 154"ECDH" , 155"ED25519" , 156"X25519" , 157]); 158const keyUsages = new Set ([ 159"encrypt" , 160"decrypt" , 161"sign" , 162"verify" , 163"deriveKey" , 164"deriveBits" , 165"wrapKey" , 166"unwrapKey" , 167]); 168const assign = Object . assign ; 169const empty = new Uint8Array ( 0 ); 170const own = ( value :ArrayLike < number >) :CryptoBuffer => new Uint8Array ( value ); 171function peerKeyId ( algorithm :AlgorithmRecord , baseKey :KeyState ) :number { 172const publicKey = algorithm . public ; 173if ( publicKey === undefined ) { 174throw new TypeError ( "ECDH public key is required" ); 175} 176const peer = keyState ( publicKey ); 177if ( 178peer . meta . type !== "public" || peer . meta . name !== baseKey . meta . name || 179peer . meta . curve !== baseKey . meta . curve 180) { 181throw exception ( 182"ECDH public key does not match the base key" , 183"InvalidAccessError" , 184); 185} 186return peer . id ; 187} 188const keyFinalizer = new FinalizationRegistry < number >(( id ) => disposeKey ( id )); 189 190function sourceBytes ( value :unknown ) :CryptoBuffer { 191if ( isView ( value )) { 192let buffer ; 193try { 194buffer = dataViewBuffer ( value ); 195} catch {} 196try { 197if ( buffer !== undefined ) { 198if ( getArrayBufferLength ( buffer ) === 0 ) return own ( empty ); 199return own ( 200new Uint8Array ( buffer , dataViewOffset ( value ), dataViewLength ( value )), 201); 202} 203getArrayBufferLength ( typedBuffer ( value )); 204const length = byteLength ( value ); 205if ( length === 0 ) return own ( empty ); 206return own ( new Uint8Array ( typedBuffer ( value ), byteOffset ( value ), length )); 207} catch { 208throw new TypeError ( "Expected an attached BufferSource" ); 209} 210} 211try { 212const length = getArrayBufferLength ( value ); 213return length === 0 214 ?own ( empty ) 215 :own ( new Uint8Array ( value as ArrayBuffer , 0 , length )); 216} catch { 217throw new TypeError ( "Expected a non-shared BufferSource" ); 218} 219} 220function algorithm ( value :unknown ) :string { 221let name ; 222if ( 223value !== null && ( typeof value === "object" || typeof value === "function" ) 224) { 225name = dictionary ( value ). name ; 226if ( name === undefined ) throw new TypeError ( "Algorithm.name is required" ); 227} else name = value ; 228const input = string ( name ); 229let result = "" ; 230for ( let i = 0 ; i < input . length ; i ++ ) { 231const code = charCodeAt ( input , i ); 232result += fromCharCode ( code >= 97 && code <= 122 ?code - 32 :code ); 233} 234return result ; 235} 236function usageList ( value :unknown , name :string ) :CryptoKeyUsage [] { 237if ( 238value === null || value === undefined || 239typeof ( value as { [ SymbolIterator ] ?:unknown })[ SymbolIterator ] !== 240"function" 241) throw new TypeError ( "Expected an iterable of key usages" ); 242const sequence = ArrayFrom ( value as Iterable < unknown >); 243const usages = array (); 244for ( let i = 0 ; i < sequence . length ; i ++ ) { 245const usage = string ( sequence [ i ]); 246if ( ! has ( keyUsages , usage )) throw new TypeError ( "Invalid key usage" ); 247if ( ! arrayIncludes ( usages , usage )) arrayPush ( usages , usage ); 248} 249const allowed = name === "HMAC" || name === "ECDSA" || name === "ED25519" || 250name === "RSA-PSS" || name === "RSASSA-PKCS1-V1_5" 251 ?[ "sign" , "verify" ] 252 :name === "PBKDF2" || name === "HKDF" || name === "ECDH" || 253name === "X25519" 254 ?[ "deriveBits" , "deriveKey" ] 255 :name === "RSA-OAEP" 256 ?[ "encrypt" , "decrypt" , "wrapKey" , "unwrapKey" ] 257 :name === "AES-KW" 258 ?[ "wrapKey" , "unwrapKey" ] 259 :[ "encrypt" , "decrypt" , "wrapKey" , "unwrapKey" ]; 260if ( arraySome ( usages , ( usage ) => ! arrayIncludes ( allowed , usage ))) { 261throw exception ( 262"The requested key usages are not valid for this algorithm" , 263"SyntaxError" , 264); 265} 266return usages as CryptoKeyUsage []; 267} 268function hashName ( value :unknown ) :string { 269const name = algorithm ( value ); 270if ( ! has ( digestNames , name )) { 271throw exception ( "Unsupported hash algorithm" , "NotSupportedError" ); 272} 273return name ; 274} 275function hmacLength ( hash :string ) :number { 276return hash === "SHA-384" || hash === "SHA-512" ?1024 :512 ; 277} 278function metadata ( 279name :string , 280length :number | undefined , 281hash :string | undefined , 282usages :readonly string [], 283extractable :boolean , 284type :CryptoKeyType = "secret" , 285curve ?:string , 286exponent ?:CryptoBuffer | null , 287) :KeyMeta { 288return { 289 name, 290 length, 291 hash, 292usages :freeze ( arraySlice ( usages )), 293 extractable, 294 type, 295 curve, 296exponent :exponent ?own ( exponent ) :undefined , 297}; 298} 299function makeKey ( id :number , meta :KeyMeta ) :CryptoKey { 300meta . usages = freeze ( arraySlice ( meta . usages )); 301meta . publicUsages = freeze ( ArrayFrom ( meta . usages )); 302const value = ObjectCreate ( CryptoKey . prototype ); 303const internal = { 304 id, 305meta :meta as KeyState [ "meta" ], 306algorithm :freshAlgorithm ( meta ), 307}; 308set ( keyStates , value , internal ); 309registerFinalizer ( keyFinalizer , value , id ); 310return value ; 311} 312registerSerializable ( keyStates , ( internal ) => 313makeKey ( cloneKey ( internal . id ), { 314name :internal . meta . name , 315length :internal . meta ! . length , 316hash :internal . meta . hash , 317curve :internal . meta . curve , 318exponent :internal . meta . exponent , 319usages :arraySlice ( internal . meta . usages ), 320extractable :internal . meta . extractable , 321type :internal . meta . type , 322})); 323registerWire ({ 324name :"CryptoKey" , 325states :keyStates , 326encode :( 327internal :KeyState , 328) :[ number , KeyMeta ] => [ serializeKey ( internal . id ), internal . meta ], 329decode :( packet :[ number , KeyMeta ]) => 330makeKey ( deserializeKey ( packet [ 0 ]), packet [ 1 ]), 331}); 332function keyState ( value :unknown ) :KeyState { 333return state ( keyStates , value ); 334} 335function freshAlgorithm ( meta :KeyMeta ) :Record < string , unknown > { 336const result :Record < string , unknown > = ObjectCreate ( null ); 337result . name = meta . name === "ED25519" 338 ?"Ed25519" 339 :meta . name === "RSASSA-PKCS1-V1_5" 340 ?"RSASSA-PKCS1-v1_5" 341 :meta . name ; 342if ( meta ! . length !== undefined ) { 343if ( 344meta . name === "RSA-OAEP" || meta . name === "RSA-PSS" || 345meta . name === "RSASSA-PKCS1-V1_5" 346) result . modulusLength = meta ! . length ; 347else if ( has ( aesNames , meta . name ) || meta . name === "HMAC" ) { 348result . length = meta ! . length ; 349} 350} 351if ( meta . curve !== undefined ) result . namedCurve = meta . curve ; 352if ( meta . exponent !== undefined ) result . publicExponent = own ( meta . exponent ); 353if ( meta . hash !== undefined ) { 354const hash :{ name :string } = ObjectCreate ( null ); 355hash . name = meta . hash ; 356result . hash = hash ; 357} 358return result ; 359} 360function operationError ( error :unknown , fallback :string ) :Error { 361const message = ( error as { message ?:string } | null | undefined )?. message ?? 362"The cryptographic operation failed" ; 363const codes = [ 364"NotSupportedError" , 365"InvalidAccessError" , 366"DataError" , 367"SyntaxError" , 368"OperationError" , 369]; 370for ( let i = 0 ; i < codes . length ; i ++ ) { 371const code = codes [ i ]; 372if ( startsWith ( message , ` ${ code } : ` )) { 373return exception ( stringSlice ( message , code . length + 2 ), code ); 374} 375} 376const name = 377stringIncludes ( message , "JWK" ) || message === "Invalid CryptoKey length" 378 ?"DataError" 379 :stringIncludes ( message , "usage" ) && 380! stringIncludes ( message , "does not permit" ) || 381stringIncludes ( message , "must be non-extractable" ) || 382message === "Invalid asymmetric key" 383 ?"SyntaxError" 384 :stringIncludes ( message , "does not permit" ) || 385stringIncludes ( message , "not extractable" ) 386 ?"InvalidAccessError" 387 :fallback ; 388return exception ( message , name ); 389} 390async function settled < T > ( 391promise :Promise < T >, 392fallback = "OperationError" , 393) :Promise < T > { 394try { 395return await promise ; 396} catch ( error ) { 397throw operationError ( error , fallback ); 398} 399} 400async function settledBytes ( 401promise :Promise < CryptoBuffer >, 402) :Promise < ArrayBuffer > { 403return typedBuffer ( await settled ( promise )) as ArrayBuffer ; 404} 405function emptyRun ( 406operation :CryptoOperation , 407id :number , 408input :CryptoBuffer = empty , 409params :RunParams | undefined = undefined , 410) { 411params = assign ( ObjectCreate ( null ), params ) as RunParams ; 412return runCrypto ([ 413operation , 414id , 415params . hash ?? "" , 416params . iv ?? empty , 417params . counter ?? empty , 418params . additionalData ?? empty , 419params . tagLength ?? 128 , 420params . iterations ?? 0 , 421params . salt ?? empty , 422params . info ?? empty , 423input , 424params . peerId ?? 0 , 425]); 426} 427function pssSaltLength ( value :unknown ) :number { 428const number = + ( value as number ); 429if ( ! numberIsFinite ( number )) { 430throw new TypeError ( "Invalid RSA-PSS saltLength" ); 431} 432const integer = trunc ( number ); 433if ( integer < 0 || integer > 0xffffffff ) { 434throw new TypeError ( "Invalid RSA-PSS saltLength" ); 435} 436return integer ; 437} 438function algorithmParams ( 439value :unknown , 440forKey = false , 441) :AlgorithmRecord & { 442dictionary :AlgorithmRecord | undefined ; 443name :string ; 444length ?:number ; 445hash ?:string ; 446exponent ?:CryptoBuffer ; 447saltLength ?:number ; 448curve ?:string ; 449} { 450const dictionaryValue = typeof value === "string" 451 ?undefined 452 :dictionary ( value ) as AlgorithmRecord ; 453const name = algorithm ( value ); 454const result :AlgorithmRecord & { 455dictionary :AlgorithmRecord | undefined ; 456name :string ; 457length ?:number ; 458hash ?:string ; 459exponent ?:CryptoBuffer ; 460saltLength ?:number ; 461curve ?:string ; 462} = { dictionary :dictionaryValue , name}; 463if ( startsWith ( name , "AES-" )) { 464if ( forKey ) result . length = dictionaryValue ?. length as number | undefined ; 465} else if ( name === "HMAC" ) { 466if ( forKey ) { 467const hash = dictionaryValue ?. hash ; 468result . hash = hash === undefined ?undefined :hashName ( hash ); 469result . length = dictionaryValue ?. length as number | undefined ; 470} 471} else if ( name === "PBKDF2" || name === "HKDF" ) { 472} else if ( 473name === "RSA-OAEP" || name === "RSA-PSS" || name === "RSASSA-PKCS1-V1_5" 474) { 475if ( forKey ) { 476const hash = dictionaryValue ?. hash ; 477result . hash = hash === undefined ?undefined :hashName ( hash ); 478result . length = dictionaryValue ?. modulusLength as number | undefined ; 479const exponent = dictionaryValue ?. publicExponent ; 480result . exponent = exponent === undefined 481 ?undefined 482 :sourceBytes ( exponent ); 483if ( ! result . hash ) { 484throw new TypeError ( "RSA key algorithms require a hash" ); 485} 486} else if ( name === "RSA-PSS" ) { 487result . saltLength = pssSaltLength ( dictionaryValue ?. saltLength ); 488} 489} else if ( name === "ECDSA" ) { 490if ( forKey ) { 491result . curve = dictionaryValue ?. namedCurve as string | undefined ; 492} else { 493const hash = dictionaryValue ?. hash ; 494result . hash = hash === undefined ?undefined :hashName ( hash ); 495} 496} else if ( name === "ECDH" ) { 497if ( forKey ) { 498result . curve = dictionaryValue ?. namedCurve as string | undefined ; 499} 500} else if ( ! has ( asymmetricNames , name )) { 501throw exception ( 502"The requested algorithm is not supported" , 503"NotSupportedError" , 504); 505} 506return result ; 507} 508 509function checkKeyOperation ( 510key :unknown , 511usage :string , 512algorithmName :string , 513) :KeyState { 514const internal = keyState ( key ); 515if ( 516internal . meta . name !== algorithmName || 517! arrayIncludes ( internal . meta . usages , usage ) 518) { 519throw exception ( 520"The CryptoKey does not permit this operation" , 521"InvalidAccessError" , 522); 523} 524return internal ; 525} 526function normalizeInputAlgorithm ( 527value :unknown , 528) :{ name :string ; params :RunParams } { 529const a :AlgorithmRecord | undefined = typeof value === "string" 530 ?undefined 531 :dictionary ( value ) as AlgorithmRecord , 532name = algorithm ( value ), 533params = ObjectCreate ( null ); 534if ( name === "AES-CBC" ) { 535params . iv = sourceBytes ( a ! . iv ); 536if ( params . iv . length !== 16 ) { 537throw exception ( "AES-CBC requires a 16-byte IV" , "OperationError" ); 538} 539} else if ( name === "AES-CTR" ) { 540params . counter = sourceBytes ( a ! . counter ); 541if ( params . counter . length !== 16 ) { 542throw exception ( "AES-CTR requires a 16-byte counter" , "OperationError" ); 543} 544const bits = Number ( a ! . length ); 545if ( ! numberIsInteger ( bits ) || bits < 1 || bits > 128 ) { 546throw exception ( "Invalid AES-CTR counter length" , "OperationError" ); 547} 548params . tagLength = bits ; 549} else if ( name === "AES-GCM" ) { 550params . iv = sourceBytes ( a ! . iv ); 551if ( ! params . iv . length ) { 552throw exception ( "AES-GCM IV must not be empty" , "OperationError" ); 553} 554params . additionalData = a ! . additionalData === undefined 555 ?empty 556 :sourceBytes ( a ! . additionalData ); 557params . tagLength = a ! . tagLength === undefined ?128 :Number ( a ! . tagLength ); 558if ( ! arrayIncludes ([ 32 , 64 , 96 , 104 , 112 , 120 , 128 ], params . tagLength )) { 559throw exception ( "Invalid AES-GCM tag length" , "OperationError" ); 560} 561} else if ( name === "RSA-OAEP" ) { 562params . iv = a ?. label === undefined ?empty :sourceBytes ( a . label ); 563} else if ( name === "RSA-PSS" ) { 564params . tagLength = pssSaltLength ( a ?. saltLength ); 565} else if ( name !== "AES-KW" ) { 566throw exception ( 567"The requested algorithm is not yet implemented" , 568"NotSupportedError" , 569); 570} 571return { name, params}; 572} 573function keyFormat ( value :unknown ) :CryptoKeyFormat { 574const format = string ( value ); 575if ( ! arrayIncludes ([ "raw" , "jwk" , "spki" , "pkcs8" ], format )) { 576throw new TypeError ( "Invalid key format" ); 577} 578return format as CryptoKeyFormat ; 579} 580const jwkMembers = [ 581"alg" , 582"crv" , 583"d" , 584"dp" , 585"dq" , 586"e" , 587"ext" , 588"k" , 589"key_ops" , 590"kty" , 591"n" , 592"oth" , 593"p" , 594"q" , 595"qi" , 596"use" , 597"x" , 598"y" , 599]; 600function jwkSequence ( 601value :unknown , 602convert :( value :unknown ) => unknown , 603) :unknown [] { 604const iterator = ( value as { [ SymbolIterator ] ?:unknown } | null | undefined ) 605?.[ SymbolIterator ]; 606if ( typeof iterator !== "function" ) { 607throw new TypeError ( "Expected an iterable JWK sequence" ); 608} 609const iterable :Iterable < unknown > = ObjectCreate ( null ); 610iterable [ SymbolIterator ] = () => 611apply ( iterator , value , []) as Iterator < unknown >; 612return arraySlice ( ArrayFrom ( iterable , convert )); 613} 614function jwkDictionary ( value :unknown ) :Record < string , unknown > { 615value = dictionary ( value ); 616const result :Record < string , unknown > = ObjectCreate ( null ); 617for ( let index = 0 ; index < jwkMembers . length ; index ++ ) { 618const name = jwkMembers [ index ] ! , 619member = ( value as Record < string , unknown >)[ name ]; 620if ( member === undefined ) continue ; 621if ( name === "ext" ) result [ name ] = Boolean ( member ); 622else if ( name === "key_ops" ) result [ name ] = jwkSequence ( member , string ); 623else if ( name === "oth" ) { 624result [ name ] = jwkSequence ( member , ( value ) => { 625value = dictionary ( value ); 626const info :Record < string , unknown > = ObjectCreate ( null ), 627members = [ "d" , "r" , "t" ]; 628for ( let index = 0 ; index < members . length ; index ++ ) { 629const name = members [ index ] ! , 630member = ( value as Record < string , unknown >)[ name ]; 631if ( member !== undefined ) info [ name ] = string ( member ); 632} 633return info ; 634}); 635} else result [ name ] = string ( member ); 636} 637return result ; 638} 639 640class CryptoKey { 641constructor () { 642throw new TypeError ( "CryptoKey cannot be constructed directly" ); 643} 644get type () { 645return keyState ( this ). meta . type ; 646} 647get extractable () { 648return keyState ( this ). meta . extractable ; 649} 650get algorithm () { 651return keyState ( this ). algorithm ; 652} 653get usages () { 654return keyState ( this ). meta . publicUsages ; 655} 656} 657 658class SubtleCrypto { 659constructor ( token :typeof secret ) { 660if ( token !== secret ) throw new TypeError ( "Illegal constructor" ); 661set ( subtleStates , this , true ); 662} 663 664async digest ( algorithmIdentifier :unknown , data :unknown ) { 665required ( arguments . length , 2 ); 666state ( subtleStates , this ); 667const name = algorithm ( algorithmIdentifier ); 668if ( ! has ( digestNames , name )) { 669throw exception ( 670"The digest algorithm is not supported" , 671"NotSupportedError" , 672); 673} 674return settledBytes ( digestBytes ([ name , sourceBytes ( data )])); 675} 676 677async generateKey ( 678algorithmIdentifier :unknown , 679extractable :unknown , 680usages :unknown , 681) { 682required ( arguments . length , 3 ); 683state ( subtleStates , this ); 684const p = algorithmParams ( algorithmIdentifier , true ); 685if ( p . name === "HMAC" && ! p . hash ! ) { 686throw new TypeError ( "HMAC requires a hash algorithm" ); 687} 688const keyUsage = usageList ( usages , p . name ), 689isExtractable = Boolean ( extractable ); 690if ( has ( asymmetricNames , p . name )) { 691let length = p . length ?? 0 , 692curve = p . curve ?? "" , 693exponent = p . exponent ?? empty ; 694if ( startsWith ( p . name , "RSA-" ) || startsWith ( p . name , "RSASSA-" )) { 695if ( 696! numberIsInteger ( length ) || length < 512 || length > 8192 || 697! exponent ?. length 698) throw exception ( "Invalid RSA key parameters" , "DataError" ); 699if ( 700exponent [ exponent . length - 1 ] % 2 === 0 || 701( exponent . length === 1 && exponent [ 0 ] < 3 ) 702) throw exception ( "Invalid RSA public exponent" , "DataError" ); 703} else if ( p . name === "ECDSA" || p . name === "ECDH" ) { 704if ( ! arrayIncludes ([ "P-256" , "P-384" , "P-521" ], curve )) { 705throw exception ( "Unsupported elliptic curve" , "NotSupportedError" ); 706} 707length = curve === "P-256" ?256 :curve === "P-384" ?384 :521 ; 708} else { 709length = 256 ; 710} 711const result = await settled ( 712generateKeyPairBytes ([ 713p . name , 714p . hash ?? "" , 715length , 716exponent , 717curve , 718keyUsage , 719isExtractable , 720]), 721); 722const publicId = result [ 0 ], 723privateId = result [ 1 ], 724actualLength = result [ 2 ], 725actualExponent = result [ 3 ]; 726const publicUsages = p . name === "RSA-OAEP" 727 ?arrayFilter ( keyUsage , ( u ) => u === "encrypt" || u === "wrapKey" ) 728 :p . name === "ECDH" || p . name === "X25519" 729 ?[] 730 :arrayFilter ( keyUsage , ( u ) => u === "verify" ); 731const privateUsages = p . name === "RSA-OAEP" 732 ?arrayFilter ( keyUsage , ( u ) => u === "decrypt" || u === "unwrapKey" ) 733 :p . name === "ECDH" || p . name === "X25519" 734 ?keyUsage 735 :arrayFilter ( keyUsage , ( u ) => u === "sign" ); 736return { 737publicKey :makeKey ( 738publicId , 739metadata ( 740p . name , 741actualLength , 742p . hash , 743publicUsages , 744true , 745"public" , 746curve || undefined , 747actualExponent , 748), 749), 750privateKey :makeKey ( 751privateId , 752metadata ( 753p . name , 754actualLength , 755p . hash , 756privateUsages , 757isExtractable , 758"private" , 759curve || undefined , 760actualExponent , 761), 762), 763}; 764} 765if ( ! has ( aesNames , p . name ) && p . name !== "HMAC" ) { 766throw exception ( 767"Key generation is not supported for this algorithm" , 768"NotSupportedError" , 769); 770} 771let length :number | undefined = p . length ; 772if ( p . name === "HMAC" && length === undefined ) length = hmacLength ( p . hash ! ); 773if ( 774! numberIsInteger ( length ) || 775( startsWith ( p . name , "AES-" ) && 776! arrayIncludes ([ 128 , 192 , 256 ], length ! )) || 777( p . name === "HMAC" && ( length ! < 1 || length ! > 65536 )) 778) { 779throw exception ( "Invalid key length" , "OperationError" ); 780} 781const hash = p . hash ?? "" ; 782const id = await settled ( 783generateKeyBytes ([ p . name , length ! , hash , keyUsage , isExtractable ]), 784); 785return makeKey ( 786id , 787metadata ( p . name , length , hash || undefined , keyUsage , isExtractable ), 788); 789} 790 791async importKey ( 792format :unknown , 793keyData :unknown , 794algorithmIdentifier :unknown , 795extractable :unknown , 796usages :unknown , 797) { 798required ( arguments . length , 5 ); 799state ( subtleStates , this ); 800const formatName = keyFormat ( format ); 801const jwk = formatName === "jwk" ?jwkDictionary ( keyData ) :undefined ; 802const p = algorithmParams ( algorithmIdentifier , true ), 803keyUsage = usageList ( usages , p . name ), 804isExtractable = Boolean ( extractable ); 805if ( p . name === "HMAC" && ! p . hash ! ) { 806throw new TypeError ( "HMAC requires a hash algorithm" ); 807} 808if ( has ( asymmetricNames , p . name )) { 809if ( formatName === "pkcs8" && keyUsage . length === 0 ) { 810throw exception ( "Private key usages must not be empty" , "SyntaxError" ); 811} 812if ( formatName === "jwk" ) { 813const result = await settled ( 814importJwk ([ 815jwk ! , 816p . name , 817p . hash ?? "" , 818p . curve ?? "" , 819keyUsage , 820isExtractable , 821p . name === "HMAC" ?p . length ?? 0 :0 , 822]), 823); 824return makeKey ( 825result . id , 826metadata ( 827p . name , 828result . length , 829p . hash , 830keyUsage , 831isExtractable , 832result . type , 833p . curve , 834result . exponent , 835), 836); 837} 838const data = sourceBytes ( keyData ); 839const result = await settled ( 840importAsymmetric ([ 841formatName , 842data , 843p . name , 844p . hash ?? "" , 845p . curve ?? "" , 846keyUsage , 847isExtractable , 848]), 849); 850return makeKey ( 851result . id , 852metadata ( 853p . name , 854result . length , 855p . hash , 856keyUsage , 857isExtractable , 858result . type , 859p . curve , 860result . exponent , 861), 862); 863} 864if ( formatName !== "raw" && formatName !== "jwk" ) { 865throw exception ( "Key format is not supported" , "NotSupportedError" ); 866} 867let raw = formatName === "raw" ?sourceBytes ( keyData ) :undefined ; 868let length = raw ?raw . length * 8 :undefined ; 869if ( raw && p . name === "HMAC" ) { 870length = p . length ?? length ; 871if ( 872! numberIsInteger ( length ) || length ! <= 0 || length ! > raw . length * 8 || 873length ! <= raw . length * 8 - 8 874) { 875throw exception ( "Invalid HMAC key length" , "DataError" ); 876} 877raw = own ( subarray ( raw , 0 , ceil ( length ! / 8 ))); 878if ( length ! % 8 !== 0 ) raw [ raw . length - 1 ] &= 0xff << ( 8 - length ! % 8 ); 879} 880if ( 881raw && startsWith ( p . name , "AES-" ) && 882! arrayIncludes ([ 128 , 192 , 256 ], length ) 883) throw exception ( "Invalid AES key length" , "DataError" ); 884if ( raw && p . name === "HMAC" && ! raw . length ) { 885throw exception ( "Invalid HMAC key" , "DataError" ); 886} 887const hash = p . hash ?? "" ; 888if ( formatName === "raw" ) { 889const id = await settled ( 890importKeyBytes ([ 891p . name , 892raw ! , 893length ?? 0 , 894hash , 895keyUsage , 896isExtractable , 897]), 898); 899return makeKey ( 900id , 901metadata ( p . name , length , hash || undefined , keyUsage , isExtractable ), 902); 903} 904const idAndLength = await settled ( 905importJwk ([ 906jwk ! , 907p . name , 908hash , 909p . curve ?? "" , 910keyUsage , 911isExtractable , 912p . name === "HMAC" ?p . length ?? 0 :0 , 913]), 914); 915const { id, length :jwkLength } = idAndLength ; 916return makeKey ( 917id , 918metadata ( p . name , jwkLength , hash || undefined , keyUsage , isExtractable ), 919); 920} 921 922async exportKey ( format :unknown , key :unknown ) { 923required ( arguments . length , 2 ); 924state ( subtleStates , this ); 925const internal = keyState ( key ), formatName = keyFormat ( format ); 926if ( ! internal . meta . extractable ) { 927throw exception ( "CryptoKey is not extractable" , "InvalidAccessError" ); 928} 929if ( 930formatName === "raw" || formatName === "spki" || formatName === "pkcs8" 931) return settledBytes ( exportKeyBytes ([ internal . id , formatName ])); 932return settled ( exportKeyJwk ( internal . id )); 933} 934 935async encrypt ( algorithmIdentifier :unknown , key :unknown , data :unknown ) { 936required ( arguments . length , 3 ); 937state ( subtleStates , this ); 938const p = normalizeInputAlgorithm ( algorithmIdentifier ), 939k = checkKeyOperation ( key , "encrypt" , p . name ); 940if ( p . name === "AES-KW" ) { 941throw exception ( 942"AES-KW is only available through wrapKey and unwrapKey" , 943"InvalidAccessError" , 944); 945} 946p . params . hash = k . meta . hash ?? "" ; 947return settledBytes ( emptyRun ( "encrypt" , k . id , sourceBytes ( data ), p . params )); 948} 949async decrypt ( algorithmIdentifier :unknown , key :unknown , data :unknown ) { 950required ( arguments . length , 3 ); 951state ( subtleStates , this ); 952const p = normalizeInputAlgorithm ( algorithmIdentifier ), 953k = checkKeyOperation ( key , "decrypt" , p . name ); 954if ( p . name === "AES-KW" ) { 955throw exception ( 956"AES-KW is only available through wrapKey and unwrapKey" , 957"InvalidAccessError" , 958); 959} 960p . params . hash = k . meta . hash ?? "" ; 961return settledBytes ( emptyRun ( "decrypt" , k . id , sourceBytes ( data ), p . params )); 962} 963async sign ( algorithmIdentifier :unknown , key :unknown , data :unknown ) { 964required ( arguments . length , 3 ); 965state ( subtleStates , this ); 966const p = algorithmParams ( algorithmIdentifier ), 967k = checkKeyOperation ( key , "sign" , p . name ); 968if ( 969! arrayIncludes ([ 970"HMAC" , 971"RSA-PSS" , 972"RSASSA-PKCS1-V1_5" , 973"ECDSA" , 974"ED25519" , 975], p . name ) 976) { 977throw exception ( 978"The requested signing algorithm is not supported" , 979"NotSupportedError" , 980); 981} 982if ( p . name === "ECDSA" && ! p . hash ! ) { 983throw new TypeError ( "ECDSA requires a hash" ); 984} 985if ( k . meta . hash && p . hash && p . hash !== k . meta . hash ) { 986throw exception ( 987"Operation hash does not match the key" , 988"InvalidAccessError" , 989); 990} 991return settledBytes ( 992emptyRun ( "sign" , k . id , sourceBytes ( data ), { 993hash :k . meta . hash ?? p . hash ?? "" , 994tagLength :p . name === "RSA-PSS" ?p . saltLength :0 , 995}), 996); 997} 998async verify ( 999algorithmIdentifier :unknown , 1000key :unknown , 1001signature :unknown , 1002data :unknown , 1003) { 1004required ( arguments . length , 4 ); 1005state ( subtleStates , this ); 1006const p = algorithmParams ( algorithmIdentifier ), 1007k = checkKeyOperation ( key , "verify" , p . name ); 1008if ( 1009! arrayIncludes ([ 1010"HMAC" , 1011"RSA-PSS" , 1012"RSASSA-PKCS1-V1_5" , 1013"ECDSA" , 1014"ED25519" , 1015], p . name ) 1016) { 1017throw exception ( 1018"The requested signing algorithm is not supported" , 1019"NotSupportedError" , 1020); 1021} 1022if ( p . name === "ECDSA" && ! p . hash ! ) { 1023throw new TypeError ( "ECDSA requires a hash" ); 1024} 1025if ( k . meta . hash && p . hash && p . hash !== k . meta . hash ) { 1026throw exception ( 1027"Operation hash does not match the key" , 1028"InvalidAccessError" , 1029); 1030} 1031const sig = sourceBytes ( signature ), 1032result = await settled ( 1033emptyRun ( "verify" , k . id , sourceBytes ( data ), { 1034hash :k . meta . hash ?? p . hash ?? "" , 1035tagLength :p . name === "RSA-PSS" ?p . saltLength :0 , 1036additionalData :sig , 1037}), 1038); 1039return result [ 0 ] === 1 ; 1040} 1041async deriveBits ( 1042algorithmIdentifier :unknown , 1043baseKey :unknown , 1044length :unknown = null , 1045) { 1046required ( arguments . length , 2 ); 1047state ( subtleStates , this ); 1048const p = dictionary ( algorithmIdentifier ), 1049name = algorithm ( p ), 1050k = checkKeyOperation ( baseKey , "deriveBits" , name ); 1051if ( name === "ECDH" || name === "X25519" ) { 1052const maximum = ceil ( k . meta ! . length ! / 8 ) * 8 ; 1053const bitLength = length == null ?maximum :Number ( length ); 1054if ( 1055! numberIsInteger ( bitLength ) || bitLength < 0 || 1056bitLength > maximum 1057) throw exception ( "Invalid derived bit length" , "OperationError" ); 1058return settledBytes ( 1059emptyRun ( "deriveBits" , k . id , empty , { 1060peerId :peerKeyId ( p , k ), 1061tagLength :bitLength , 1062}), 1063); 1064} 1065if ( name !== "PBKDF2" && name !== "HKDF" ) { 1066throw exception ( 1067"The requested derivation algorithm is not yet implemented" , 1068"NotSupportedError" , 1069); 1070} 1071const bitLength = Number ( length ); 1072if ( 1073length === null || ! numberIsInteger ( bitLength ) || bitLength < 0 || 1074bitLength % 8 1075) { 1076throw exception ( "Invalid derived bit length" , "OperationError" ); 1077} 1078const params = { 1079hash :hashName ( p . hash ! ), 1080salt :sourceBytes ( p . salt ), 1081info :name === "HKDF" ?sourceBytes ( p . info ) :empty , 1082iterations :name === "PBKDF2" ?Number ( p . iterations ) :0 , 1083tagLength :bitLength , 1084}; 1085if ( 1086name === "PBKDF2" && 1087( ! numberIsInteger ( params . iterations ) || params . iterations <= 0 ) 1088) throw exception ( "Invalid PBKDF2 iteration count" , "OperationError" ); 1089return settledBytes ( emptyRun ( "deriveBits" , k . id , empty , params )); 1090} 1091async deriveKey ( 1092algorithmIdentifier :unknown , 1093baseKey :unknown , 1094derivedKeyType :unknown , 1095extractable :unknown , 1096usages :unknown , 1097) { 1098required ( arguments . length , 5 ); 1099state ( subtleStates , this ); 1100const source = dictionary ( algorithmIdentifier ), 1101sourceName = algorithm ( source ); 1102const base = checkKeyOperation ( baseKey , "deriveKey" , sourceName ); 1103if ( 1104sourceName !== "PBKDF2" && sourceName !== "HKDF" && 1105sourceName !== "ECDH" && sourceName !== "X25519" 1106) { 1107throw exception ( 1108"The requested derivation algorithm is not yet implemented" , 1109"NotSupportedError" , 1110); 1111} 1112const target = algorithmParams ( derivedKeyType , true ); 1113if ( ! has ( aesNames , target . name ) && target . name !== "HMAC" ) { 1114throw exception ( 1115"Derived key algorithm is not supported" , 1116"NotSupportedError" , 1117); 1118} 1119const length = target . length ?? 1120( target . name === "HMAC" ?hmacLength ( target . hash ! ) :undefined ); 1121if ( 1122! numberIsInteger ( length ) || 1123( startsWith ( target . name , "AES-" ) && 1124! arrayIncludes ([ 128 , 192 , 256 ], length ! )) || 1125( target . name === "HMAC" && ( length ! < 1 || length ! > 65536 )) 1126) throw exception ( "Invalid derived key length" , "OperationError" ); 1127const keyUsage = usageList ( usages , target . name ), 1128isExtractable = Boolean ( extractable ); 1129const params = sourceName === "ECDH" || sourceName === "X25519" 1130 ?{ hash :"" , salt :empty , info :empty , iterations :0 } 1131 :{ 1132hash :hashName ( source . hash ), 1133salt :sourceBytes ( source . salt ), 1134info :sourceName === "HKDF" ?sourceBytes ( source . info ) :empty , 1135iterations :sourceName === "PBKDF2" ?Number ( source . iterations ) :0 , 1136}; 1137if ( 1138sourceName === "PBKDF2" && 1139( ! numberIsInteger ( params . iterations ) || params . iterations <= 0 ) 1140) throw exception ( "Invalid PBKDF2 iteration count" , "OperationError" ); 1141const targetHash = target . hash ?? "" ; 1142const peerId = sourceName === "ECDH" || sourceName === "X25519" 1143 ?peerKeyId ( source , base ) 1144 :0 ; 1145const id = await settled ( 1146deriveKeyBytes ([ 1147base . id , 1148params . hash , 1149params . salt , 1150params . iterations , 1151params . info , 1152length ! , 1153target . name , 1154targetHash , 1155keyUsage , 1156isExtractable , 1157peerId , 1158]), 1159); 1160return makeKey ( 1161id , 1162metadata ( 1163target . name , 1164length , 1165targetHash || undefined , 1166keyUsage , 1167isExtractable , 1168), 1169); 1170} 1171async wrapKey ( 1172format :unknown , 1173key :unknown , 1174wrappingKey :unknown , 1175wrapAlgorithm :unknown , 1176) { 1177required ( arguments . length , 4 ); 1178state ( subtleStates , this ); 1179if ( ! keyState ( key ). meta . extractable ) { 1180throw exception ( "CryptoKey is not extractable" , "InvalidAccessError" ); 1181} 1182const p = normalizeInputAlgorithm ( wrapAlgorithm ), 1183wrapping = checkKeyOperation ( wrappingKey , "wrapKey" , p . name ); 1184if ( 1185! arrayIncludes ( 1186[ "AES-KW" , "AES-CBC" , "AES-CTR" , "AES-GCM" , "RSA-OAEP" ], 1187p . name , 1188) 1189) { 1190throw exception ( 1191"The wrapping algorithm is not supported" , 1192"NotSupportedError" , 1193); 1194} 1195p . params . hash = wrapping . meta . hash ?? "" ; 1196const formatName = keyFormat ( format ); 1197return settledBytes ( 1198wrapKeyBytes ([ 1199wrapping . id , 1200keyState ( key ). id , 1201formatName , 1202p . name , 1203p . params . hash , 1204p . params . iv ?? empty , 1205p . params . counter ?? empty , 1206p . params . additionalData ?? empty , 1207p . params . tagLength ?? 128 , 1208]), 1209); 1210} 1211async unwrapKey ( 1212format :unknown , 1213wrappedKey :unknown , 1214unwrappingKey :unknown , 1215unwrapAlgorithm :unknown , 1216unwrappedKeyAlgorithm :unknown , 1217extractable :unknown , 1218usages :unknown , 1219) { 1220required ( arguments . length , 7 ); 1221state ( subtleStates , this ); 1222const p = normalizeInputAlgorithm ( unwrapAlgorithm ), 1223wrapping = checkKeyOperation ( unwrappingKey , "unwrapKey" , p . name ); 1224if ( 1225! arrayIncludes ( 1226[ "AES-KW" , "AES-CBC" , "AES-CTR" , "AES-GCM" , "RSA-OAEP" ], 1227p . name , 1228) 1229) { 1230throw exception ( 1231"The unwrapping algorithm is not supported" , 1232"NotSupportedError" , 1233); 1234} 1235p . params . hash = wrapping . meta . hash ?? "" ; 1236const formatName = keyFormat ( format ); 1237const target = algorithmParams ( unwrappedKeyAlgorithm , true ), 1238keyUsage = usageList ( usages , target . name ); 1239const result = await settled ( 1240unwrapKeyBytes ([ 1241wrapping . id , 1242formatName , 1243sourceBytes ( wrappedKey ), 1244target . name , 1245target . hash ?? "" , 1246target . curve ?? "" , 1247keyUsage , 1248Boolean ( extractable ), 1249p . params . iv ?? empty , 1250p . params . counter ?? empty , 1251p . params . additionalData ?? empty , 1252p . params . tagLength ?? 128 , 1253target . length ?? 0 , 1254]), 1255); 1256return makeKey ( 1257result . id , 1258metadata ( 1259target . name , 1260result . length , 1261target . hash , 1262keyUsage , 1263Boolean ( extractable ), 1264result . type , 1265target . curve , 1266result . exponent , 1267), 1268); 1269} 1270} 1271 1272class Crypto { 1273constructor ( token :typeof secret ) { 1274if ( token !== secret ) throw new TypeError ( "Illegal constructor" ); 1275set ( cryptoStates , this , { subtle :new SubtleCrypto ( secret ) }); 1276} 1277get subtle () { 1278return state ( cryptoStates , this ). subtle ; 1279} 1280getRandomValues < T extends CryptoView > ( array :T ) :T { 1281required ( arguments . length , 1 ); 1282state ( cryptoStates , this ); 1283if ( ! isView ( array )) throw new TypeError ( "Expected an ArrayBufferView" ); 1284let source ; 1285try { 1286source = typedBuffer ( array ); 1287} catch { 1288source = dataViewBuffer ( array ); 1289} 1290try { 1291getArrayBufferLength ( source ); 1292} catch { 1293throw new TypeError ( "Shared buffers are not allowed" ); 1294} 1295if ( ! has ( integerTags , tag ( array ))) { 1296throw exception ( "Expected an integer TypedArray" , "TypeMismatchError" ); 1297} 1298const length = byteLength ( array ); 1299if ( length > 65536 ) { 1300throw exception ( 1301"The requested length exceeds 65536 bytes" , 1302"QuotaExceededError" , 1303); 1304} 1305if ( length === 0 ) return array ; 1306if ( resizable ( source )) { 1307const generated = new Uint8Array ( length ); 1308randomBytes ( generated ); 1309setBytes ( new Uint8Array ( source , byteOffset ( array ), length ), generated ); 1310} else { 1311randomBytes ( array ); 1312} 1313return array ; 1314} 1315randomUUID () { 1316state ( cryptoStates , this ); 1317return uuid ( null ); 1318} 1319} 1320 1321export type { Crypto , CryptoKey , SubtleCrypto }; 1322 1323installInterface ( "CryptoKey" , CryptoKey ); 1324installInterface ( "SubtleCrypto" , SubtleCrypto ); 1325installInterface ( "Crypto" , Crypto ); 1326const crypto = new Crypto ( secret ); 1327define ( globalThis , "crypto" , { 1328value :crypto , 1329writable :true , 1330configurable :true , 1331});