|
| 1 | +// Copyright 2014-present runtime.js project authors |
| 2 | +// |
| 3 | +// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +// you may not use this file except in compliance with the License. |
| 5 | +// You may obtain a copy of the License at |
| 6 | +// |
| 7 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +// |
| 9 | +// Unless required by applicable law or agreed to in writing, software |
| 10 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +// See the License for the specific language governing permissions and |
| 13 | +// limitations under the License. |
| 14 | + |
| 15 | +'use strict'; |
| 16 | + |
| 17 | +var lib = require('./resources').libsodium; |
| 18 | +var constants = lib.crypto_constants(); |
| 19 | + |
| 20 | +// Helper function to convert hex results into Uint8Arrays. |
| 21 | +function hexToU8(hexStr) { |
| 22 | + var u8 = new Uint8Array(hexStr.length/2); |
| 23 | + for (var i = 0; i < (hexStr.length/2); i++) { |
| 24 | + u8[i] = parseInt(hexStr.substr(i*2, 2), 16); |
| 25 | + } |
| 26 | + return u8; |
| 27 | +} |
| 28 | + |
| 29 | +function throwErr(funcName, argName, i, msg) { |
| 30 | + throw new Error(funcName + ': ' + argName + ' (argument ' + i + ') ' + msg + '.'); |
| 31 | +} |
| 32 | + |
| 33 | +function stringOrU8(arg, funcName, argName, i, msg) { |
| 34 | + var ret = arg; |
| 35 | + if (!(arg instanceof Uint8Array)) { |
| 36 | + if (typeof arg === 'string' || (arg instanceof String)) { |
| 37 | + ret = new Uint8Array(arg.length); |
| 38 | + for (var i = 0; i < arg.length; i++) { |
| 39 | + ret[i] = arg.charCodeAt(i); |
| 40 | + } |
| 41 | + } else { |
| 42 | + throwErr(funcName, argName, i, (msg ? msg : 'must be a string or Uint8Array')); |
| 43 | + } |
| 44 | + } |
| 45 | + return ret; |
| 46 | +} |
| 47 | + |
| 48 | +function isNumber(arg, funcName, argName, i, msg) { |
| 49 | + if (typeof arg !== 'number' && !(arg instanceof Number)) { |
| 50 | + throwErr(funcName, argName, i, (msg ? msg : 'must be a number')); |
| 51 | + } |
| 52 | + return arg; |
| 53 | +} |
| 54 | + |
| 55 | +module.exports = { |
| 56 | + crypto_secretbox_easy: function(data, key) { |
| 57 | + var dataArr = stringOrU8(data, 'crypto_secretbox_easy', 'data', 0); |
| 58 | + var keyArr = stringOrU8(key, 'crypto_secretbox_easy', 'key', 1); |
| 59 | + |
| 60 | + var nonceArr = runtime.random.getRandomValues(constants.crypto_secretbox_NONCEBYTES); |
| 61 | + |
| 62 | + var cipher = lib.crypto_secretbox_easy(dataArr, keyArr, nonceArr); |
| 63 | + if (!cipher) { |
| 64 | + throw new Error('crypto_secretbox_easy: error creating box.'); |
| 65 | + } |
| 66 | + return { |
| 67 | + ciphertext: hexToU8(cipher), |
| 68 | + nonce: nonceArr |
| 69 | + }; |
| 70 | + }, |
| 71 | + crypto_secretbox_open_easy: function(cipher, key, nonce) { |
| 72 | + var cipherArr = stringOrU8(cipher, 'crypto_secretbox_open_easy', 'cipher', 0); |
| 73 | + var keyArr = stringOrU8(key, 'crypto_secretbox_open_easy', 'key', 1); |
| 74 | + var nonceArr = stringOrU8(nonce, 'crypto_secretbox_open_easy', 'nonce', 2); |
| 75 | + |
| 76 | + var decipher = lib.crypto_secretbox_open_easy(cipherArr, keyArr, nonceArr); |
| 77 | + if (!decipher) { |
| 78 | + throw new Error('crypto_secretbox_open_easy: error decrypting box.'); |
| 79 | + } |
| 80 | + return hexToU8(decipher); |
| 81 | + }, |
| 82 | + crypto_auth: function(data, key) { |
| 83 | + var dataArr = stringOrU8(data, 'crypto_auth', 'data', 0); |
| 84 | + var keyArr; |
| 85 | + if (key === null || key === undefined) { |
| 86 | + keyArr = runtime.random.getRandomValues(constants.crypto_auth_KEYBYTES); |
| 87 | + } else { |
| 88 | + keyArr = stringOrU8(key, 'crypto_auth', 'key', 1, 'must be unspecified, a string, or Uint8Array'); |
| 89 | + } |
| 90 | + |
| 91 | + var mac = lib.crypto_auth(dataArr, keyArr); |
| 92 | + if (!mac) { |
| 93 | + throw new Error('crypto_auth: error creating tag.'); |
| 94 | + } |
| 95 | + return { |
| 96 | + mac: hexToU8(mac), |
| 97 | + key: keyArr |
| 98 | + }; |
| 99 | + }, |
| 100 | + crypto_auth_verify: function(mac, key, data) { |
| 101 | + var macArr = stringOrU8(mac, 'crypto_auth_verify', 'MAC', 0); |
| 102 | + var keyArr = stringOrU8(key, 'crypto_auth_verify', 'key', 1); |
| 103 | + var dataArr = stringOrU8(data, 'crypto_auth_verify', 'data', 2); |
| 104 | + |
| 105 | + return lib.crypto_auth_verify(macArr, keyArr, dataArr); |
| 106 | + }, |
| 107 | + crypto_aead_chacha20poly1305_encrypt: function(data, key, addData) { |
| 108 | + var dataArr = stringOrU8(data, 'crypto_aead_chacha20poly1305_encrypt', 'data', 0); |
| 109 | + var keyArr = stringOrU8(key, 'crypto_aead_chacha20poly1305_encrypt', 'key', 1); |
| 110 | + var addDataArr = stringOrU8(key, 'crypto_aead_chacha20poly1305_encrypt', 'additional data', 2); |
| 111 | + |
| 112 | + var nonceArr = runtime.random.getRandomValues(constants.crypto_aead_chacha20poly1305_NPUBBYTES); |
| 113 | + |
| 114 | + var cipher = lib.crypto_aead_chacha20poly1305_encrypt(dataArr, keyArr, nonceArr, addDataArr); |
| 115 | + if (!cipher) { |
| 116 | + throw new Error('crypto_aead_chacha20poly1305_encrypt: error creating box.'); |
| 117 | + } |
| 118 | + return { |
| 119 | + ciphertext: hexToU8(cipher.ciphertext), |
| 120 | + nonce: nonceArr, |
| 121 | + length: cipher.ciphertext_len |
| 122 | + }; |
| 123 | + }, |
| 124 | + crypto_aead_chacha20poly1305_decrypt: function(cipher, cipherLength, key, nonce, addData) { |
| 125 | + var cipherArr = stringOrU8(cipher, 'crypto_aead_chacha20poly1305_decrypt', 'cipher', 0); |
| 126 | + var cipherLen = isNumber(cipherLength, 'crypto_aead_chacha20poly1305_decrypt', 'cipher length', 1); |
| 127 | + var keyArr = stringOrU8(key, 'crypto_aead_chacha20poly1305_decrypt', 'key', 2); |
| 128 | + var nonceArr = stringOrU8(nonce, 'crypto_aead_chacha20poly1305_decrypt', 'nonce', 3); |
| 129 | + var addDataArr = stringOrU8(key, 'crypto_aead_chacha20poly1305_decrypt', 'additional data', 4); |
| 130 | + |
| 131 | + var decipher = lib.crypto_aead_chacha20poly1305_decrypt(cipherArr, keyArr, nonceArr, addDataArr, cipherLen); |
| 132 | + if (!decipher) { |
| 133 | + throw new Error('crypto_aead_chacha20poly1305_decrypt: error decrypting box.'); |
| 134 | + } |
| 135 | + return hexToU8(decipher.deciphertext); |
| 136 | + }, |
| 137 | + crypto_aead_aes256gcm_is_available: function() { |
| 138 | + // AES-256 GCM isn't supported on QEMU's system-x86_64 |
| 139 | + return false; |
| 140 | + }, |
| 141 | + crypto_aead_aes256gcm_encrypt: function(data, key, addData) { |
| 142 | + // AES-256 GCM isn't supported on QEMU's system-x86_64 |
| 143 | + throw new Error('CPU not supported'); |
| 144 | + /*var dataArr = stringOrU8(data, 'crypto_aead_aes256gcm_encrypt', 'data', 0); |
| 145 | + var keyArr = stringOrU8(key, 'crypto_aead_aes256gcm_encrypt', 'key', 1); |
| 146 | + var addDataArr = stringOrU8(key, 'crypto_aead_aes256gcm_encrypt', 'additional data', 2); |
| 147 | + var nonceArr = runtime.random.getRandomValues(constants.crypto_aead_aes256gcm_NPUBBYTES); |
| 148 | + var cipher = lib.crypto_aead_aes256gcm_encrypt(dataArr, keyArr, nonceArr, addDataArr); |
| 149 | + if (!cipher) { |
| 150 | + throw new Error('crypto_aead_aes256gcm_encrypt: error creating box.'); |
| 151 | + } |
| 152 | + if (cipher instanceof Error) throw cipher; |
| 153 | + return { |
| 154 | + ciphertext: hexToU8(cipher.ciphertext), |
| 155 | + nonce: nonceArr, |
| 156 | + length: cipher.ciphertext_len |
| 157 | + };*/ |
| 158 | + }, |
| 159 | + crypto_aead_aes256gcm_decrypt: function(cipher, cipherLength, key, nonce, addData) { |
| 160 | + // AES-256 GCM isn't supported on QEMU's system-x86_64 |
| 161 | + throw new Error('CPU not supported'); |
| 162 | + /*var cipherArr = stringOrU8(cipher, 'crypto_aead_aes256gcm_decrypt', 'cipher', 0); |
| 163 | + var cipherLen = isNumber(cipherLength, 'crypto_aead_aes256gcm_decrypt', 'cipher length', 1); |
| 164 | + var keyArr = stringOrU8(key, 'crypto_aead_aes256gcm_decrypt', 'key', 2); |
| 165 | + var nonceArr = stringOrU8(nonce, 'crypto_aead_aes256gcm_decrypt', 'nonce', 3); |
| 166 | + var addDataArr = stringOrU8(key, 'crypto_aead_aes256gcm_decrypt', 'additional data', 4); |
| 167 | + var decipher = lib.crypto_aead_aes256gcm_decrypt(cipherArr, keyArr, nonceArr, addDataArr, cipherLen); |
| 168 | + if (!decipher) { |
| 169 | + throw new Error('crypto_aead_aes256gcm_decrypt: error decrypting box.'); |
| 170 | + } |
| 171 | + if (decipher instanceof Error) throw decipher; |
| 172 | + return hexToU8(decipher.deciphertext);*/ |
| 173 | + }, |
| 174 | + crypto_box_easy: function(data, pk, sk) { |
| 175 | + var dataArr = stringOrU8(data, 'crypto_box_easy', 'data', 0); |
| 176 | + var pkArr = stringOrU8(pk, 'crypto_box_easy', 'public key', 1); |
| 177 | + var skArr = stringOrU8(sk, 'crypto_box_easy', 'secret key', 2); |
| 178 | + |
| 179 | + var nonceArr = runtime.random.getRandomValues(constants.crypto_box_NONCEBYTES); |
| 180 | + |
| 181 | + var cipher = lib.crypto_box_easy(dataArr, pkArr, skArr, nonceArr); |
| 182 | + if (!cipher) { |
| 183 | + throw new Error('crypto_box_easy: error creating box.'); |
| 184 | + } |
| 185 | + return { |
| 186 | + ciphertext: hexToU8(cipher), |
| 187 | + nonce: nonceArr |
| 188 | + }; |
| 189 | + }, |
| 190 | + crypto_box_open_easy: function(cipher, pk, sk, nonce) { |
| 191 | + var cipherArr = stringOrU8(cipher, 'crypto_box_open_easy', 'cipher', 0); |
| 192 | + var pkArr = stringOrU8(pk, 'crypto_box_open_easy', 'public key', 1); |
| 193 | + var skArr = stringOrU8(sk, 'crypto_box_open_easy', 'secret key', 1); |
| 194 | + var nonceArr = stringOrU8(nonce, 'crypto_box_open_easy', 'nonce', 2); |
| 195 | + |
| 196 | + var decipher = lib.crypto_box_open_easy(cipherArr, pkArr, skArr, nonceArr); |
| 197 | + if (!decipher) { |
| 198 | + throw new Error('crypto_box_open_easy: error decrypting box.'); |
| 199 | + } |
| 200 | + return hexToU8(decipher); |
| 201 | + }, |
| 202 | + crypto_box_keypair: function() { |
| 203 | + var keypair = lib.crypto_box_keypair(); |
| 204 | + if (typeof keypair === 'string') throw new Error(keypair); |
| 205 | + return keypair; |
| 206 | + }, |
| 207 | + crypto_box_seed_keypair: function(seed) { |
| 208 | + var seedArr = stringOrU8(seed, 'crypto_box_seed_keypair', 'seed', 0); |
| 209 | + |
| 210 | + var keypair = lib.crypto_box_seed_keypair(seedArr); |
| 211 | + if (typeof keypair === 'string') throw new Error(keypair); |
| 212 | + return keypair; |
| 213 | + } |
| 214 | +}; |
| 215 | + |
| 216 | +var justConvertHex = [ |
| 217 | + { |
| 218 | + funcName: 'randombytes_buf', |
| 219 | + errorInfo: 'error generating randomness.', |
| 220 | + dataVerifier: isNumber, |
| 221 | + argName: 'buffer length' |
| 222 | + }, |
| 223 | + { |
| 224 | + funcName: 'crypto_generichash', |
| 225 | + errorInfo: 'error calculating hash.' |
| 226 | + }, |
| 227 | + { |
| 228 | + funcName: 'crypto_hash_sha256', |
| 229 | + errorInfo: 'error calculating hash.' |
| 230 | + }, |
| 231 | + { |
| 232 | + funcName: 'crypto_hash_sha512', |
| 233 | + errorInfo: 'error calculating hash.' |
| 234 | + } |
| 235 | +]; |
| 236 | + |
| 237 | +for (var i = 0; i < justConvertHex.length; i++) { |
| 238 | + (function(i) { |
| 239 | + var f = justConvertHex[i]; |
| 240 | + module.exports[f.funcName] = function(data) { |
| 241 | + var result = lib[f.funcName]((f.dataVerifier ? f.dataVerifier : stringOrU8)(data, f.funcName, (f.argName ? f.argName : 'data'), 0)); |
| 242 | + if (!result) { |
| 243 | + throw new Error(f.funcName + ': ' + (f.errorInfo ? f.errorInfo : 'unknown error.')); |
| 244 | + } |
| 245 | + return hexToU8(result); |
| 246 | + } |
| 247 | + })(i); |
| 248 | +} |
0 commit comments