From 4a19984bca32b408b45bd068f520467fa7f13f2e Mon Sep 17 00:00:00 2001 From: Alexis Hernandez Date: Wed, 12 Jan 2022 13:00:03 -0700 Subject: [PATCH 1/3] Showcase that the crypto api is broken --- .../dom/tests/chrome/CryptoTests.scala | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests-chrome/src/test/scala/org/scalajs/dom/tests/chrome/CryptoTests.scala diff --git a/tests-chrome/src/test/scala/org/scalajs/dom/tests/chrome/CryptoTests.scala b/tests-chrome/src/test/scala/org/scalajs/dom/tests/chrome/CryptoTests.scala new file mode 100644 index 000000000..91d011079 --- /dev/null +++ b/tests-chrome/src/test/scala/org/scalajs/dom/tests/chrome/CryptoTests.scala @@ -0,0 +1,21 @@ +package org.scalajs.dom.tests.chrome + +import org.junit.Test + +class CryptoTests { + import org.scalajs.dom + import scala.scalajs.js.typedarray._ + + @Test final def cryptoGetRandomValuesWork(): Unit = { + // This fails + dom.crypto.getRandomValues(Array.ofDim[Byte](8).toTypedArray) + } + + @Test final def webcryptoGetRandomValuesWork(): Unit = { + dom.webcrypto.getRandomValues(Array.ofDim[Byte](8).toTypedArray) + } + + @Test final def cryptoCryptoGetRandomValuesWork(): Unit = { + dom.crypto.crypto.getRandomValues(Array.ofDim[Byte](8).toTypedArray) + } +} From f5b46f0ebe4dc616da02010f05a4d4480109af6c Mon Sep 17 00:00:00 2001 From: Alexis Hernandez Date: Thu, 13 Jan 2022 09:34:48 -0700 Subject: [PATCH 2/3] Add WebCryptoApiTests --- .../dom/tests/chrome/CryptoTests.scala | 21 --- .../dom/tests/shared/SharedTests.scala | 2 +- .../dom/tests/shared/WebCryptoApiTests.scala | 122 ++++++++++++++++++ 3 files changed, 123 insertions(+), 22 deletions(-) delete mode 100644 tests-chrome/src/test/scala/org/scalajs/dom/tests/chrome/CryptoTests.scala create mode 100644 tests-shared/src/main/scala/org/scalajs/dom/tests/shared/WebCryptoApiTests.scala diff --git a/tests-chrome/src/test/scala/org/scalajs/dom/tests/chrome/CryptoTests.scala b/tests-chrome/src/test/scala/org/scalajs/dom/tests/chrome/CryptoTests.scala deleted file mode 100644 index 91d011079..000000000 --- a/tests-chrome/src/test/scala/org/scalajs/dom/tests/chrome/CryptoTests.scala +++ /dev/null @@ -1,21 +0,0 @@ -package org.scalajs.dom.tests.chrome - -import org.junit.Test - -class CryptoTests { - import org.scalajs.dom - import scala.scalajs.js.typedarray._ - - @Test final def cryptoGetRandomValuesWork(): Unit = { - // This fails - dom.crypto.getRandomValues(Array.ofDim[Byte](8).toTypedArray) - } - - @Test final def webcryptoGetRandomValuesWork(): Unit = { - dom.webcrypto.getRandomValues(Array.ofDim[Byte](8).toTypedArray) - } - - @Test final def cryptoCryptoGetRandomValuesWork(): Unit = { - dom.crypto.crypto.getRandomValues(Array.ofDim[Byte](8).toTypedArray) - } -} diff --git a/tests-shared/src/main/scala/org/scalajs/dom/tests/shared/SharedTests.scala b/tests-shared/src/main/scala/org/scalajs/dom/tests/shared/SharedTests.scala index 3a02babea..59843ebd0 100644 --- a/tests-shared/src/main/scala/org/scalajs/dom/tests/shared/SharedTests.scala +++ b/tests-shared/src/main/scala/org/scalajs/dom/tests/shared/SharedTests.scala @@ -3,7 +3,7 @@ package org.scalajs.dom.tests.shared import org.scalajs.dom.tests.shared.AsyncTesting._ import org.junit.Test -trait SharedTests { +trait SharedTests extends WebCryptoApiTests { // =================================================================================================================== // Tests WITHOUT org.scalajs.dom._ in scope diff --git a/tests-shared/src/main/scala/org/scalajs/dom/tests/shared/WebCryptoApiTests.scala b/tests-shared/src/main/scala/org/scalajs/dom/tests/shared/WebCryptoApiTests.scala new file mode 100644 index 000000000..a53b13dda --- /dev/null +++ b/tests-shared/src/main/scala/org/scalajs/dom/tests/shared/WebCryptoApiTests.scala @@ -0,0 +1,122 @@ +package org.scalajs.dom.tests.shared + +import org.junit.Assert._ +import org.junit.Test +import org.scalajs.dom +import org.scalajs.dom.tests.shared.AsyncTesting._ + +import scala.scalajs.js +import scala.scalajs.js.typedarray._ + +trait WebCryptoApiTests { + + @Test final def getRandomValuesWork: Unit = { + dom.webcrypto.getRandomValues(Array.ofDim[Byte](8).toTypedArray) + } + + @Test final def generateAESEncryptionKeyWorks: AsyncResult = async { + generateAESEncryptionKey() + } + + @Test final def aesEncryptionDecryptionTest: AsyncResult = async { + val data = "data" + for { + key <- generateAESEncryptionKey() + iv = generateAESInitializationVector() + encrypted <- aesEncrypt(key, iv, data) + decrypted <- aesDecrypt(key, iv, encrypted) + } yield assertEquals(data, decrypted) + } + + @Test final def aesKeyDerivationWorks: AsyncResult = async { + val derivationAlgorithm = "PBKDF2" + val pbdkf2 = new dom.Pbkdf2Params { + val name = derivationAlgorithm + val salt = "salt".getBytes.toTypedArray.buffer + val iterations = 100.toDouble + val hash = "SHA-512" + } + val aesCtr = new dom.AesDerivedKeyParams { + val name = "AES-GCM" + val length = 256 + } + + for { + pbkdf2Key <- dom.crypto.subtle + .importKey( + dom.KeyFormat.raw, + "password".getBytes.toTypedArray.buffer, + derivationAlgorithm, + false, + js.Array(dom.KeyUsage.deriveKey, dom.KeyUsage.deriveBits) + ) + .toFuture + + aesKey <- dom.crypto.subtle + .deriveKey( + pbdkf2, + pbkdf2Key, + aesCtr, + true, + js.Array(dom.KeyUsage.encrypt, dom.KeyUsage.decrypt) + ) + .toFuture + .map(_.asInstanceOf[dom.CryptoKey]) + } yield assertNotNull(aesKey) + } + + private def generateAESEncryptionKey() = { + dom.crypto.subtle + .generateKey( + new dom.AesKeyAlgorithm { + val name = "AES-GCM" + val length = 256 + }, + true, + js.Array(dom.KeyUsage.encrypt, dom.KeyUsage.decrypt) + ) + .toFuture + .map(_.asInstanceOf[dom.CryptoKey]) + } + + private def generateAESInitializationVector() = { + dom.webcrypto.getRandomValues(Array.ofDim[Byte](12).toTypedArray) + } + + private def aesEncrypt(key: dom.CryptoKey, iv0: dom.BufferSource, data: String) = { + dom.crypto.subtle + .encrypt( + new dom.AesGcmParams { + val name = "AES-GCM" + val iv = iv0 + val tagLength = 128 + val additionalData = "".getBytes.toTypedArray.buffer + }, + key, + data.getBytes().toTypedArray + ) + .toFuture + .map(_.asInstanceOf[ArrayBuffer]) + } + + private def aesDecrypt(key: dom.CryptoKey, iv0: dom.BufferSource, encrypted: dom.BufferSource) = { + dom.crypto.subtle + .decrypt( + new dom.AesGcmParams { + val name = "AES-GCM" + val iv = iv0 + val tagLength = 128 + val additionalData = "".getBytes.toTypedArray.buffer + }, + key, + encrypted + ) + .toFuture + .map(_.asInstanceOf[ArrayBuffer]) + .map { buffer => + val arr = Array.ofDim[Byte](buffer.byteLength) + TypedArrayBuffer.wrap(buffer).get(arr) + new String(arr, "UTF-8") + } + } +} From 64a26639abb74fb1648c6db6450605668d896bb8 Mon Sep 17 00:00:00 2001 From: Alexis Hernandez Date: Thu, 13 Jan 2022 09:57:09 -0700 Subject: [PATCH 3/3] Fix tests --- .../org/scalajs/dom/tests/shared/BrowserTests.scala | 2 +- .../org/scalajs/dom/tests/shared/SharedTests.scala | 2 +- .../scalajs/dom/tests/shared/WebCryptoApiTests.scala | 10 ++++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/tests-shared/src/main/scala/org/scalajs/dom/tests/shared/BrowserTests.scala b/tests-shared/src/main/scala/org/scalajs/dom/tests/shared/BrowserTests.scala index 10d8c26a8..c0e55b4ad 100644 --- a/tests-shared/src/main/scala/org/scalajs/dom/tests/shared/BrowserTests.scala +++ b/tests-shared/src/main/scala/org/scalajs/dom/tests/shared/BrowserTests.scala @@ -17,7 +17,7 @@ import scala.scalajs.js import scala.scalajs.js.Thenable.Implicits._ import scala.util.Try -trait BrowserTests { +trait BrowserTests extends WebCryptoApiTests { def read[T](reader: ReadableStreamReader[T])(values: Seq[T]): Future[Seq[T]] = { reader diff --git a/tests-shared/src/main/scala/org/scalajs/dom/tests/shared/SharedTests.scala b/tests-shared/src/main/scala/org/scalajs/dom/tests/shared/SharedTests.scala index 59843ebd0..3a02babea 100644 --- a/tests-shared/src/main/scala/org/scalajs/dom/tests/shared/SharedTests.scala +++ b/tests-shared/src/main/scala/org/scalajs/dom/tests/shared/SharedTests.scala @@ -3,7 +3,7 @@ package org.scalajs.dom.tests.shared import org.scalajs.dom.tests.shared.AsyncTesting._ import org.junit.Test -trait SharedTests extends WebCryptoApiTests { +trait SharedTests { // =================================================================================================================== // Tests WITHOUT org.scalajs.dom._ in scope diff --git a/tests-shared/src/main/scala/org/scalajs/dom/tests/shared/WebCryptoApiTests.scala b/tests-shared/src/main/scala/org/scalajs/dom/tests/shared/WebCryptoApiTests.scala index a53b13dda..17b0c8725 100644 --- a/tests-shared/src/main/scala/org/scalajs/dom/tests/shared/WebCryptoApiTests.scala +++ b/tests-shared/src/main/scala/org/scalajs/dom/tests/shared/WebCryptoApiTests.scala @@ -29,12 +29,14 @@ trait WebCryptoApiTests { } @Test final def aesKeyDerivationWorks: AsyncResult = async { + import scalajs.js.|._ + val derivationAlgorithm = "PBKDF2" val pbdkf2 = new dom.Pbkdf2Params { val name = derivationAlgorithm - val salt = "salt".getBytes.toTypedArray.buffer + val salt: dom.BufferSource = "salt".getBytes.toTypedArray.buffer val iterations = 100.toDouble - val hash = "SHA-512" + val hash: dom.HashAlgorithmIdentifier = dom.HashAlgorithm.`SHA-512` } val aesCtr = new dom.AesDerivedKeyParams { val name = "AES-GCM" @@ -90,7 +92,7 @@ trait WebCryptoApiTests { val name = "AES-GCM" val iv = iv0 val tagLength = 128 - val additionalData = "".getBytes.toTypedArray.buffer + val additionalData: dom.BufferSource = "".getBytes.toTypedArray.buffer }, key, data.getBytes().toTypedArray @@ -106,7 +108,7 @@ trait WebCryptoApiTests { val name = "AES-GCM" val iv = iv0 val tagLength = 128 - val additionalData = "".getBytes.toTypedArray.buffer + val additionalData: dom.BufferSource = "".getBytes.toTypedArray.buffer }, key, encrypted