From c74f157d97ed1d6b4fbad71301a277bc33960f1e Mon Sep 17 00:00:00 2001 From: Max Pschonder Date: Tue, 2 Feb 2021 16:48:40 +0100 Subject: [PATCH] finished encryption and decryption with @hivemind-js --- .gitignore | 4 +++ package-lock.json | 3 +- spago.dhall | 11 ++++++- src/AWS/Crypto/Crypto.js | 12 +++++--- src/AWS/Crypto/Crypto.purs | 52 +++++++++++++++++++++------------ src/Main.purs | 10 ------- test/AWS/Crypto/CryptoSpec.purs | 24 +++++++++++++++ test/Main.purs | 11 ++++--- 8 files changed, 88 insertions(+), 39 deletions(-) delete mode 100644 src/Main.purs create mode 100644 test/AWS/Crypto/CryptoSpec.purs diff --git a/.gitignore b/.gitignore index 361cf52..927bd70 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,7 @@ node_modules # Generated files .psci output + +/.spago +/.idea +/.psc-ide-port diff --git a/package-lock.json b/package-lock.json index bb08501..2458408 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,8 +8,7 @@ "version": "0.1.0", "license": "Apache-2.0", "dependencies": { - "@aws-crypto/client-node": "^2.0.0", - "@aws-crypto/kms-keyring-node": "^2.0.0" + "@aws-crypto/client-node": "^2.0.0" }, "devDependencies": { "purescript": "^0.13.8", diff --git a/spago.dhall b/spago.dhall index 875fe0e..3ff45c0 100644 --- a/spago.dhall +++ b/spago.dhall @@ -3,7 +3,16 @@ Welcome to a Spago project! You can edit this file as you like. -} { name = "my-project" -, dependencies = [ "aff-promise", "console", "effect", "psci-support" ] +, dependencies = + [ "aff-promise" + , "console" + , "debug" + , "effect" + , "node-buffer" + , "psci-support" + , "spec" + , "spec-discovery" + ] , packages = ./packages.dhall , sources = [ "src/**/*.purs", "test/**/*.purs" ] } diff --git a/src/AWS/Crypto/Crypto.js b/src/AWS/Crypto/Crypto.js index 114cacd..ffbbf0e 100644 --- a/src/AWS/Crypto/Crypto.js +++ b/src/AWS/Crypto/Crypto.js @@ -2,10 +2,14 @@ const crypto = require("@aws-crypto/client-node") +exports.makeClient = () => crypto.buildClient( + crypto.CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT +) + exports.makeKeyringImpl = (generatorKeyId, keyIds) => () => new crypto.KmsKeyringNode({ generatorKeyId, keyIds }) -exports.encryptImpl = (keyring, cleartext, context) => () => crypto.encrypt(keyring, cleartext, { - encryptionContext: context, - }) +exports.encryptImpl = (client, keyring, context, plaintext) => () => client.encrypt(keyring, plaintext, { + encryptionContext: context, +}) -exports.decryptImpl = (keyring, ciphertext) => decrypt(keyring, result) \ No newline at end of file +exports.decryptImpl = (client, keyring, ciphertext) => () => client.decrypt(keyring, ciphertext) \ No newline at end of file diff --git a/src/AWS/Crypto/Crypto.purs b/src/AWS/Crypto/Crypto.purs index b34415c..2785257 100644 --- a/src/AWS/Crypto/Crypto.purs +++ b/src/AWS/Crypto/Crypto.purs @@ -1,42 +1,58 @@ module AWS.Crypto.Crypto where import Prelude - import Control.Promise (Promise, toAffE) -import Data.Function.Uncurried (Fn2, Fn3, runFn2, runFn3) +import Data.Function.Uncurried (Fn2, Fn3, Fn4, runFn2, runFn3, runFn4) import Data.Newtype (class Newtype, un) import Effect (Effect) import Effect.Aff (Aff) +import Effect.Class (liftEffect) +import Node.Buffer (Buffer, toString, fromString) +import Node.Encoding (Encoding(..)) + +foreign import data KmsKeyring :: Type -foreign import data KmsKeyring :: Type +newtype Arn + = Arn String -newtype Arn = Arn String derive instance ntArn :: Newtype Arn _ +foreign import data Client :: Type + +foreign import makeClient :: Effect Client foreign import makeKeyringImpl :: Fn2 String (Array String) (Effect KmsKeyring) makeKeyring :: Arn -> Array Arn -> Effect KmsKeyring -makeKeyring (Arn generatorKeyId) keyIds = runFn2 makeKeyringImpl generatorKeyId $ map (un Arn) keyIds - +makeKeyring (Arn generatorKeyId) keyIds = runFn2 makeKeyringImpl generatorKeyId $ map (un Arn) keyIds -type InternalEncryptionResult = { result :: String } +type InternalEncryptionResult + = { result :: Buffer } -foreign import encryptImpl :: forall a. Fn3 KmsKeyring String a (Effect (Promise InternalEncryptionResult)) +foreign import encryptImpl :: forall a. Fn4 Client KmsKeyring a String (Effect (Promise InternalEncryptionResult)) -type EncryptionResult = { ciphertext :: String } +type EncryptionResult + = { ciphertext :: String } -encrypt :: forall a. KmsKeyring -> String -> a -> Aff EncryptionResult -encrypt keyring plaintext context = runFn3 encryptImpl keyring plaintext context # toAffE <#> - \ir -> { ciphertext : ir.result } +encrypt :: forall a. Client -> KmsKeyring -> a -> String -> Aff EncryptionResult +encrypt client keyring context plaintext = runFn4 encryptImpl client keyring context plaintext # toAffE >>= convert + where + convert :: InternalEncryptionResult -> Aff EncryptionResult + convert ir = liftEffect $ toString Base64 ir.result <#> { ciphertext: _ } +type InternalDecryptionResult a + = { plaintext :: Buffer, messageHeader :: { encryptionContext :: a } } -type InternalDecryptionResult a = { plaintext :: String, messageHeader :: { encryptionContext :: a } } +foreign import decryptImpl :: forall a. Fn3 Client KmsKeyring Buffer (Effect (Promise (InternalDecryptionResult a))) -foreign import decryptImpl :: forall a. Fn2 KmsKeyring String (Effect (Promise (InternalDecryptionResult a))) +type DecryptionResult a + = { plaintext :: String, encryptionContext :: a } -type DecryptionResult a = { plaintext :: String, encryptionContext :: a } +decrypt :: forall a. Client -> KmsKeyring -> String -> Aff (DecryptionResult a) +decrypt client keyring ciphertext = (convertIn >>= runFn3 decryptImpl client keyring) # toAffE >>= convertOut + where + convertIn :: Effect Buffer + convertIn = fromString ciphertext Base64 -decrypt :: forall a. KmsKeyring -> String -> Aff (DecryptionResult a) -decrypt keyring ciphertext = runFn2 decryptImpl keyring ciphertext # toAffE <#> - \ir -> { plaintext : ir.plaintext, encryptionContext : ir.messageHeader.encryptionContext } + convertOut :: InternalDecryptionResult a -> Aff (DecryptionResult a) + convertOut ir = liftEffect $ toString UTF8 ir.plaintext <#> { plaintext: _, encryptionContext: ir.messageHeader.encryptionContext } diff --git a/src/Main.purs b/src/Main.purs deleted file mode 100644 index 5c18dca..0000000 --- a/src/Main.purs +++ /dev/null @@ -1,10 +0,0 @@ -module Main where - -import Prelude - -import Effect (Effect) -import Effect.Console (log) - -main :: Effect Unit -main = do - log "🍝" diff --git a/test/AWS/Crypto/CryptoSpec.purs b/test/AWS/Crypto/CryptoSpec.purs new file mode 100644 index 0000000..7bb69b1 --- /dev/null +++ b/test/AWS/Crypto/CryptoSpec.purs @@ -0,0 +1,24 @@ +module AWS.Crypto.CryptoSpec where + +import AWS.Crypto.Crypto (Arn(..), EncryptionResult, decrypt, encrypt, makeClient, makeKeyring) +import Effect.Class (liftEffect) +import Prelude (Unit, bind, ($)) +import Test.Spec (Spec, describe, pending') +import Test.Spec.Assertions (shouldEqual) + +spec :: Spec Unit +spec = + describe "AWS.Crypto" do + describe "decrypt/encrypt" do + pending' "should roundtrip encrypt/decrypt data" do + client <- liftEffect makeClient + keyring <- + liftEffect + $ makeKeyring + (Arn "arn:aws:kms:us-west-2:658956600833:key/b3537ef1-d8dc-4780-9f5a-55776cbb2f7f") + [] + let + testData = "some-data" + encrypted :: EncryptionResult <- encrypt client keyring {} testData + decrypted <- decrypt client keyring (encrypted.ciphertext) + decrypted.plaintext `shouldEqual` testData diff --git a/test/Main.purs b/test/Main.purs index f91f98c..78fd5e2 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -3,9 +3,12 @@ module Test.Main where import Prelude import Effect (Effect) -import Effect.Class.Console (log) +import Effect.Aff (launchAff_) +import Test.Spec.Discovery (discover) +import Test.Spec.Reporter.Console (consoleReporter) +import Test.Spec.Runner (runSpec) main :: Effect Unit -main = do - log "🍝" - log "You should add some tests." +main = launchAff_ do + specs <- discover "\\..*Spec" + runSpec [consoleReporter] specs