Weak Encryption in js-xlsx - encryption

Fortify scan check is showing weak encryption js-xlsx file.
It is showing that RC4 is a vulnerable and weak encryption method.
It is recommended to use strong encryption instead of RC4.
Below is the code for rc4 encryption.
function parse_RC4CryptoHeader(blob, length) {
var o = {};
var vers = o.EncryptionVersionInfo = parse_CRYPTOVersion(blob, 4);
length -= 4;
if (vers.Minor != 2)
throw new Error('unrecognized minor version code: ' + vers.Minor);
if (vers.Major > 4 || vers.Major < 2)
throw new Error('unrecognized major version code: ' + vers.Major);
o.Flags = blob.read_shift(4);
length -= 4;
var sz = blob.read_shift(4);
length -= 4;
o.EncryptionHeader = parse_EncryptionHeader(blob, sz);
length -= sz;
o.EncryptionVerifier = parse_EncryptionVerifier(blob, length);
return o;
}
/* [MS-OFFCRYPTO] 2.3.6.1 RC4 Encryption Header */
function parse_RC4Header(blob) {
var o = {};
var vers = o.EncryptionVersionInfo = parse_CRYPTOVersion(blob, 4);
if (vers.Major != 1 || vers.Minor != 1)
throw 'unrecognized version code ' + vers.Major + ' : ' + vers.Minor;
o.Salt = blob.read_shift(16);
o.EncryptedVerifier = blob.read_shift(16);
o.EncryptedVerifierHash = blob.read_shift(16);
return o;
}
Can anyone help me to convert these methods into a secure encryption algorithm?

Related

What is missing from the AES Validation Standard Pseudocode for the Monte Carlo Tests?

I'm trying to use the prescribed validation procedure for AES-128 in CBC mode, as defined in the NIST AESAVS standard. One of the more important parts of the test suite is the Monte Carlo test, which provides an algorithm for generating many 10000 pseudorandom tests cases such that it is unlikely that a hardcoded circuit could fake AES. The algorithm pseudocode therein appears to be taking some liberties with variable scope and definition, so I am hoping someone could help me fill in the missing information to interpret this correctly.
The verbatim algorithm for the 128-bit key case is as follows:
Key[0] = Key
IV[0] = IV
PT[0] = PT
For i = 0 to 99
Output Key[i]
Output IV[i]
Output PT[0]
For j = 0 to 999
If ( j=0 )
CT[j] = AES(Key[i], IV[i], PT[j])
PT[j+1] = IV[i]
Else
CT[j] = AES(Key[i], PT[j])
PT[j+1] = CT[j-1]
Output CT[j]
Key[i+1] = Key[i] xor CT[j]
IV[i+1] = CT[j]
PT[0] = CT[j-1]
For the above pseudocode, starting with these initial values:
Key = 9dc2c84a37850c11699818605f47958c
IV = 256953b2feab2a04ae0180d8335bbed6
PT = 2e586692e647f5028ec6fa47a55a2aab
The first three iterations of the outer loop should output:
KEY = 9dc2c84a37850c11699818605f47958c
IV = 256953b2feab2a04ae0180d8335bbed6
PLAINTEXT = 2e586692e647f5028ec6fa47a55a2aab
CIPHERTEXT = 1b1ebd1fc45ec43037fd4844241a437f
KEY = 86dc7555f3dbc8215e6550247b5dd6f3
IV = 1b1ebd1fc45ec43037fd4844241a437f
PLAINTEXT = c1b77ed52521525f0a4ba341bdaf51d9
CIPHERTEXT = bf43583a665fa45fdee831243a16ea8f
KEY = 399f2d6f95846c7e808d6100414b3c7c
IV = bf43583a665fa45fdee831243a16ea8f
PLAINTEXT = 7cbeea19157ec7bbf6289e2dff5e8ee4
CIPHERTEXT = 5464e1900f81e06f67139456da25fc09
It looks like we are using j outside of the inner loop, which I believe is the source of the confusion. I had originally assumed that this meant whatever the final value of the ciphertext CT was (CT[999]), which would lead me to believe that the plaintext for the next outer loop PT[0] is initialized to CT[998]. However, this interpretation doesn't match the expected outputs given.
I also thought that maybe brackets are not indicating an array of values here, but rather they represent the time steps relative to now. However, this also makes referencing j outside of the loop confusing. If the loop has expired, then is i or j the current time?
Am I missing some crucial step here? Is there a typo (there is no errata in the document)?
Could anyone with some experience on the matter comment on the appropriate interpretation?
Some months ago I tried to get the AES CBC MonteCarlo running on Java. I encountered the same problems but in the end I could find a complete and running solution that meets the official NIST vector results.
Before I start - your inital test vector seems to be an own vector but not the one provided by NIST - here is the link to the official NIST-website with all AES testvectors:
NIST-Website: https://csrc.nist.gov/Projects/cryptographic-algorithm-validation-program/Block-Ciphers Montecarlo testvectors: https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/aes/aesmct.zip
My test will start with these data:
[ENCRYPT]
COUNT = 0
KEY = 8809e7dd3a959ee5d8dbb13f501f2274
IV = e5c0bb535d7d54572ad06d170a0e58ae
PLAINTEXT = 1fd4ee65603e6130cfc2a82ab3d56c24
CIPHERTEXT = b127a5b4c4692d87483db0c3b0d11e64
and the function uses a "double" byte array for the inner and outer loop. I do not present the complete sourcode here on SO but the complete code is available in my GitHub repository https://github.com/java-crypto/Known_Answer_Tests with many other tests and test vector files. The encryption/decryption has to be done with NoPadding - don't use AES in default mode as in most
cases it would run with PKCS#5/#7 padding.
If you like you can run the code online (reduced to AES CBC 128 MonteCarlo) here: https://repl.it/#javacrypto/AesCbcMonteCarloTest#Main.java
The program will run the complete encryption and decryption test and does an additional cross-check (means the encryption result is checked
by a decryption and vice versa).
As it is some months ago that I took care of this I'm just offering my solution in Java code - hopefully it helps you in
your understanding of the NIST test procedure.
public static byte[] aes_cbc_mct_encrypt(byte[] PLAINTEXT, byte[] KEYinit, byte[] IVinit) throws Exception {
int i = 0; // outer loop
int j = 0; // inner loop
byte[][] KEY = new byte[101][128];
byte[][] IV = new byte[1001][128];
byte[][] PT = new byte[1001][128]; // plaintext
byte[][] CT = new byte[1001][128]; // ciphertext
byte[] CTsave = new byte[256]; // nimmt den letzten ct fuer nutzung als neuen iv auf
// init
int KEYLENGTH = KEYinit.length * 8;
KEY[0] = KEYinit;
IV[0] = IVinit;
PT[0] = PLAINTEXT;
for (i = 0; i < 100; i++) {
for (j = 0; j < 1000; j++) {
if (j == 0) {
CT[j] = aes_cbc_encrypt(PT[j], KEY[i], IV[i]);
CTsave = CT[j]; // sicherung fuer naechsten iv
PT[j + 1] = IV[i];
} else {
IV[i] = CTsave;
CT[j] = aes_cbc_encrypt(PT[j], KEY[i], IV[i]);
CTsave = CT[j];
PT[j + 1] = CT[j - 1];
}
}
j = j - 1; // correction of loop counter
if (KEYLENGTH == 128) {
KEY[i + 1] = xor(KEY[i], CT[j]);
}
if (KEYLENGTH == 192) {
KEY[i + 1] = xor192(KEY[i], CT[j - 1], CT[j]);
}
if (KEYLENGTH == 256) {
KEY[i + 1] = xor256(KEY[i], CT[j - 1], CT[j]);
}
IV[i + 1] = CT[j];
PT[0] = CT[j - 1];
ctCalculated[i] = CT[j].clone();
}
return CT[j];
}
public static byte[] xor(byte[] a, byte[] b) {
// nutzung in der mctCbcEncrypt und mctCbcDecrypt methode
byte[] result = new byte[Math.min(a.length, b.length)];
for (int i = 0; i < result.length; i++) {
result[i] = (byte) (((int) a[i]) ^ ((int) b[i]));
}
return result;
}
public static byte[] aes_cbc_encrypt(byte[] plaintextByte, byte[] keyByte, byte[] initvectorByte) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, BadPaddingException, IllegalBlockSizeException {
byte[] ciphertextByte = null;
SecretKeySpec keySpec = new SecretKeySpec(keyByte, "AES");
IvParameterSpec ivKeySpec = new IvParameterSpec(initvectorByte);
Cipher aesCipherEnc = Cipher.getInstance("AES/CBC/NOPADDING");
aesCipherEnc.init(Cipher.ENCRYPT_MODE, keySpec, ivKeySpec);
ciphertextByte = aesCipherEnc.doFinal(plaintextByte);
return ciphertextByte;
}

cryptoJS AES encrypt returning a wrong decryption [duplicate]

How to convert from Hex string to ASCII string in JavaScript?
Ex:
32343630 it will be 2460
function hex2a(hexx) {
var hex = hexx.toString();//force conversion
var str = '';
for (var i = 0; i < hex.length; i += 2)
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
return str;
}
hex2a('32343630'); // returns '2460'
Another way to do it (if you use Node.js):
var input = '32343630';
const output = Buffer.from(input, 'hex');
log(input + " -> " + output); // Result: 32343630 -> 2460
For completeness sake the reverse function:
function a2hex(str) {
var arr = [];
for (var i = 0, l = str.length; i < l; i ++) {
var hex = Number(str.charCodeAt(i)).toString(16);
arr.push(hex);
}
return arr.join('');
}
a2hex('2460'); //returns 32343630
You can use this..
var asciiVal = "32343630".match(/.{1,2}/g).map(function(v){
return String.fromCharCode(parseInt(v, 16));
}).join('');
document.write(asciiVal);
** for Hexa to String**
let input = '32343630';
Note : let output = new Buffer(input, 'hex'); // this is deprecated
let buf = Buffer.from(input, "hex");
let data = buf.toString("utf8");
I found a useful function present in web3 library.
var hexString = "0x1231ac"
string strValue = web3.toAscii(hexString)
Update: Newer version of web3 has this function in utils
The functions now resides in utils:
var hexString = "0x1231ac"
string strValue = web3.utils.hexToAscii(hexString)
I've found that the above solution will not work if you have to deal with control characters like 02 (STX) or 03 (ETX), anything under 10 will be read as a single digit and throw off everything after. I ran into this problem trying to parse through serial communications. So, I first took the hex string received and put it in a buffer object then converted the hex string into an array of the strings like so:
buf = Buffer.from(data, 'hex');
l = Buffer.byteLength(buf,'hex');
for (i=0; i<l; i++){
char = buf.toString('hex', i, i+1);
msgArray.push(char);
}
Then .join it
message = msgArray.join('');
then I created a hexToAscii function just like in #Delan Azabani's answer above...
function hexToAscii(str){
hexString = str;
strOut = '';
for (x = 0; x < hexString.length; x += 2) {
strOut += String.fromCharCode(parseInt(hexString.substr(x, 2), 16));
}
return strOut;
}
then called the hexToAscii function on 'message'
message = hexToAscii(message);
This approach also allowed me to iterate through the array and slice into the different parts of the transmission using the control characters so I could then deal with only the part of the data I wanted.
Hope this helps someone else!
console.log(
"68656c6c6f20776f726c6421".match(/.{1,2}/g).reduce((acc,char)=>acc+String.fromCharCode(parseInt(char, 16)),"")
)
An optimized version of the implementation of the reverse function proposed by #michieljoris (according to the comments of #Beterraba and #Mala):
function a2hex(str) {
var hex = '';
for (var i = 0, l = str.length; i < l; i++) {
var hexx = Number(str.charCodeAt(i)).toString(16);
hex += (hexx.length > 1 && hexx || '0' + hexx);
}
return hex;
}
alert(a2hex('2460')); // display 32343630
I use this one, it seems more clear to me as I also receive data with spaces like '30 31 38 30 38 30' and the output is 018080
hexToString(hex: string): string {
return hex.split(' ').map(s => string.fromCharCode(parseInt(s,16))).join('');
}

Decrypting an Affine Cipher with Modulus

I'm trying to decrypt the ciphertext vczkh which I know was encoded using an affine cipher with the equation 7x + 8(mod 26). This makes my decryption function p = (c – b) * a^-1 (mod 26) where b = 8, a = 7, c = number corresponding with cipher character starting from 0, and p is the same for plaintext. Since I can't have a fraction I calculated that 11 is congruent to 7 making my function p = (c - 8) * 11. Running this for all five letters gives me NMFWP but I know the answer is supposed to be NOVEL. I do not know what I'm doing wrong.
In order to decrypt and affine cipher given a and b you need to use Dk = a^-1(y-b) mod m where m depends in the cardinality of the alphabet you are currently using (English 26, Italian 21, ...), a^-1 = m-a and k = (a, b).
For instance, vczkh with a=7 and b=8 gets decrypted into nqlmh given a^-1 = m - a = 26 - 7 = 19
So for v, since v is at position 21 in the english alphabet:
v -> 19(21-8) mod 26 -> 247 mod 26 -> 13 that corresponds to n
Here's a Javascript script I wrote
//Getting Args from console
var args = {
"operation" : process.argv[2],
"a" : parseInt(process.argv[3]),
"b" : parseInt(process.argv[4]),
"word" : process.argv[5]
};
var encryptedWord = [];
var decryptedWord = [];
if(!args.operation || !args.a || !args.b || !args.word){
console.log("Arguments are missing, please, use: node \"encrypt/decrypt\" a b word");
return;
} else {
if(typeof args.a === 'number' || typeof args.b === 'number'){
if(typeof args.word !== 'string'){
console.log("Word must be a string");
return;
} else {
// If a and m are coprimes
if(gcdCalc(args.a, 26) === 1){
if(args.operation === "encrypt"){
encryptWord().then(function(encrWord){
console.log("Word "+args.word+" got encrypted into "+encrWord);
});
} else if(args.operation === "decrypt"){
decryptWord().then(function(decrWord){
console.log("Ciphetext "+args.word+" got decrypted into "+decrWord);
});
} else {
console.log("Invalid operation specified. Use encrypt or decrypt.");
return;
}
} else {
console.log("a "+args.a+ " and m 26 are not coprimes");
return;
}
}
} else {
console.log("You must assign an Integer number to a and b. Remember that a must be coprime with m (26)");
return;
}
}
function gcdCalc(a, b) {
if (b) {
return gcdCalc(b, a % b);
} else {
return Math.abs(a);
}
};
function encryptWord(){
return new Promise( function(resolve){
var chars = args.word.split("");
var currInt = 0;
var currEnc = "";
chars.forEach( function( currChar){
currInt = parseInt(currChar, 36) - 10;
// E(a,b)(n) = an + b mod 26
currEnc = mod((args.a * currInt + args.b), 26);
encryptedWord.push(String.fromCharCode(97 + currEnc));
});
return resolve(encryptedWord.join(""));
});
}
function decryptWord(){
return new Promise( function(resolve){
var chars = args.word.split("");
var currInt = 0;
var currEnc = "";
//a^-1 = m - a
var a_1 = 26 - args.a;
chars.forEach( function( currChar){
currInt = parseInt(currChar, 36) - 10;
// D(y) = a^-1 * (y - b) mod 26
currEnc = mod((a_1 * (currInt - args.b)), 26);
decryptedWord.push(String.fromCharCode(97 + currEnc));
});
return resolve(decryptedWord.join(""));
});
}
function mod(n, m) {
var remain = n % m;
return Math.floor(remain >= 0 ? remain : remain + m);
};
To run it you need node, then, you can use it:
Encrypt : node affine-cipher.js encrypt 5 8 affine that becomes ihhwvc
Decrypt : node affine-cipher.js decrypt 5 8 ihhwvc that becomes affine
Notice that a and m MUST be coprime. For instance gcd(a, m) MUST be 1.
The total number of possible keys is 312 since a may vary in 12 different numbers which are coprime with 26 and b may vary in all 26 different number, so 12*26=312.
Hope I've been helpful.

Encryption Algorithm returning different value in php and javascript

In PHP
<?php
$token = 'uid=pratik#gmail.com|ts=1412917909|hash=r1xWbgfHUxDLlppGYuOKQJdIM1MTrkryEArkMQx9ERw=|url=http://myintranet.com';
$key = 'a1cbbb6eb5cb2c1c27a9f02a4434d3af';
$token = mb_convert_encoding( $token ,'UTF-16LE' );
$blockSize = 16;
$pad = $blockSize - (strlen($token) % $blockSize);
$token .= str_repeat(chr($pad), $pad);
$token = mcrypt_encrypt(MCRYPT_RIJNDAEL_128,$key, $token, MCRYPT_MODE_CBC, $iv);
$token = base64_encode($token);
echo "\n This is the token $token \n";
?>
Output -:
This is the token TXz3UEgAdjGhyriNGcMJBUk4QcW3dA7rttzjbKztw19X8bSIMDZt8s6uSQy2OP5QcSpJuReKv73wFXzPyCXt05CNY6XWlx9Lfrv6Nosj0+4mHdD7/Wvx0QWqxuuv5qv4sgtgSif59Wy/ZAoYhfH8yzN/3hWnx6zzOrV6jxyDttmffk1zcBwtJ3X41mMVbPLOd1/2K3ZYxCcJ1VxESFDNB4N1okvGMRkCM0tL77oZiKv+n6CP9FEgKivCfvytFB8JWc9K++8vbLdV/iGgkEa7h0pfAZtYpryQQjFzqLx8NSQ=
In Javascript
'use strict';
var CryptoJS = require("crypto-js");
String.prototype.repeat = function( num )
{
return new Array( num + 1 ).join( this );
}
function encodeUTF16LE(str) {
var out, i, len, c;
var char2, char3;
out = "";
len = str.length;
i = 0;
while(i < len) {
c = str.charCodeAt(i++);
switch(c >> 4)
{
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
// 0xxxxxxx
out += str.charAt(i-1);
break;
case 12: case 13:
// 110x xxxx 10xx xxxx
char2 = str.charCodeAt(i++);
out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
out += str.charAt(i-1);
break;
case 14:
// 1110 xxxx 10xx xxxx 10xx xxxx
char2 = str.charCodeAt(i++);
char3 = str.charCodeAt(i++);
out += String.fromCharCode(((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0));
break;
}
}
var byteArray = new Uint8Array(out.length * 2);
for (var i = 0; i < out.length; i++) {
byteArray[i*2] = out.charCodeAt(i); // & 0xff;
byteArray[i*2+1] = out.charCodeAt(i) >> 8; // & 0xff;
}
return String.fromCharCode.apply( String, byteArray );
}
var token = 'uid=pratik#gmail.com|ts=1412917909|hash=r1xWbgfHUxDLlppGYuOKQJdIM1MTrkryEArkMQx9ERw=|url=http://myintranet.com';
var key = 'a1cbbb6eb5cb2c1c27a9f02a4434d3af';
var blockSize = 16;
token = encodeUTF16LE(token);
var pad = blockSize - (token.length % blockSize);
token = token + (String.fromCharCode(pad)).repeat(pad);
token = CryptoJS.AES.encrypt(token, key,
{ iv: iv,
mode: CryptoJS.mode.CBC
});
console.log("\n This is the token " + token + "\n");
token = token.ciphertext.toString(CryptoJS.enc.Base64);
console.log("\n This is the token " + token + "\n");
Output
This is the token U2FsdGVkX19iQjVHkx/vmhljCsRyTBUA0QFJ8I+pPvxAa2dK6iO4r9FUw2Um2j0H+iyXZ/G0UO0fhJTFzfJEfS1cMfAaq0Z7UBUpVhtrH5IArr2F3BI6yWC8Kpo4ZimyW+xnWp0BYUpLUNQTLsFooiIqPHv3s9HHMe3k0altm6ou1pAKaIr8IAY1OzIDTbaRO55mPf0rU6Z2XTLGR6kYoAx9Lk4dZ3RA66cynXWFMuHznL0fik3phZ8cUiKd/Twquil97YHT+CB/1ulxEBD17VQvnsCJI1lYNn9dyWAUG96KMgGk3jFxiW9eRzV5Poywnt0QNaRpmZiG41KNFmtMtw==
This is the token GWMKxHJMFQDRAUnwj6k+/EBrZ0rqI7iv0VTDZSbaPQf6LJdn8bRQ7R+ElMXN8kR9LVwx8BqrRntQFSlWG2sfkgCuvYXcEjrJYLwqmjhmKbJb7GdanQFhSktQ1BMuwWiiIio8e/ez0ccx7eTRqW2bqi7WkApoivwgBjU7MgNNtpE7nmY9/StTpnZdMsZHqRigDH0uTh1ndEDrpzKddYUy4fOcvR+KTemFnxxSIp39PCq6KX3tgdP4IH/W6XEQEPXtVC+ewIkjWVg2f13JYBQb3ooyAaTeMXGJb15HNXk+jLCe3RA1pGmZmIbjUo0Wa0y3
I think the problem is CryptoJS.AES.encrypt where I am not passing it the correct configuration?
Really stuck on this, so if you have any suggestions, I'd like to know.
In both cases the key is not treated in the way you expect. 'a1cbbb6eb5cb2c1c27a9f02a4434d3af' is the hex representation of a 16 byte (= 128 bit) key, that can be used for AES128.
mcrypt does not know its getting a hex representation and treats it as 32 byte string and - IIRC - throws away everything except its first 16 bytes ('a1cbbb6eb5cb2c1c'). You need to unhexlify the key to get the raw bytes: $key=pack('H*', 'a1cbbb6eb5cb2c1c27a9f02a4434d3af').
CryptoJS also does not know it's presented a hex representation of the key and treats it as password instead, which is used as input to PBKDF2. The library has its own unhexlify routines: var key = CryptoJS.enc.Hex.parse('a1cbbb6eb5cb2c1c27a9f02a4434d3af'); The resulting WordArray will be treated by CryptoJS as binary key input.

How to get current frame of currently playing video file?

So we have flv file, we play it with mx:vidodisplay for example. how to get on which stream frame we are currently on?
you can check the nearest keyframe to the current time in stream metadata
upd
when creating a stream you need to handle its' onMetaData call:
private var metaInfo: Object;
private function initStream():void{
stream = new NetStream(conn);
stream.bufferTime = 5;
stream.addEventListener(NetStatusEvent.NET_STATUS, onStatus);
stream.client = new Object();
stream.client.onMetaData = onMetaData;/*this is what you need*/
video.attachNetStream(stream);
}
private function onMetaData(info:Object):void {
metaInfo = info;
var tmpstr:String = '';
for(var s:String in info){
var tstr:String = s + ' = ' + info[s] + '\n';
tmpstr += tstr.indexOf('object') == -1 ? tstr : '';
for(var a:String in info[s]){
var ttstr:String = s + ':' + a + ' = ' + info[s][a] + '\n';
tmpstr += ttstr.indexOf('object') == -1 ? ttstr : '';
for(var c:String in info[s][a]){
var tttstr:String = s + ':' + a + ':' + c + ' = ' + info[s][a][c] + '\n';
tmpstr += tttstr.indexOf('object') == -1 ? tttstr : '';
}
}
}
trace(tmpstr);
}
in this trace you'll see if the streams' metadata has items like:
seekpoints:93:offset = 10342550
seekpoints:93:time = 165.799
or maybe:
keyframes:times = 0,0.48,0.96,1.44,1.92,2.4,2.88,3.36,3.84,4.32,4.8,5.28,5.76,6.24
keyframes:filepositions = 1063,95174,136998,176043,209542,239148,271062,302006,331724,363948,395039,427503,456317,483313
it depends on encoder settings, if your metadata has any object of this kind (metadata['keyframes'], metadata['seekpoints'] etc) you can do the following:
for (var i:int = 0; i < metaInfo['keyframes']['times'].length; i++) {
if (stream.time < metaInfo['keyframes']['times'][i]) {
var keyFrameNum: int = (metaInfo['keyframes']['times'][i] - stream.time < stream.time - metaInfo['keyframes']['times'][i - 1]) ? i : i - 1;
}
}
I did a static class to parse netstream metadata object to as3 object. You can use JSON.stringify(parse(info)) to check all attribute in info. This class just draftly implement. May be some bugs inside.
public class NetStreamMetaData
{
public static function parse(object:Object, isArray:Boolean = false):Object{
var ret:Object = {};
if(isArray)
ret = [];
var k:String;
for(k in object){
if(isNaN(Number(k))){
if(object[k] is Array){
ret[k] = parse(object[k], true);
}else{
ret[k] = object[k];
}
}else{
if(object[k] is Array){
ret.push(parse(object[k], false));
}else{
ret.push(object[k]);
}
}
}
return ret;
}
}

Resources