diff --git a/test/browser_chrome_test.dart b/test/browser_chrome_test.dart deleted file mode 100644 index 850123a5..00000000 --- a/test/browser_chrome_test.dart +++ /dev/null @@ -1,186 +0,0 @@ -@TestOn('chrome') -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import 'dart:js_interop'; -import 'dart:typed_data'; - -import 'package:test/test.dart'; -import 'package:webcrypto/src/impl_js/impl_js.dart'; -import 'package:webcrypto/src/crypto_subtle.dart'; -import 'package:webcrypto/src/testing/webcrypto/random.dart'; - -void main() { - group('fillRandomBytes', () { - test('Uint8List: success', () { - final data = Uint8List(16 * 1024); - isAllZero(data); - fillRandomBytes(data); - isNotAllZero(data); - }); - - test('Uint8List: too long', () { - expect( - () => fillRandomBytes(Uint8List(1000000)), - throwsA(isA().having( - (e) => e.message, - 'message', - contains( - '''Failed to execute 'getRandomValues' on 'Crypto': The ArrayBufferView's byte length (1000000) exceeds the number of bytes of entropy available via this API (65536).''', - ), - )), - ); - }); - - test('Uint64List: not supported type', () { - expect( - () => fillRandomBytes(Uint64List(32)), - throwsA( - isA().having( - (e) => e.message, - 'message', - contains( - 'Uint64List not supported on the web.', - ), - ), - ), - ); - }); - }); - - group('crypto', () { - test('getRandomValues: success', () { - final data = Uint8List(16 * 1024); - isAllZero(data); - window.crypto.getRandomValues(data.toJS); - isNotAllZero(data); - }); - - test('getRandomValues: too long', () { - expect( - () => window.crypto.getRandomValues(Uint8List(1000000).toJS), - throwsA( - isA() - .having( - (e) => e.name, - 'name', - 'QuotaExceededError', - ) - .having( - (e) => e.message, - 'message', - contains( - '''Failed to execute 'getRandomValues' on 'Crypto': The ArrayBufferView's byte length (1000000) exceeds the number of bytes of entropy available via this API (65536).''', - ), - ), - ), - ); - }); - - test('getRandomValues: not supported type', () { - expect( - () => window.crypto.getRandomValues(Float32List(32).toJS), - throwsA( - isA() - .having( - (e) => e.name, - 'name', - 'TypeMismatchError', - ) - .having( - (e) => e.message, - 'message', - contains( - '''Failed to execute 'getRandomValues' on 'Crypto': The provided ArrayBufferView is of type 'Float32', which is not an integer array type.''', - ), - ), - ), - ); - }); - }); - - group('crypto.subtle', () { - test('generateCryptoKey: success', () async { - final key = await window.crypto.subtle - .generateCryptoKey( - const Algorithm( - name: 'ECDH', - namedCurve: 'P-384', - ).toJS, - false, - ['deriveBits'].toJS, - ) - .toDart; - - expect(key, isA()); - }); - - test('generateCryptoKey: invalid keyUsages: SyntaxError', () async { - expect( - () async => await window.crypto.subtle - .generateCryptoKey( - const Algorithm( - name: 'ECDH', - namedCurve: 'P-384', - ).toJS, - false, - [].toJS, - ) - .toDart, - throwsA( - isA() - .having( - (e) => e.name, - 'name', - 'SyntaxError', - ) - .having( - (e) => e.message, - 'message', - contains( - '''Usages cannot be empty when creating a key''', - ), - ), - ), - ); - }); - - test('generateCryptoKey: invalid algorithm: TypeError', () async { - expect( - () async => await window.crypto.subtle - .generateCryptoKey( - const Algorithm().toJS, - false, - ['deriveBits'].toJS, - ) - .toDart, - throwsA( - isA() - .having( - (e) => e.name, - 'name', - 'TypeError', - ) - .having( - (e) => e.message, - 'message', - contains( - '''Failed to execute 'generateKey' on 'SubtleCrypto': Algorithm: name: Missing or not a string''', - ), - ), - ), - ); - }); - }); -} diff --git a/test/browser_firefox_test.dart b/test/browser_firefox_test.dart deleted file mode 100644 index c79328dc..00000000 --- a/test/browser_firefox_test.dart +++ /dev/null @@ -1,186 +0,0 @@ -@TestOn('firefox') -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import 'dart:js_interop'; -import 'dart:typed_data'; - -import 'package:test/test.dart'; -import 'package:webcrypto/src/impl_js/impl_js.dart'; -import 'package:webcrypto/src/crypto_subtle.dart'; -import 'package:webcrypto/src/testing/webcrypto/random.dart'; - -void main() { - group('fillRandomBytes', () { - test('Uint8List: success', () { - final data = Uint8List(16 * 1024); - isAllZero(data); - fillRandomBytes(data); - isNotAllZero(data); - }); - - test('Uint8List: too long', () { - expect( - () => fillRandomBytes(Uint8List(1000000)), - throwsA(isA().having( - (e) => e.message, - 'message', - contains( - '''Crypto.getRandomValues: getRandomValues can only generate maximum 65536 bytes''', - ), - )), - ); - }); - - test('Uint64List: not supported type', () { - expect( - () => fillRandomBytes(Uint64List(32)), - throwsA( - isA().having( - (e) => e.message, - 'message', - contains( - 'Uint64List not supported on the web.', - ), - ), - ), - ); - }); - }); - - group('crypto', () { - test('getRandomValues: success', () { - final data = Uint8List(16 * 1024); - isAllZero(data); - window.crypto.getRandomValues(data.toJS); - isNotAllZero(data); - }); - - test('getRandomValues: too long', () { - expect( - () => window.crypto.getRandomValues(Uint8List(1000000).toJS), - throwsA( - isA() - .having( - (e) => e.name, - 'name', - 'QuotaExceededError', - ) - .having( - (e) => e.message, - 'message', - contains( - '''Crypto.getRandomValues: getRandomValues can only generate maximum 65536 bytes''', - ), - ), - ), - ); - }); - - test('getRandomValues: not supported type', () { - expect( - () => window.crypto.getRandomValues(Float32List(32).toJS), - throwsA( - isA() - .having( - (e) => e.name, - 'name', - 'TypeMismatchError', - ) - .having( - (e) => e.message, - 'message', - contains( - '''The type of an object is incompatible with the expected type of the parameter associated to the object''', - ), - ), - ), - ); - }); - }); - - group('crypto.subtle', () { - test('generateCryptoKey: success', () async { - final key = await window.crypto.subtle - .generateCryptoKey( - const Algorithm( - name: 'ECDH', - namedCurve: 'P-384', - ).toJS, - false, - ['deriveBits'].toJS, - ) - .toDart; - - expect(key, isA()); - }); - - test('generateCryptoKey: invalid keyUsages: SyntaxError', () async { - expect( - () async => await window.crypto.subtle - .generateCryptoKey( - const Algorithm( - name: 'ECDH', - namedCurve: 'P-384', - ).toJS, - false, - [].toJS, - ) - .toDart, - throwsA( - isA() - .having( - (e) => e.name, - 'name', - 'SyntaxError', - ) - .having( - (e) => e.message, - 'message', - contains( - '''An invalid or illegal string was specified''', - ), - ), - ), - ); - }); - - test('generateCryptoKey: invalid algorithm: SyntaxError', () async { - expect( - () async => await window.crypto.subtle - .generateCryptoKey( - const Algorithm().toJS, - false, - ['deriveBits'].toJS, - ) - .toDart, - throwsA( - isA() - .having( - (e) => e.name, - 'name', - 'SyntaxError', - ) - .having( - (e) => e.message, - 'message', - contains( - '''An invalid or illegal string was specified''', - ), - ), - ), - ); - }); - }); -} diff --git a/test/crypto_subtle_test.dart b/test/crypto_subtle_test.dart new file mode 100644 index 00000000..8cc63e4b --- /dev/null +++ b/test/crypto_subtle_test.dart @@ -0,0 +1,293 @@ +@TestOn('browser') +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:js_interop'; +import 'dart:typed_data'; + +import 'package:test/test.dart'; +import 'package:webcrypto/src/crypto_subtle.dart' as subtle; +import 'package:webcrypto/src/impl_js/impl_js.dart'; + +void main() { + group('fillRandomBytes', () { + test('Uint8List: success', () { + final data = Uint8List(16 * 1024); + expect( + data.every((e) => e == 0), + isTrue, + ); + fillRandomBytes(data); + expect( + data.any((e) => e != 0), + isTrue, + ); + }); + + test('Uint8List: too long', () { + expect( + () => fillRandomBytes(Uint8List(1000000)), + throwsA( + isA(), + ), + ); + }); + + test('Uint64List: not supported type', () { + expect( + () => fillRandomBytes(Uint64List(32)), + throwsA( + isA(), + ), + ); + }); + }); + + group('crypto', () { + test('getRandomValues: success', () { + final data = Uint8List(16 * 1024); + expect( + data.every((e) => e == 0), + isTrue, + ); + subtle.window.crypto.getRandomValues(data.toJS); + expect( + data.any((e) => e != 0), + isTrue, + ); + }); + + test('getRandomValues: too long', () { + expect( + () => subtle.window.crypto.getRandomValues(Uint8List(1000000).toJS), + throwsA( + isA().having( + (e) => e.name, + 'name', + 'QuotaExceededError', + ), + ), + ); + }); + + test('getRandomValues: not supported type', () { + expect( + () => subtle.window.crypto.getRandomValues(Float32List(32).toJS), + throwsA( + isA().having( + (e) => e.name, + 'name', + 'TypeMismatchError', + ), + ), + ); + }); + }); + + group('crypto.subtle', () { + test('generateCryptoKey: success', () async { + expect( + await subtle.window.crypto.subtle + .generateCryptoKey( + const subtle.Algorithm( + name: 'AES-GCM', + length: 256, + ).toJS, + false, + ['encrypt', 'decrypt'].toJS, + ) + .toDart, + isA() + .having( + (key) => key.type, + 'type', + 'secret', + ) + .having( + (key) => key.extractable, + 'extractable', + false, + ) + .having( + (key) => key.usages, + 'usages', + containsAll(['encrypt', 'decrypt']), + ), + ); + }); + + test('generateCryptoKey: invalid keyUsages', () { + expect( + () async => await subtle.window.crypto.subtle + .generateCryptoKey( + const subtle.Algorithm( + name: 'AES-GCM', + length: 256, + ).toJS, + false, + [].toJS, + ) + .toDart, + throwsA( + isA().having( + (e) => e.name, + 'name', + 'SyntaxError', + ), + ), + ); + }); + + test('generateCryptoKey: invalid algorithm', () { + expect( + () async => await subtle.window.crypto.subtle + .generateCryptoKey( + const subtle.Algorithm().toJS, + false, + ['encrypt', 'decrypt'].toJS, + ) + .toDart, + throwsA( + isA().having( + (e) => e.name, + 'name', + // Chrome / Safari throw TypeError + // Firefox throws SyntaxError + anyOf('TypeError', 'SyntaxError'), + ), + ), + ); + }); + + test('generateKeyPair: e65537', () async { + expect( + await subtle.window.crypto.subtle + .generateCryptoKeyPair( + subtle.Algorithm( + name: 'RSA-OAEP', + modulusLength: 4096, + publicExponent: Uint8List.fromList([0x01, 0x00, 0x01]), + hash: 'SHA-256', + ).toJS, + false, + ['encrypt', 'decrypt'].toJS, + ) + .toDart, + isA() + .having( + (key) => key.publicKey.type, + 'publicKey.type', + 'public', + ) + .having( + (key) => key.publicKey.extractable, + 'publicKey.extractable', + true, + ) + .having( + (key) => key.publicKey.usages, + 'publicKey.usages', + ['encrypt'], + ) + .having( + (key) => key.privateKey.type, + 'privateKey.type', + 'private', + ) + .having( + (key) => key.privateKey.extractable, + 'privateKey.extractable', + false, + ) + .having( + (key) => key.privateKey.usages, + 'privateKey.usages', + ['decrypt'], + ), + ); + }); + + test(testOn: 'chrome || firefox', 'generateKeyPair: e3', () async { + expect( + await subtle.window.crypto.subtle + .generateCryptoKeyPair( + subtle.Algorithm( + name: 'RSA-OAEP', + modulusLength: 4096, + publicExponent: Uint8List.fromList([0x03]), + hash: 'SHA-256', + ).toJS, + false, + ['encrypt', 'decrypt'].toJS, + ) + .toDart, + isA() + .having( + (key) => key.publicKey.type, + 'publicKey.type', + 'public', + ) + .having( + (key) => key.publicKey.extractable, + 'publicKey.extractable', + true, + ) + .having( + (key) => key.publicKey.usages, + 'publicKey.usages', + ['encrypt'], + ) + .having( + (key) => key.privateKey.type, + 'privateKey.type', + 'private', + ) + .having( + (key) => key.privateKey.extractable, + 'privateKey.extractable', + false, + ) + .having( + (key) => key.privateKey.usages, + 'privateKey.usages', + ['decrypt'], + ), + ); + }); + + test(testOn: 'safari', 'generateKeyPair: e3', () { + expect( + () async => await subtle.window.crypto.subtle + .generateCryptoKeyPair( + subtle.Algorithm( + name: 'RSA-OAEP', + modulusLength: 4096, + publicExponent: Uint8List.fromList([0x03]), + hash: 'SHA-256', + ).toJS, + false, + ['encrypt', 'decrypt'].toJS, + ) + .toDart, + throwsA( + isA().having( + (e) => e.name, + 'name', + 'OperationError', + ), + ), + ); + }); + }); +}