From af8de22210ccb52be8912e035368c293275c5e2f Mon Sep 17 00:00:00 2001 From: jin Date: Sun, 19 Nov 2023 18:25:34 +0300 Subject: [PATCH] $mol_crypto_key - new async crypto api, more sync --- crypto/key/key.ts | 127 +++++++++++++++++++++++++++++++++++++ crypto/key/key.web.test.ts | 64 +++++++++++++++++++ 2 files changed, 191 insertions(+) create mode 100644 crypto/key/key.ts create mode 100644 crypto/key/key.web.test.ts diff --git a/crypto/key/key.ts b/crypto/key/key.ts new file mode 100644 index 00000000000..a0a19cb5a7d --- /dev/null +++ b/crypto/key/key.ts @@ -0,0 +1,127 @@ +namespace $ { + + const algorithm = { + name: 'ECDSA', + hash: 'SHA-256', + namedCurve: "P-256", + } + + export class $mol_crypto_key extends $mol_buffer { + + static from< This extends typeof $mol_crypto_key >( this: This, serial: string | Uint8Array ) { + + if( typeof serial === 'string' ) { + serial = new Uint8Array([ + ... $mol_base64_decode_safe( serial.slice( 0, 43 ) ), + ... $mol_base64_decode_safe( serial.slice( 43, 86 ) ), + ... $mol_base64_decode_safe( serial.slice( 86, 129 ) ), + ]) + } + + return new this( serial.buffer, serial.byteOffset, serial.byteLength ) as InstanceType< This > + } + + asArray() { + return new Uint8Array( this.buffer, this.byteOffset, this.byteLength ) + } + + @ $mol_memo.method + toString() { + const arr = this.asArray() + return $mol_base64_encode_safe( arr.subarray( 0, 32 ) ) + + $mol_base64_encode_safe( arr.subarray( 32, 64 ) ) + + $mol_base64_encode_safe( arr.subarray( 64 ) ) + } + + } + + export class $mol_crypto_key_public extends $mol_crypto_key { + + static size_str = 86 + static size_bin = 64 + + @ $mol_memo.method + async native() { + const str = this.toString() + return $mol_crypto_native.subtle.importKey( + 'jwk', + { + crv: "P-256", + ext: true, + key_ops: [ 'verify' ], + kty: "EC", + x: str.slice( 0, 43 ), + y: str.slice( 43, 86 ), + }, + algorithm, + true, + [ 'verify' ], + ) + } + + async verify( data: BufferSource, sign: BufferSource ) { + return await $mol_crypto_native.subtle.verify( + algorithm, + await this.native(), + sign, + data, + ) + } + + } + + export class $mol_crypto_key_private extends $mol_crypto_key { + + static size_str = 129 + static size_bin = 96 + static size_sign = 64 + + static async generate() { + + const pair = await $mol_crypto_native.subtle.generateKey( + algorithm, + true, + [ 'sign', 'verify' ] + ) + + const { x, y, d } = await $mol_crypto_native.subtle.exportKey( 'jwk', pair.privateKey ) + return $mol_crypto_key_private.from( x + y! + d! ) + + } + + @ $mol_memo.method + async native() { + const str = this.toString() + return await $mol_crypto_native.subtle.importKey( + 'jwk', + { + crv: "P-256", + ext: true, + key_ops: [ 'sign' ], + kty: "EC", + x: str.slice( 0, 43 ), + y: str.slice( 43, 86 ), + d: str.slice( 86, 129 ), + }, + algorithm, + true, + [ 'sign' ], + ) + } + + @ $mol_memo.method + public() { + return new $mol_crypto_key_public( this.buffer, this.byteOffset, this.byteOffset + 64 ) + } + + async sign( data: BufferSource ) { + return await $mol_crypto_native.subtle.sign( + algorithm, + await this.native(), + data + ) + } + + } + +} diff --git a/crypto/key/key.web.test.ts b/crypto/key/key.web.test.ts new file mode 100644 index 00000000000..b34f54b559d --- /dev/null +++ b/crypto/key/key.web.test.ts @@ -0,0 +1,64 @@ +namespace $ { + $mol_test({ + + async 'str sizes'() { + + const key_private = await $$.$mol_crypto_key_private.generate() + const key_public = key_private.public() + + $mol_assert_equal( key_private.toString().length, $mol_crypto_key_private.size_str ) + $mol_assert_equal( key_public.toString().length, $mol_crypto_key_public.size_str ) + + $mol_assert_equal( key_private.asArray().length, $mol_crypto_key_private.size_bin ) + $mol_assert_equal( key_public.asArray().length, $mol_crypto_key_public.size_bin ) + + const data = new Uint8Array([ 1, 2, 3 ]) + const sign = await key_private.sign( data ) + $mol_assert_equal( sign.byteLength, $mol_crypto_key_private.size_sign ) + + }, + + async 'verify self signed with auto generated key'() { + + const Alice = await $$.$mol_crypto_key_private.generate() + const data = new Uint8Array([ 1, 2, 3 ]) + const sign = await Alice.sign( data ) + + $mol_assert_ok( await Alice.public().verify( data, sign ) ) + + }, + + async 'verify signed with str exported auto generated key'() { + + const Alice = await $$.$mol_crypto_key_private.generate() + const data = new Uint8Array([ 1, 2, 3 ]) + + const Bella = $mol_crypto_key_private.from( Alice.toString() ) + const sign = await Bella.sign( data ) + + const Catie = $mol_crypto_key_public.from( Alice.public().toString() ) + $mol_assert_ok( await Catie.verify( data, sign ) ) + + const Diana = $mol_crypto_key_public.from( Alice.toString() ) + $mol_assert_ok( await Diana.verify( data, sign ) ) + + }, + + async 'verify signed with bin exported auto generated key'() { + + const Alice = await $$.$mol_crypto_key_private.generate() + const data = new Uint8Array([ 1, 2, 3 ]) + + const Bella = $mol_crypto_key_private.from( Alice.asArray() ) + const sign = await Bella.sign( data ) + + const Catie = $mol_crypto_key_public.from( Alice.public().asArray() ) + $mol_assert_ok( await Catie.verify( data, sign ) ) + + const Diana = $mol_crypto_key_public.from( Alice.asArray() ) + $mol_assert_ok( await Diana.verify( data, sign ) ) + + }, + + }) +}