Home > OS >  Password Generator performance : Python vs Javascript (Google apps script)
Password Generator performance : Python vs Javascript (Google apps script)

Time:11-30

I created a random code generator script via Google apps script. My goal is to generate 6000 uniques random codes (in spreadsheet) as fast as possible. The following javascript code crashes with Google spreadsheet apps script --> too long to execute and the same code under python generates 20,000 random codes in less than 1 second... I'm not a JS ninja, do you have any idea to optimize the JS code below ?

Code JS

function main(nbre_car,nbre_pass,number,letter_maj,letter_min,spec_car){
var nbre_car = 6;
var nbre_pass = 6000;
var number = true;
var letter_maj = false;
var letter_min = false;
var spec_car = false;
var prefixe="FOULE";
return generate_password(nbre_car,nbre_pass,number,letter_maj,letter_min,spec_car,prefixe)

}

function combinaison_possible(char_number,lenght_possible_char){
  combinaison_nbre=Math.pow(lenght_possible_char,char_number)
  return combinaison_nbre
}

function generate_password(nbre_car,nbre_pass,number=true,letter_maj=false,letter_min=false,spec_car=false,prefixe="") {
    if (Number.isInteger(nbre_car)&&Number.isInteger(nbre_pass)){
      
    }
    else{
      return "Veuillez rentrer un nombre entier pour les champs en bleu"
    }

    
    
    var nbre_car = nbre_car || 10;
    var nbre_pass = nbre_pass || 3;
    var pass_number="123456789";
    var pass_letter_maj="ABCDEFGHIJKLMNPQRSTUVWXYZ";
    var pass_letter_min="abcdefghijklmnpqrstuvwxyz"
    var pass_spec_car="'(-è_çà)=:;,!."
    // Check entry type
    

    // Create an empty map which will contain all password
    var col = new Map([]);
    var prefixe=prefixe;
  
    var list_char='';
    list_char= letter_maj == true ? list_char pass_letter_maj : list_char
    list_char= number == true ? list_char pass_number : list_char
    list_char= letter_min == true ? list_char pass_letter_min : list_char
    list_char= spec_car == true ? list_char pass_spec_car : list_char

    
    // Teste les combinaisons possible entre le nombre de caractère demandés pour le password et la liste disponible
    if (combinaison_possible(nbre_car,list_char.length)>=nbre_pass) {
    // Création des mots de passe unique
      while(col.size===0||nbre_pass>col.size) {
      Logger.log("col.size : " col.size)
      Logger.log("nbre_pass : " nbre_pass)
        search_new_pass=true;
        while (search_new_pass==true){
          pass=create_one_password(nbre_car,list_char,prefixe)
          Logger.log('nom du password : ' pass)
          if (verify_unique(col,pass)!=true)
            col.set({}, pass);
            Logger.log("valeur de col : " col)
            search_new_pass=false;
            
        }
      }
    }
    else{
      col = [];
      col.push("Vous avez demander trop de mots de passe, cela va créer des doublons,Veuillez diminuer le nombre de mots de passe à afficher");
    }
      final_values=[...col.values()];
      //Logger.log('valeur final de col : ' final_values)
      console.log(Array.from(col.values()));
      return Array.from(col.values());
    
  }


function create_one_password(nbre_car,list_char,prefixe) {
  var nbre_car = nbre_car; 
  s = '', r = list_char;
    for (var i=0; i < nbre_car; i  ) { 
      s  = r.charAt(Math.floor(Math.random()*r.length)); 
      }
    return prefixe s;
}

Code Python

import random

def combinaison_possible(char_number,lenght_possible_char):
  combinaison_nbre=pow(lenght_possible_char,char_number)
  return combinaison_nbre


def generate_password(nbre_car,nbre_pass,number=True,letter_maj=True,letter_min=True,spec_car=True,prefixe="FOULE") :

    if(not isinstance(nbre_car,int) and isinstance(not nbre_pass,int)) :
        print( "Veuillez rentrer un nombre entier pour les champs en bleu")

    nbre_car = nbre_car 
    nbre_pass = nbre_pass 
    pass_number="123456789"
    pass_letter_maj="ABCDEFGHIJKLMNPQRSTUVWXYZ"
    pass_letter_min="abcdefghijklmnpqrstuvwxyz"
    pass_spec_car="!@#$%^&*()_ "
    prefixe=prefixe
    list_char=''

    col={}
    longueur_col=len(col)
    list_char= list_char pass_letter_maj if letter_maj else list_char
    list_char= list_char pass_letter_min if letter_min else list_char
    list_char= list_char pass_number if number else list_char
    list_char= list_char pass_spec_car if spec_car else list_char
    
    if (combinaison_possible(nbre_car,len(list_char))>=nbre_pass) :
       
        while(len(col)==0 or nbre_pass>len(col)):
            longueur_col=len(col)
           
            search_new_pass=True
           
            while (search_new_pass==True):
                pass_word = prefixe ''.join(random.choice(list_char) for i in range(nbre_car))
                if pass_word not in col:
                    col[longueur_col]=pass_word
                    search_new_pass=False        
        print (col)
    else :
        print("Le nombre de mot de passe à générer est trop important par rapport au nombre de caractères possible")

generate_password(6,20000)

CodePudding user response:

Performance-wise, the main difference between the Apps Script and Python versions is that the Apps Script code logs about 20,000 values in the Apps Script console, which is slow, while the Python code outputs 1 value.

The Apps Script code has several syntactical and semantical errors, including:

  • verify_unique() is undefined
  • col.set({}, pass) does not make sense; perhaps use an Array instead of a Map, find if a value is already in the list with col.includes(pass), insert values with col.push(pass), and use col instead of Array.from(col.values()) to retrieve the values
  • var prefixe = prefixe; is superfluous

See Apps Script at Stack Overflow and Clean Code JavaScript.

CodePudding user response:

I think the code could be quite a bit easier. I did not study your code extensively. But this would be my approach to solve the problem. As you can see it takes less than one second to generate 20'000 passwords.

What actually really takes a long time is the duplicate check.

Aside from thath be careful when generating passwords without a cryptographically secure random algorithm.

Please have a look at this for how to use Crypto.getRandomValues()

const CHARACTER_POOL =
  "123456789ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijklmnpqrstuvwxyz'(-è_çà)=:;,!.";
const PASSWORDS_TO_GENERATE = 20000;
const PASSWORD_LENGTH = 6;
const PREFIX = "";

const createPassword = () => {
  let password = "";
  for (let i = 0; i < PASSWORD_LENGTH; i  ) {
    // this is not secure
    password  = CHARACTER_POOL.charAt(
      Math.floor(Math.random() * CHARACTER_POOL.length)
    );
  }
  return `${PREFIX}${password}`;
};

const generatePassword = () => {
  const passwords = [];
  while (passwords.length < PASSWORDS_TO_GENERATE) {
    const password = createPassword();
    if (!passwords.includes(password)) {
      passwords.push(password);
    }
  }
  return passwords;
};

const start = new Date().getTime();
const passwords = generatePassword();
console.log(`It took ${(new Date().getTime() - start) / 1000} to generate ${passwords.length} passwords`);
console.log(passwords);

  • Related