|
| 1 | +module Node.Crypto.Cipher |
| 2 | + ( Cipher |
| 3 | + , new |
| 4 | + , NewOptions |
| 5 | + , new' |
| 6 | + , toDuplex |
| 7 | + , finalBuf |
| 8 | + , finalStr |
| 9 | + , getAuthTag |
| 10 | + , setAAD |
| 11 | + , SetAADOptions |
| 12 | + , setAAD' |
| 13 | + , setAutoPadding |
| 14 | + , setAutoPadding' |
| 15 | + , updateBufBuf |
| 16 | + , updateBufStr |
| 17 | + , updateStrBuf |
| 18 | + , updateStrStr |
| 19 | + ) where |
| 20 | + |
| 21 | + |
| 22 | +import Data.Nullable (Nullable) |
| 23 | +import Effect (Effect) |
| 24 | +import Effect.Uncurried (EffectFn1, EffectFn2, EffectFn3, EffectFn4, runEffectFn1, runEffectFn2, runEffectFn3, runEffectFn4) |
| 25 | +import Node.Buffer (Buffer) |
| 26 | +import Node.Encoding (Encoding, encodingToNode) |
| 27 | +import Node.Stream (Duplex) |
| 28 | +import Prim.Row as Row |
| 29 | +import Unsafe.Coerce (unsafeCoerce) |
| 30 | + |
| 31 | +-- | Instances of the `Cipher` class are used to encrypt data. The class can be used in one of two ways: |
| 32 | +-- | - As a stream that is both readable and writable, where plain unencrypted data is written to produce encrypted data on the readable side, or |
| 33 | +-- | - Using the `cipher.update()` and `cipher.final()` methods to produce the encrypted data. |
| 34 | +foreign import data Cipher :: Type |
| 35 | + |
| 36 | +foreign import newImpl :: EffectFn3 (String) (Buffer) (Nullable Buffer) (Cipher) |
| 37 | + |
| 38 | +-- | Creates and returns a Cipher object, with the given `algorithm`, `key`, and `initialization vector` (IV). |
| 39 | +-- | |
| 40 | +-- | Use `new'` instead of this function when using |
| 41 | +-- | - A cipher in CCM or OCB mode as the `authTagLength` options is required and specifies the length of the authentication tag in bytes |
| 42 | +-- | - A cipher in GCM mode and you want to use the `authTagLength` option to set the length of the authentication tag that is returned by `getAuthTag` (defaults to 16 bytes). |
| 43 | +-- | |
| 44 | +-- | For `chacha20-poly1305`, the `authTagLength` option defaults to 16 bytes. |
| 45 | +-- | |
| 46 | +-- | The `algorithm` is dependent on OpenSSL, examples are 'aes192', etc. On recent OpenSSL releases, |
| 47 | +-- | `openssl list -cipher-algorithms` will display the available cipher algorithms. |
| 48 | +-- | |
| 49 | +-- | If the cipher does not need an IV, it may be null. |
| 50 | +-- | |
| 51 | +-- | Initialization vectors should be unpredictable and unique; ideally, they will be cryptographically random. |
| 52 | +-- | They do not have to be secret: IVs are typically just added to ciphertext messages unencrypted. |
| 53 | +-- | It may sound contradictory that something has to be unpredictable and unique, but does not have to be secret; |
| 54 | +-- | remember that an attacker must not be able to predict ahead of time what a given IV will be. |
| 55 | +new :: String -> Buffer -> Nullable Buffer -> Effect Cipher |
| 56 | +new algorithm key initializationBuffer = |
| 57 | + runEffectFn3 newImpl algorithm key initializationBuffer |
| 58 | + |
| 59 | +foreign import newOptsImpl :: forall r. EffectFn4 (String) (Buffer) (Nullable Buffer) ({ | r }) (Cipher) |
| 60 | + |
| 61 | +type NewOptions = |
| 62 | + ( authTagLength :: Number |
| 63 | + ) |
| 64 | + |
| 65 | +-- | Creates and returns a Cipher object, with the given `algorithm`, `key`, `initialization vector` (IV), and `options`. |
| 66 | +-- | |
| 67 | +-- | The `options` argument controls stream behavior and is optional except when a cipher in CCM or OCB mode (e.g. 'aes-128-ccm') is used. |
| 68 | +-- | In that case, the `authTagLength` option is required and specifies the length of the authentication tag in bytes, see CCM mode. |
| 69 | +-- | In GCM mode, the `authTagLength` option is not required but can be used to set the length of the authentication tag that |
| 70 | +-- | will be returned by `getAuthTag()` and defaults to 16 bytes. |
| 71 | +-- | For `chacha20-poly1305`, the `authTagLength` option defaults to 16 bytes. |
| 72 | +-- | |
| 73 | +-- | The `algorithm` is dependent on OpenSSL, examples are 'aes192', etc. On recent OpenSSL releases, |
| 74 | +-- | `openssl list -cipher-algorithms` will display the available cipher algorithms. |
| 75 | +-- | |
| 76 | +-- | If the cipher does not need an initialization vector, it may be null. |
| 77 | +-- | |
| 78 | +-- | Initialization vectors should be unpredictable and unique; ideally, they will be cryptographically random. |
| 79 | +-- | They do not have to be secret: IVs are typically just added to ciphertext messages unencrypted. |
| 80 | +-- | It may sound contradictory that something has to be unpredictable and unique, but does not have to be secret; |
| 81 | +-- | remember that an attacker must not be able to predict ahead of time what a given IV will be. |
| 82 | +new' :: forall r trash. Row.Union r trash NewOptions => String -> Buffer -> Nullable Buffer -> { | r } -> Effect Cipher |
| 83 | +new' algorithm key initializationBuffer options = |
| 84 | + runEffectFn4 newOptsImpl algorithm key initializationBuffer options |
| 85 | + |
| 86 | +toDuplex :: Cipher -> Duplex |
| 87 | +toDuplex = unsafeCoerce |
| 88 | + |
| 89 | +foreign import finalBufImpl :: EffectFn1 (Cipher) (Buffer) |
| 90 | + |
| 91 | +-- | Once the `cipher.final()` method has been called, |
| 92 | +-- | the `Cipher` object can no longer be used to encrypt data. |
| 93 | +-- | Attempts to call `cipher.final()` more than once will result in an error being thrown. |
| 94 | +finalBuf :: Cipher -> Effect Buffer |
| 95 | +finalBuf cipher = runEffectFn1 finalBufImpl cipher |
| 96 | + |
| 97 | +foreign import finalStrImpl :: EffectFn2 (Cipher) (String) (Buffer) |
| 98 | + |
| 99 | +-- | Once the `cipher.final()` method has been called, |
| 100 | +-- | the `Cipher` object can no longer be used to encrypt data. |
| 101 | +-- | Attempts to call `cipher.final()` more than once will result in an error being thrown. |
| 102 | +-- | |
| 103 | +-- | `cipher # finalStr UTF8` |
| 104 | +finalStr :: Encoding -> Cipher -> Effect Buffer |
| 105 | +finalStr encoding cipher = runEffectFn2 finalStrImpl cipher (encodingToNode encoding) |
| 106 | + |
| 107 | +foreign import getAuthTagImpl :: EffectFn1 (Cipher) (Buffer) |
| 108 | + |
| 109 | + |
| 110 | +-- | When using an authenticated encryption mode (GCM, CCM, OCB, and chacha20-poly1305 are currently supported), |
| 111 | +-- | the `cipher.getAuthTag()` method returns a `Buffer` containing the authentication tag that has been computed from the given data. |
| 112 | +-- | |
| 113 | +-- | The `cipher.getAuthTag()` method should only be called after encryption has been completed using the `cipher.final()` method. |
| 114 | +-- | |
| 115 | +-- | If the `authTagLength` option was set during the `cipher` instance's creation, this function will return exactly `authTagLength` bytes. |
| 116 | +getAuthTag :: Cipher -> Effect Buffer |
| 117 | +getAuthTag cipher = runEffectFn1 getAuthTagImpl cipher |
| 118 | + |
| 119 | +foreign import setAADImpl :: EffectFn2 (Cipher) (Buffer) (Cipher) |
| 120 | + |
| 121 | +-- | When using an authenticated encryption mode (GCM, CCM, OCB, and chacha20-poly1305 are currently supported), |
| 122 | +-- | the `cipher.setAAD()` method sets the value used for the _additional authenticated data_ (AAD) input parameter. |
| 123 | +-- | |
| 124 | +-- | The `cipher.setAAD()` method must be called before `cipher.update()`. |
| 125 | +setAAD :: Buffer -> Cipher -> Effect Cipher |
| 126 | +setAAD buffer cipher = |
| 127 | + runEffectFn2 setAADImpl cipher buffer |
| 128 | + |
| 129 | +type SetAADOptions = |
| 130 | + ( plainTextLength :: Number |
| 131 | + , encoding :: String |
| 132 | + ) |
| 133 | + |
| 134 | +foreign import setAADOptsImpl :: forall r. EffectFn3 (Cipher) (Buffer) ({ | r }) (Cipher) |
| 135 | + |
| 136 | +-- | When using an authenticated encryption mode (GCM, CCM, OCB, and chacha20-poly1305 are currently supported), |
| 137 | +-- | the `cipher.setAAD()` method sets the value used for the additional authenticated data (AAD) input parameter. |
| 138 | +-- | |
| 139 | +-- | The `plaintextLength` option is optional for GCM and OCB. When using CCM, the `plaintextLength` option must |
| 140 | +-- | be specified and its value must match the length of the plaintext in bytes. See CCM mode. |
| 141 | +-- | |
| 142 | +-- | The `cipher.setAAD()` method must be called before `cipher.update()`. |
| 143 | +setAAD' |
| 144 | + :: forall r trash |
| 145 | + . Row.Union r trash SetAADOptions |
| 146 | + => Buffer |
| 147 | + -> { | r } |
| 148 | + -> Cipher |
| 149 | + -> Effect Cipher |
| 150 | +setAAD' buffer r cipher = |
| 151 | + runEffectFn3 setAADOptsImpl cipher buffer r |
| 152 | + |
| 153 | +foreign import setAutoPaddingImpl :: EffectFn1 (Cipher) (Cipher) |
| 154 | + |
| 155 | +-- | Sets the `cipher`'s `autoPadding` to `true`. |
| 156 | +-- | |
| 157 | +-- | When using block encryption algorithms, the `Cipher` class will automatically |
| 158 | +-- | add padding to the input data to the appropriate block size. To disable the default padding call `cipher.setAutoPadding(false)`. |
| 159 | +-- | |
| 160 | +-- | When `autoPadding` is `false`, the length of the entire input data must be a |
| 161 | +-- | multiple of the cipher's block size or `cipher.final()` will throw an error. |
| 162 | +-- | Disabling automatic padding is useful for non-standard padding, for instance using `0x0` instead of PKCS padding. |
| 163 | +-- | |
| 164 | +-- | The `cipher.setAutoPadding()` method must be called before `cipher.final()`. |
| 165 | +setAutoPadding :: Cipher -> Effect Cipher |
| 166 | +setAutoPadding cipher = runEffectFn1 setAutoPaddingImpl cipher |
| 167 | + |
| 168 | +foreign import setAutoPaddingBoolImpl :: EffectFn2 (Cipher) (Boolean) (Cipher) |
| 169 | + |
| 170 | +-- | When using block encryption algorithms, the `Cipher` class will automatically |
| 171 | +-- | add padding to the input data to the appropriate block size. To disable the default padding call `cipher.setAutoPadding(false)`. |
| 172 | +-- | |
| 173 | +-- | When `autoPadding` is `false`, the length of the entire input data must be a |
| 174 | +-- | multiple of the cipher's block size or `cipher.final()` will throw an error. |
| 175 | +-- | Disabling automatic padding is useful for non-standard padding, for instance using `0x0` instead of PKCS padding. |
| 176 | +-- | |
| 177 | +-- | The `cipher.setAutoPadding()` method must be called before `cipher.final()`. |
| 178 | +setAutoPadding' :: Boolean -> Cipher -> Effect Cipher |
| 179 | +setAutoPadding' boolean cipher = |
| 180 | + runEffectFn2 setAutoPaddingBoolImpl cipher boolean |
| 181 | + |
| 182 | +foreign import updateBufBufImpl :: EffectFn2 (Cipher) (Buffer) (Buffer) |
| 183 | + |
| 184 | +-- | Updates the `cipher`. This variant's input is `Buffer` and output is `Buffer`. |
| 185 | +-- | |
| 186 | +-- | The `cipher.update()` method can be called multiple times with new data until `cipher.final()` is called. Calling `cipher.update()` after `cipher.final()` will result in an error being thrown. |
| 187 | +updateBufBuf :: Buffer -> Cipher -> Effect Buffer |
| 188 | +updateBufBuf buffer cipher = |
| 189 | + runEffectFn2 updateBufBufImpl cipher buffer |
| 190 | + |
| 191 | +foreign import updateBufStrImpl :: EffectFn3 (Cipher) (Buffer) (String) (String) |
| 192 | + |
| 193 | +-- | Updates the `cipher`. This variant's input is `Buffer` and output is `String` using the speified encoding. |
| 194 | +-- | |
| 195 | +-- | The `cipher.update()` method can be called multiple times with new data until `cipher.final()` is called. Calling `cipher.update()` after `cipher.final()` will result in an error being thrown. |
| 196 | +updateBufStr :: Buffer -> Encoding -> Cipher -> Effect String |
| 197 | +updateBufStr buffer outputEncoding cipher = |
| 198 | + runEffectFn3 updateBufStrImpl cipher buffer (encodingToNode outputEncoding) |
| 199 | + |
| 200 | +foreign import updateStrBufImpl :: EffectFn3 (Cipher) (String) (String) (Buffer) |
| 201 | + |
| 202 | +-- | Updates the `cipher`. This variant's input is `String` using the specified encoding and output is `Buffer`. |
| 203 | +-- | |
| 204 | +-- | The `cipher.update()` method can be called multiple times with new data until `cipher.final()` is called. Calling `cipher.update()` after `cipher.final()` will result in an error being thrown. |
| 205 | +updateStrBuf :: String -> Encoding -> Cipher -> Effect Buffer |
| 206 | +updateStrBuf string inputEncoding cipher = |
| 207 | + runEffectFn3 updateStrBufImpl cipher string (encodingToNode inputEncoding) |
| 208 | + |
| 209 | +foreign import updateStrStrImpl :: EffectFn4 (Cipher) (String) (String) (String) (String) |
| 210 | + |
| 211 | +-- | Updates the `cipher`. This variant's input is `String` using the first specified encoding and output is `String` using the second specified encoding. |
| 212 | +-- | |
| 213 | +-- | The `cipher.update()` method can be called multiple times with new data until `cipher.final()` is called. Calling `cipher.update()` after `cipher.final()` will result in an error being thrown. |
| 214 | +-- | ``` |
| 215 | +-- | updateStr cipher input inputEncoding outputEncoding |
| 216 | +-- | ``` |
| 217 | +updateStrStr :: String -> Encoding -> Encoding -> Cipher -> Effect String |
| 218 | +updateStrStr string inputEncoding outputEncoding cipher = |
| 219 | + runEffectFn4 updateStrStrImpl cipher string (encodingToNode inputEncoding) (encodingToNode outputEncoding) |
| 220 | + |
0 commit comments