Generator losowych haseł

40

Serwer domeny wymaga, aby wszyscy pracownicy mieli silne, losowe hasło zgodne z następującymi zasadami:

  • Dokładnie 15 znaków.
  • Tylko znaki wpisywane na klawiaturze (jak pokazano poniżej w kodzie). Nauczanie sprzedaży używania kodów ALT + NUMPAD jest niedozwolone.
  • Co najmniej 1 mała litera: abcdefghijklmnopqrstuvwxyz
  • Co najmniej 1 duża litera: ABCDEFGHIJKLMNOPQRSTUVWXYZ
  • Co najmniej 1 cyfra: 0123456789
  • Co najmniej 1 symbol: `~!@#$%^&*()_+-={}|[]\:";'<>?,./

W tym celu dział IT zlecił i rozprowadzi generator losowych haseł do wszystkich pracowników. Wszyscy pracownicy będą zobowiązani do korzystania z generatora losowych haseł. Wymagania dotyczące generatora losowych haseł to, oprócz powyższych ograniczeń hasła:

  • Musi być w stanie wygenerować wszystkie permutacje wszystkich dozwolonych znaków.
  • Musi wyświetlać wygenerowane hasło na ekranie.
  • Kod musi być jak najmniejszy (w bajtach).

Prześlij swoje proponowane rozwiązanie w ciągu następnego tygodnia.

Hand-E-Food
źródło
10
Powinieneś także zażądać, aby wszystkie dozwolone hasła pojawiały się z takim samym prawdopodobieństwem (w przeciwnym razie mogę po prostu utworzyć listę o długości 30 znaków z dozwolonymi znakami, przetasować ją i podać pierwsze 15)
Martin Thoma
@moose, zgodził się. Dodałem nową regułę.
Hand-E-Food
22
Informatycy powinni zostać zwolnieni, a przynajmniej lepiej wykształceni: jeśli generujesz hasła losowo, wówczas ograniczenie zestawu dozwolonych haseł do tych, które zawierają co najmniej jedną postać z każdej kategorii, w rzeczywistości osłabia hasła, ponieważ zmniejsza rozmiar dopuszczalny zestaw. A nasze programy byłyby o wiele łatwiejsze, gdybyśmy nie musieli tego sprawdzać… OK, nie modyfikuj konkursu po otrzymaniu tylu zgłoszeń; jest w porządku jako wyzwanie.
MvG
10
@MvG Rzeczywiście:correcthorsebatterystaple
Jonathon Reinhart
1
Tak naprawdę nie odpowiedziałeś na @moose, wymagając, aby wszystkie hasła były generowalne. Powinny pojawić się z jednakowym prawdopodobieństwem.
Ethan Bolker

Odpowiedzi:

29

Matematyka (18)

Pozwól mi trochę oszukać

= 15char ASCII pwd
&(^F7yP8k:*1P<t

PS to nie bezpieczeństwo :)

Ybeltukov
źródło
6
Gdzie jest kod
DavidC
11
Czy to gwarantuje spełnienie przynajmniej jednego z wymagań każdej klasy postaci ?
Hand-E-Food
3
@ Hand-E-Food Tak, jest! Jeśli spojrzysz na interpretację, zobaczysz: długość hasła 15, wymagane małe litery, wymagane duże litery, wymagane cyfry, wymagane znaki specjalne.
ybeltukov,
6
+1 Sprytny, ale podstępny.
DavidC
10
Wiedziałem, że Mathematica ma funkcję do wszystkiego, ale to ?
Konrad Borowski
13

Ruby, 74 69 bajtów

Po prostu losowo próbkuj z zakresu ascii 33–126, aż wszystkie klasy znaków będą obecne:

$_=[*?!..?~].sample(15)*''until~/\d/&&~/[a-z]/&&~/[A-Z]/&&~/\W|_/
p$_

Rubin, 39 bajtów

Wykorzystując sprytne odkrycie łosia:

p"0123abcdABCD-+/<".chars.sample(15)*''

Edytuj, aby zaspokoić tłum:

Pamiętaj, że zasady uległy zmianie po pierwszym opublikowaniu tego. W tym czasie oba poprzednie wpisy dotyczyły reguł. Chciałbym również zauważyć, że reguły wciąż nie są zbyt dobrze zdefiniowane:

(..) wszystkie kombinacje wszystkich dopuszczalnych znaków

„Permutacje”. Nie ma permutacji dozwolonych znaków, która jest zgodna z resztą reguł, ponieważ każda permutacja zestawu dozwolonych znaków jest tak długa, jak sam zestaw dopuszczalnych znaków (podczas gdy hasło powinno mieć 15 znaków). I nie ma powtórzeń w permutacji. Mój pierwszy wpis jest jednak bardziej „losowy” niż wiele innych dobrze ocenianych odpowiedzi tutaj.

Niemniej jednak masz to. Umożliwia powtórzenia znaków i podkreślenie:

Rubinowy, 77 bajtów

$_=([*?!..?~]*15).sample(15)*''until~/\d/&&~/[a-z]/&&~/[A-Z]/&&~/\W|_/
puts$_

Użyłem również putszamiast ptego, ponieważ pdrukuje ciąg zamknięty w „cudzysłowie”, a niektóre znaki uciekały odwrotnym ukośnikiem.

Rubinowy, 70 bajtów

Jak zauważa Ventero, ~można go pominąć przed wyrażeniami regularnymi i printzastąpić puts$_. Ale z brzydkim wyjściem powoduje to, że równie dobrze możesz wydrukować wszystkie odrzucone hasła, ściskając je w jednej linijce:

puts$_=([*?!..?~]*15).sample(15)*''until/\d/&&/[a-z]/&&/[A-Z]/&&/\W|_/

Wyjaśnienie

Zgodnie z prośbą. $_jest pół-magiczną zmienną, która zawiera ostatni wiersz odczytany z danych wejściowych - więc nie zawsze musisz ją przechowywać, tak jak to . Tutaj jednak używamy go z powodu innej właściwości, a mianowicie, że ~operator stosuje do niej wyrażenie regularne, sztuczkę, której nauczyłem się po raz pierwszy przez Chrona . Zastąpiłem użycie all, ale powinno być dość łatwe do zrozumienia, jeśli dostaniesz resztę ( zobacz dokumenty ).

daniero
źródło
2
Czy mógłbyś wyjaśnić trochę swój kod? Co ma .all?{|r|~r}zrobić? Co ma $_=zrobić?
Martin Thoma,
3
Przykładowa linia jest sprytna, ale myślę, że narusza „Musi być w stanie wygenerować wszystkie permutacje wszystkich dozwolonych znaków”. Nigdzie nie jest powiedziane, że hasło może zawierać reklamy tylko w zakresie liter. Jeśli z jest znakiem dozwolonym, powinna istnieć szansa> 0, że z jest w haśle.
nitro2k01
1
Czy \Ww Ruby zawiera podkreślenie _? W większości dialektów regularnych wiem, że tak nie jest. A jeśli twój kod nie mógł wygenerować haseł, w których pojedynczy _był jedynym niealfanumerycznym symbolem, to naruszyłoby jedno wymaganie. Drugie podejście bardzo oczywiście narusza ten wymóg, ale wydaje mi się, że w tamtym czasie nie było poprawnie sformułowane.
MvG
1
@MvG: Masz rację. \Wnie zawiera podkreślenia w RegEx kompatybilnym z Perlem ( źródło ).
Martin Thoma,
1
Co więcej, na twoje rozwiązanie wpływa ten sam problem @moose, który miałem z Pythonem: samplenie powtarza elementów, więc hasła z powtarzającymi się elementami nie mogą być generowane przez kod. Czy możesz rozwiązać te dwa problemy, aby twoja odpowiedź była zgodna z pytaniem? Widząc, jak twoje jest wiodącym rozwiązaniem, z wyjątkiem Wolfram Alpha, fajnie byłoby zobaczyć, czy możesz się dostosować i nadal utrzymać pozycję lidera. Myślę, że nie powinno to być zbyt trudne.
MvG
12

Java 8 - 354 329 319 275 267 znaków

Dla zabawy, używając lambda z Javą 8 - każde możliwe wyjście ma takie samo prawdopodobieństwo znalezienia.

Wykorzystuje fakt, że dozwolone znaki mają kolejne kody ascii, od 33 do 126.

class A {
    //flags for, respectively, small caps, large caps, digits, punctuation
    static int a, A, d, p;

    public static void main(String[] x) {
        String s;
        do {
            //Using special String constructor that takes an int[]
            s = new String(new java.util.Random().ints(15, 33, 127)
                                .toArray(),
                           0, 15);
            a = A = d = p = 0;
            s.chars()
                .map(c ->
                      c > 96 & c < 123 ? a = 1
                    : c > 64 & c < 90  ? A = 1
                    : c > 47 & c < 58  ? d = 1
                    : (p = 1))
                .min();
        } while (a + A + d + p < 4);
        System.out.println(s);
    }
}

Przykładowe dane wyjściowe:

.*;Tm?svthiEK`3  
o.dzMgtW5|Q?ATo  
FUmVsu<4JF4eB]1

Skompresowany program:

class A{static int a,A,d,p;public static void main(String[]x){String s;do{s=new String(new java.util.Random().ints(15,33,127).toArray(),0,15);a=A=d=p=0;s.chars().map(c->c>96&c<123?a=1:c>64&c<90?A=1:c>47&c<58?d=1:(p=1)).min();}while(a+A+d+p<4);System.out.println(s);}}

assylias
źródło
Jak o while(a+A+d+p<4)wraz z a|=1zamiast a++? Lub użyj masek bitowych, tj. Takich jak a|=1przelotowe a|=8, z a<15warunkiem pętli as. To oszczędza kolejne 13 znaków, jeśli poprawnie policzyłem.
MvG
@MvG dobra uwaga - zrobiłem coś podobnego, oszczędzając kilka dodatkowych znaków, jak sądzę.
assylias
@MvG A używanie new String(int[],int,int)zapisuje kolejne 40 znaków nieparzystych!
assylias
8

Python 2.X + 3.X (229 znaków): Generuj i zamień

Pomysł

  1. Najpierw utwórz listę z 15 dozwolonymi symbolami
  2. Zamień losową pozycję rna losową cyfrę
  3. Wymienić losową pozycję s, z s != r, przez górną litery
  4. To samo dotyczy małych liter i symboli jak w 2 i 3.

Kod

from random import randint as r, shuffle as s
a=list(range(15))
p=a[:]
for i in range(15):
    a[i]=chr(r(32,126))
s(p)
a[p.pop()]=chr(r(48,57))
a[p.pop()]=chr(r(65,90))
a[p.pop()]=chr(r(97,122))
a[p.pop()]=chr(r(33,47))
print(a)

Python 2.X + 3.X (194 znaki): Wygeneruj i sprawdź

import random
from re import search as s
p=''
while not all([s("\d",p),s("[a-z]",p),s("[A-Z]",p),s("[\W_]",p)]):
 p=str(map(chr,[random.choice(list(range(33,127))) for i in range(15)]))
print(p)
  • Dzięki MvG, który mi to powiedział \ui \lnie istnieje w wyrażeniu regularnym Python.
  • Dzięki grc, który powiedział mi, że random.samplenie ma zamiany, aby uzyskać każde możliwe dozwolone hasło, musimy pobrać próbkę z wymianą.

Wykorzystanie błędu w opisie problemu

Obecnie opis problemu nie wymaga, aby każdy symbol / cyfra pojawiał się z takim samym prawdopodobieństwem. Dzięki poniższemu rozwiązaniu nie można zakładać żadnego pojedynczego symbolu i / lub pozycji. Ale możesz to zrobić za pomocą wielu.

Python 2.X + 3.X (62 znaki)

from random import sample
print(sample("0123abcdABCD-+/<",15))

Dzięki Daniero za pomysł użycia próbki.

Martin Thoma
źródło
Bardzo płynne znalezienie wady! Podłączyłem ten, ale punkty bonusowe za jego identyfikację. :-)
Hand-E-Food
Twój gen i czek jest podobny do mojego podejścia. Z ciekawości: gdzie to jest \ludokumentowane dla wyrażeń regularnych w Pythonie? Nie widzę tego w referencji . Mój Python 3.3.3 nawet nie zaakceptuje "\u". str(…)Nie łączy litery w obu 3.3.3 lub 2.7.6. Jedna sugestia dla optmization: all(s("\\"+i,p)for i in "dluW").
MvG
random.samplewybiera elementy bez zamiany, więc nie wszystkie hasła są możliwe.
grc
@MvG: Dziękuję. Właśnie to widziałem \ui \ljest to tylko vim.
Martin Thoma,
7

Bash na * nix (109)

while ! grep -Pq [A-Z].*[a-z].*[0-9].*[\\W_]<<<$a$a$a$a
do a=`tr -dc !-~</dev/urandom|head -c15`
done
echo $a

Aby działać poprawnie, $anie może być ustawione na prawidłowe, ale nieprzypadkowe hasło z góry. Jeśli chcesz dołączyć a=i linię z przodu, to trzy kolejne znaki, ale pozwala ci to powtarzać. Oczywiście możesz również zastąpić wszystkie nowe wiersze, ;aby mieć linijkę, którą możesz wykonywać tak często, jak chcesz.

Ponadto powinno być ustawione LC_ALL=Cczy nie ustawić jakieś zmienne środowiskowe locale specyficzne ( LANGa LC_CTYPEzwłaszcza), ponieważ zakresy znaków zależą kolejność sortowania jest równa kolejności ASCII.

/dev/urandomjest źródłem losowych bajtów. !-~to zakres wszystkich dopuszczalnych znaków, jak określono w pytaniu. tr -dcusuwa wszystkie znaki niewymienione w następnym argumencie. headbierze 15 pozostałych postaci. grepsprawdza, czy każdy z wymaganych rodzajów występuje przynajmniej raz. Jego dane wejściowe składają się z czterech kopii kandydata, więc kolejność symboli nie ma znaczenia, dlatego wszystkie możliwe hasła mają szansę zostać wybranymi. Opcja -qgrep tłumi wyjście.

Z nieznanych przyczyn /dev/randomzamiast na /dev/urandomwieki. Wygląda na to, że entropia dość szybko się wyczerpała. Jeśli cdw /dev, można uniknąć jeszcze kilka bajtów, ale czuje się trochę jak oszustwo.

Python 2 (138)

import re,random
a=''
while not re.search('[A-Z].*[a-z].*[0-9].*[\W_]',a*4):
 a=''.join(random.sample(map(chr,range(33,127))*15,15))
print a

Aby kod był czytelny, dodałem nowy wiersz i wcięcie po pętli, które nie są konieczne i których nie policzyłem.

Jest to zasadniczo ten sam pomysł, co w wersji bash. Tutaj jest losowe źródło random.sample, które nie będzie powtarzać elementów. Aby temu zaradzić, używamy 15 kopii listy dozwolonych liter. W ten sposób każda kombinacja może nadal występować, chociaż te z powtarzającymi się literami będą występować rzadziej. Ale decyduję się uznać to za cechę, a nie za błąd, ponieważ pytanie nie wymagało jednakowego prawdopodobieństwa dla wszystkich permutacji, tylko możliwość.

Python 3 (145)

import re,random
a=''
while not re.search('[A-Z].*[a-z].*[0-9].*[\W_]',a*4):
 a=''.join(random.sample(list(map(chr,range(33,127)))*15,15))
print(a)

Jeden nowy wiersz i jedno tiret ponownie nie są liczone. Oprócz pewnych narzutów składni specyficznych dla Python-3 jest to to samo rozwiązanie, co dla Python 2.

JavaScript (161)

a=[];for(i=33;i<127;)a.push(s=String.fromCharCode(i++));
while(!/[A-Z].*[a-z].*[0-9].*[\W_]/.test(s+s+s+s))
for(i=0,s="";i<15;++i)s+=a[Math.random()*94|0];alert(s)

Dodałem nowe wiersze dla czytelności, ale ich nie policzyłem.

R (114)

s<-""
while(!grepl("[A-Z].*[a-z].*[0-9].*(\\W|_)",paste(rep(s,4),collapse="")))
 s<-intToUtf8(sample(33:126,15,T))
s

Dodano przerwanie linii i wcięcie wewnątrz pętli, ale nie zostało zliczone. Jeśli masz na to ochotę, możesz ponownie przenieść to do pojedynczej ;linii.

MvG
źródło
Ha! Właśnie miałem wskazać, że mogłeś użyć greplw swoim kodzie R. Gdybym tylko pomyślał o powtórzeniu hasła testowego cztery razy, abyś mógł wykonać wszystkie kontrole za jednym razem. I wiesz, gdybym tylko pomyślał samplei intToUtf8. Musisz jednak dodać replace=TRUE(lub bardziej zwięźle, wystarczy dodać ,T) do przykładowej metody, aby upewnić się, że otrzymujesz wszystkie możliwe hasła.
AmeliaBR
@AmeliaBR: Masz rację, naprawiłeś ten replace=Tbłąd, dziękuję za zwrócenie na to uwagi. Znalezienie intToUtf8zgadując prawdopodobne nazwiska z zakończeniem zakładce zajęło mi sporo czasu; Wiedziałem, że taka funkcja musi istnieć, ale chrnie używano bardziej popularnych nazw takich jak i tak dalej.
MvG
@MvG: Nie rozumiem, dlaczego Twój kod Python w ogóle się kończy. Dlaczego tego potrzebujesz *4? Myślałem, że twoje wyrażenie regularne będzie pasować do dowolnego łańcucha, w tej reklamie najpierw jedna wielka litera, potem cokolwiek, a potem jedna mała litera, niż cokolwiek ... co pomyliłem?
Martin Thoma,
@moose: Jak już zauważyłeś, moje wyrażenie regularne sprawdza wymagane kategorie w określonej kolejności. Ale łącząc cztery kopie aktualnego kandydata, mogę upewnić się, że kolejność nie ma już znaczenia: nawet jeśli moje hasło to symbole, cyfry, małe litery i wielkie litery, to nadal będzie pasować. Jedynym sposobem, w jaki dopasowanie może się nie powieść, jest całkowite pominięcie kategorii. Zauważ też, że re.searchnie re.match, więc wyrażenie regularne może pasować w dowolnym miejscu w haśle kandydata. Czy to wyjaśnia, dlaczego ostatecznie wygasa?
MvG
Ach, nie zauważyłem, że używasz re.searchzamiast re.match. To wyjaśnia to. Ale nadal uważam, że nie potrzebujesz *4. Dziękuję za wyjaśnienie (+1)
Martin Thoma
7

C # ( 123 - 139 103 - 127 znaków zagęszczonych):

Korzystanie z idealnie odpowiedniej metody ramowej w System.Web.dll:

class P
{
    static void Main()
    {
        Console.WriteLine(System.Web.Security.Membership.GeneratePassword(15, 1));
    }
}

Zagęszczony:

class P{static void Main()
{Console.WriteLine(System.Web.Security.Membership.GeneratePassword(15,1));}}

Przykład:

b+m2ae0K:{dz7:A

Możesz też pobrać wartość drugiego parametru ( int numberOfNonAlphanumericCharacters) z wiersza poleceń:

class P
{
    static void Main(string[] a)
    {
        Console.WriteLine(System.Web.Security.Membership.GeneratePassword(15, int.Parse(a[0])));
    }
}
Rik
źródło
3
GeneratePasswordnie obsługuje pełnego zestawu symboli określonych w pytaniu. Nie znalazłem też żadnych gwarancji co do minimalnej liczby wystąpień każdej kategorii postaci.
MvG
2
Możesz skompaktować dalej, używając class Pi string[] a.
d3dave
@MvG, to interesujące. Wygląda na to, że wyklucza każdy symbol powszechnie używany do pisania znaków akcentowanych w językach takich jak francuski. Prawdopodobnie sprytny ruch. Wystarczy zmienić język klawiatury, aby uzupełnić hasło.
Hand-E-Food
5

R (301 322 znaków)

Korekta zapomniała sprawdzić cyfry.

a='abcdefghijklmnopqrstuvwxyz';
f=as.factor(strsplit(paste(a,toupper(a),
    sep="0123456789`~!@#$%^&*()_+-={}|[]\\:\";'<>?,./"),"")[[1]]);
g=gsub("(.):","\\1",levels(q:q:q:q:q:q:q:q:q:q:q:q:q:q:q));
repeat{p=g[runif(1)*length(g)]; 
    if(grepl("[A-Z]",p)&&grepl("[a-z]",p)&&grepl("[0-9]",p)&&grepl("[^A-Za-z0-9]",p))break;};
print(p);

(białe znaki dodano tylko dla jasności).

Generuje wszystkie możliwe 15-znakowe kombinacje 94 znaków. Następnie losowo wybiera jeden, dopóki nie spełni kryteriów.

Magia tkwi w q:qoperacji, która generuje nowy typ danych czynników, czyli interakcję wszystkich czynników z pierwszej qlisty ze wszystkimi czynnikami z drugiej listy , przy czym każda możliwa kombinacja tych dwóch list znajduje się na liście „ poziomy ”tego czynnika. Wejdź w interakcję z 15 kopiami listy dozwolonych postaci, a otrzymasz (94 ^ 15) możliwych poziomów.

Nie próbuj tego w domu. Kod zajmuje kilka sekund, aby dowiedzieć się o wszystkich 3-znakowych kombinacjach. Naprawdę nie wyobrażam sobie, ile czasu zajęłoby ustalenie wszystkich 15-znakowych kombinacji, jeśli komputerowi nie zabrakło pamięci w międzyczasie. Kiedy uruchomiłem gotowy skrypt (hasło składające się z trzech znaków), aby to sprawdzić, pierwsze hasło, które wypluło, to „oO =”, co myślę o podsumowaniu reakcji na ten kod.

AmeliaBR
źródło
@MvG ma skrypt R, który jest zarówno bardziej praktyczny, jak i znacznie krótszy, choć o wiele mniej niesamowity: codegolf.stackexchange.com/a/17700/12413
AmeliaBR
Niemniej jednak podoba mi się twój pomysł. Wiele fragmentów kodu do golfa, które widziałem, pozwoliło na to, by określone funkcje języka wykonały ciężką pracę. A twój kod z pewnością robi to dla R, z tymi interakcjami.
MvG
4

Mathematica 170

r=RandomSample;f[i_]:=(FromCharacterCode/@Range@@i);
{t,A,a,n}=f/@{{33,126},{65,90},{97,122},{48,57}};
s=Complement[t,A,a,n];
""<>r[Join[RandomChoice/@{A,a,n,s},r[t,11]],15]

Przykłady

"<]} Pg3 / e? 3 + Z ~ Oz"
"X / 8jWe @ f (_x5P: ="
"2wz2VQhtJC? * R7 ^"

DavidC
źródło
4

Python 2.7 (182)

import random as r,string as s
z=r.sample
j=list(z(s.ascii_lowercase,12)+z(s.ascii_uppercase,1)+z(s.digits,1)+z('`~!@#$%^&*()_+-={}|[]\\:";\'<>?,./',1))
r.shuffle(j)
print ''.join(j)
Jonathon Reinhart
źródło
Możesz uzyskać 9 cyfr mniej, usuwając złączenie, ponieważ nie jest to wymagane w opisie problemu. Kolejne 2 mniej, usuwając spacje.
Martin Thoma,
@moose Wyjąłem spacje tuż przed tym, jak skomentowałeś :-) Wydaje mi się, że coś takiego joinmusi być: Czy użytkownicy powinni rozumieć składnię listy python z wyjścia ['q', 'u', ...]:?
Jonathon Reinhart
1
W ogóle pomyślałem o usunięciu wydruku. Gdy wielkość w bajtach jest ważna, mogą żyć w czasie karty dziurkacza. W ten sposób mogą być w stanie odczytać pamięć ... tylko patrząc na nią. Albo są „prawdziwymi programistami”: xkcd.com/378
Martin Thoma,
1
Jeśli poprawnie czytam kod, nie spełnia to wszystkich wymagań dotyczących permutacji , zawsze będzie zawierał 12 małych liter, co uniemożliwia hasła dla więcej niż jednej innej grupy (jak aA$bc1111111111).
IQAndreas,
1
W obronie Johnathona myślę, że reguła permutacji została dodana 5 minut po swoim stanowisku.
Hand-E-Food
4

Golfscript (60)

Ponieważ obl. brakuje skryptu golfowego i i tak potrzebuję praktyki :)

[48 10{rand}:r~+65 26r+97 26r+33 15r+11,{;32 96r+}%~]{r}$''+

Po prostu buduje tablicę z 4 wymaganymi + 11 losowymi postaciami i sortuje w losowej kolejności.

Joachim Isaksson
źródło
+1 dla {r}$. To dość brudny sposób na przetasowanie listy - podoba mi się! ;-)
Ilmari Karonen
... jednak nie sądzę, że może to kiedykolwiek wygenerować np 0Aa~~~~~~~~~~~~. :-(
Ilmari Karonen
3

JavaScript 258 240 233 225

R=Math.random;a=b=>b[b.length*R()|0];for(x=[a(l="abcdefghijklmnopqrstuvwxyz"),a(u=l.toUpperCase()),a(n="0123456789"),a(s="`~!@#$%^&*()_+-={}|[]\\:\";'<>?,./")];15>x.length;x.push(a(l+u+n+s)));alert(x.sort(y=>.5-R()).join(""))

Stosując regułę, w której:

function(x){return x*x}można ponownie zapisać jako function(x)x*x. Wydaje się, że działa tylko w przypadku funkcji zwracających wartość.

Następna iteracja, zmniejszona x.sort(function().5-R())dox.sort(y=>.5-R())

Kolejna iteracja, zmniejszona dodatkowo dzięki grubej notacji strzałek, która niestety działa tylko w przeglądarce Firefox 22 i nowszych.

WallyWest
źródło
Ładne zagęszczanie! : D
IQAndreas
2

JavaScript (kompaktowane 269 znaków)

Dla jasności jest to kod, zanim go skompresowałem JS-Fiddle :

var lowerLetters = "abcdefghijklmnopqrstuvwxyz";
var upperLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var numbers = "0123456789";
var symbols = "`~!@#$%^&*()_+-={}|[]\\:\";'<>?,./";
var allCharacters = lowerLetters + upperLetters + numbers + symbols;

String.prototype.randomChar = function() {
    return this[Math.floor(this.length * Math.random())];
}

var minLength = 15;
var result = [];

// Start off by picking one random character from each group
result.push(lowerLetters.randomChar());
result.push(upperLetters.randomChar());
result.push(numbers.randomChar());
result.push(symbols.randomChar());
// Next, pick a random character from all groups until the desired length is met
while(result.length < minLength) {
    result.push(allCharacters.randomChar());
}
result.shuffle(); // Finally, shuffle the items (custom function; doesn't actually exist in JavaScript, but is very easy to add) -> http://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array
result.join("");

Tutaj jest spakowany do 269 znaków ( JS-Fiddle tego ):

l="abcdefghijklmnopqrstuvwxyz";
u=l.toUpperCase();
n="0123456789";
s="`~!@#$%^&*()_+-={}|[]\\:\";'<>?,./";
R=Math.random;

function r(t){
    return t[~~(t.length*R())]
}

for(x=[r(l),r(u),r(n),r(s)];x.length<15;x.push(r(l+u+n+s)));
x.sort(function(){return .5-R()});
alert(x.join(""));
IQAndreas
źródło
Ponieważ kończę linie średnikami, wszystkie usuwalne białe znaki zostały zignorowane do zliczania znaków, ale pozostawiono je dla przejrzystości.
IQAndreas,
Co masz na myśli mówiąc, shuffle()że jesteś „funkcją niestandardową”? Czy to część kodu JavaScript lub kodu, który musiałbyś napisać sam?
Hand-E-Food
@ Hand-E-Food Miałem na myśli, że nie jest on wbudowany w JavaScript, a ponieważ każdy programista tutaj powinien wiedzieć, jak tasować tablicę, czułem, że włączenie funkcji w kodzie jest niepotrzebne. Jest on jednak dostępny w JS-Fiddle (linia 16).
IQAndreas
1
Chodzi mi o to, że liczy się do twojej liczby bajtów. Ale widzę, że zaimplementowałeś go w wersji kompaktowej, więc proszę zignoruj ​​mnie. :-)
Hand-E-Food
2

Clojure (63):

(->> (map char (range 33 127)) (shuffle) (take 15) (apply str))

Ale należy je poprawić, aby zawierały co najmniej 1 znak z każdej kategorii (górny, dolny, cyfra, symbol).

ntalbs
źródło
2

Na serwerze SQL

declare @a nvarchar(28)
set @a='abcdefghijklmnopqrstuvwxyz'
declare @b nvarchar(max)
set @b='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
declare @c nvarchar(max)
set @c='0123456789'
declare @d nvarchar(max)
set @d='~!@#$%^&*()_+-={}|[]\:";<>?,./'

select left(substring(@a,cast(rand()*10 as int),3)+substring(@b,cast(rand()*10 as int),6)+substring(@c,cast(rand()*10 as int),3)+substring(@d,cast(rand()*10 as int),5),15)

Zobacz to w akcji - 1

zobacz to w Akcji - 2

Vhadalgi
źródło
1
Mam problem z podążaniem za ostatnim wierszem, ale kod nie wydaje się spełniać wszystkich wymagań dotyczących permutacji .
IQAndreas
Twój kod nigdy nie wygeneruje hasła zaczynającego się od ~0Aa, ani żadnego hasła, po którym bnastępuje a.
Heinzi
@Heinzi: tak, zgadzam się, że wszystkie wymagane permutacje nie są brane pod uwagę, po prostu wyświetla 15 znaków.. znaków losowo wybranych z ... z, A..Z, 0..9,! ... + :(. ..
vhadalgi
2

SAS (191)

%macro c(p);compress(p,,"&p")ne''%mend;data x;length p$15;do x=1by 1;do t=1to 15;substr(p,t,1)=byte(ranuni(7)*94+33);end;if %c(kd)and %c(kl)and %c(ku)and %c(ad)then do;put p;stop;end;end;run;

*TQP,(f=h10*)S=

Skomentowane / wcięte:

%macro c(p); /*compress removes or keeps certain classes of characters*/
  compress(p,,"&p")ne''
%mend;
data x;
length p$15;
do x=1by 1;
    do t=1to 15;
        substr(p,t,1)=byte(ranuni(7)*94+33); /*give it a 33-126, byte discards the noninteger portion rounding down*/
    end;
    if %c(kd)and %c(kl)and %c(ku)and %c(ad)then do; /*k=keep d=digit l/u=lower/upper ad=remove digits and alphas*/
        put p;
        stop;  /*met our requirement, head home*/
    end;
end;
run;
Joe
źródło
2

PowerShell: 119

Kod Gofled

for(;!($x-cmatch'.*(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!-/:-@[-`{-~]).*')){$x='';1..15|%{$x+=[char](33..126|random)}}$x

Nie grał w golfa i skomentował

# Start for loop definition.
for(
    # Skip variable initialization, start definition of run condition.
    ;
    # Loop runs if $x does not meet complexity requirements.
    # Length requirement is not tested here because it is enforced by the generator later.
    # Much thanks to @VasiliSyrakis for the RegEx help.
    !($x-cmatch'.*(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!-/:-@[-`{-~]).*')
)
{
    # Reset $x in case the script block has already run.
    $x='';
    # Use ForEach-Object, via the % alias, to run a loop 15 times.
    1..15|%{
        # Append a random ASCII character from 33-126 to $x.
        # Note: Use get-random instead of random for faster performance.
        $x+=[char](33..126|random)
    }
}
# Display $x.
$x
# Variable cleanup - not included in golfed code.
rv x
Iszi
źródło
Myślę, że ten regex może uczynić go jeszcze trochę krótszym: ^.*(?=.{15,})(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!#$%&? "]).*$możesz zrobić z nim jedno dopasowanie, które będzie pasować tylko wtedy, gdy będzie jeden górny, dolny, cyfrowy, symbol.
Vasili Syrakis
@VasiliSyrakis Ok, może będziesz musiał przez to trochę mnie poprowadzić. Rozpocznij czat, jeśli uważasz, że zajmie to trochę czasu. Kilka rzeczy, na których jestem zdezorientowany: 1.) Widzę zawarty tam numer 15. Czy to ma upewnić się, że ciąg ma dokładnie 15 znaków? Jeśli tak, można to pominąć, ponieważ skrypt w naturalny sposób generuje jedynie ciągi 15 znaków. 2.) Co masz na myśli mówiąc „będzie pasować tylko wtedy, gdy będzie jeden górny, dolny, cyfrowy, symbol”? Czy to oznacza, że ​​będzie pasować tylko wtedy, gdy będzie dokładnie jeden lub przynajmniej jeden z nich? Pierwszy zepsuje wszystko.
Iszi
Ponadto, czy Twój RegEx ignoruje kolejność znaków? Na przykład, jeśli obniżone, aby dopasować ciągi 4 znaków, czy oba 1aZ%i (p3Rpasują? Miałem trudności ze znalezieniem sposobów na zrobienie tego online.
Iszi
Przetestowałem nowy RegEx pod kątem danych wyjściowych z mojego bieżącego skryptu. Nie wydaje się to całkiem wiarygodne. Kod: $x-cmatch'^.*(?=.{15,})(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!#$%&? "]).*$'Dobre mecze: C>suD1?hTwbDx(z j%4O]HyeG|u[U$5 O/rGeD0$hJk=GO/Nieudane mecze: 3evthX3_X^nBrR` .nA ~ uYzrR4YV-r.u-IjZE48ntQ;HxV
Iszi
Jak otworzyć pokój czatu?
Wasilij Syrakis
1

Python 2.7 (149)

from random import*
''.join(map(lambda x:chr(randint(*(x[1]or(32,126)))),sorted(map(None,sample(range(15),15),((48,57),(65,90),(97,122),(33,47))))))

Napisane w bardziej czytelny (i nie wykonywalny) sposób;

from random import *
''.join(                                          # Concatenate characters to string
  map(                                            # Map all characters using below lambda
    lambda x:chr(randint(*(x[1] or (32, 126)))),  # Map a single range to a random character
                                                  # within a specific range if supplied,
                                                  # otherwise the default "all" range.
    sorted(                                       # After distributing ranges, sort
      map(None,                                   # zip_longest alternative, distributes the
                                                  # required ranges over 4 random positions
        sample(range(15), 15),                    # 0-14 in random order
        ((48, 57), (65, 90), (97, 122), (33, 47)) # The 4 required ranges
      )
    )
  )
)

Dość prosta i zaskakująco niewiele dłuższa niż wersja „generuj, spróbuj ponownie po porażce”.

Joachim Isaksson
źródło
Czy na pewno może to wygenerować wszystkie kwalifikujące się hasła, w tym np. 0Aa~~~~~~~~~~~~? (Uwaga '~' == chr(126).)
Ilmari Karonen
1

PSQL (189)

Wydaje się, że PSQL jest trochę gadatliwy ... :)

SELECT ARRAY_TO_STRING(ARRAY_AGG(CHR((TRUNC((b-a)*RANDOM()+a))::int)ORDER BY RANDOM()),'')FROM(SELECT 32 a,127 b FROM generate_series(1,11)UNION ALL VALUES(48,58),(65,91),(97,123),(33,48))a

Demo SQLfiddle .

Joachim Isaksson
źródło
1

PHP, 235 225

Ten skrypt tasuje znaki, a następnie jest sprawdzany przez RegEx, aby upewnić się, że hasło jest silne (lub zostało zregenerowane).

<?php
while(!preg_match('/^(?=.*[A-Z])(?=.*[^A-Za-z])(?=.*[0-9])(?=.*[a-z]).{15}$/',$p)){ $p = substr(str_shuffle('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`~!@#$%^&*()_+-={}|[]\:";\'<>?,./'),0,15); }
echo $p;
ub3rst4r
źródło
1
Sprytne, ale nie zezwalają na powielanie znaków.
Hand-E-Food
1
Zamiast tego while(true) ... if (condition) breakmożesz użyćwhile (!condition)
exussum
1

JavaScript (209)

r=Math.random;function t(x,y){return String.fromCharCode(Math.floor(y*r()+x))};x=[t(33,14),t(48,10),t(65,26),t(97,26)];for(i=0;i<11;i++)x.push(t(32,95));console.log(x.sort(function(){return r()-0.5}).join(''))

Na wpół nieprzygotowany;

// Return a character in the range [x,x+y)
function t(x,y) { return String.fromCharCode(Math.floor(y*Math.random()+x)) }
// Prefill required ranges
x=[ t(33,14), t(48,10), t(65,26), t(97,26)]
// Push 11 totally random (valid) characters
for(i=0; i<11; i++)
  x.push(t(32,95))
// Shuffle and output as string
console.log(x.sort(function(){return Math.random()-0.5})
             .join(''))
Joachim Isaksson
źródło
1

Perl, 92

Nie tak zwięzłe jak odpowiedź Ruby, ale jestem pewien, że czarodziej Perla może to jeszcze skrócić ... Nie jestem zbyt zadowolony ze wszystkich m//s na końcu, ale wydaje się, że działa i powinien spełniać warunki, aby w końcu wygenerować wszystkie permutacje.

do{$_=join"",map{(map{chr}33..127)[rand 94]}0..14}while!(/[A-Z]/&/[a-z]/&/\d/&/[\W_]/);print

Przykładowe użycie:

perl -e 'do{$_=join"",map{(map{chr}33..127)[rand 94]}0..14}while!(/[A-Z]/&/[a-z]/&/\d/&/[\W_]/);print'

Edytowane, aby naprawić sprawdzanie poprawności i zmienić [[:punct:]]na [\W_]komentarze po MvG.

Dom Hastings
źródło
1
Część generująca jest ładna, ale twoje kryterium wyboru w stanie pętli jest po prostu błędne: np. Hasło aaaaaaaaaaaaaaspowoduje, że pętla się zakończy. Powinieneś przetestować kryteria przy użyciu nieprzypadkowych haseł, aby upewnić się, że robią to, co chcesz.
MvG
Rzeczywiście masz rację, naprawiłem to i zapisałem kilka bajtów! Dzięki!
Dom Hastings
1
Jesteś tego pewien [[:punct:]]? Chyba wolałbym , which is shorter and of which I'm even more sure that it is correct, at least combined with your zasięg „[\ W_] 33..127”.
MvG
Dobrze, myślę, że martwiłem się , że \Wnie uwzględniono _, ale masz absolutną rację, nie jest to konieczne: gist.github.com/anonymous/8301237 . Dziękuję Ci!
Dom Hastings
1

Java 7 ( 270 234 znaków)

Ta sama zasada jest używana przez @assylias z java 8 (generuj losowe hasła do prawidłowego hasła). Jednak zamiast używać lambdas, hasło jest generowane przez iterację tablicy char i sprawdzane przez dopasowanie wyrażenia regularnego.

class A {
  public static void main(String [] a) {
    byte[] b = new byte[15];
    String s;
    do {
      new java.util.Random().nextBytes(b);
      s = new String(b);
    } while(!s.matches("(?=.*?[a-z])(?=.*?[A-Z])(?=.*?\\d)(?=.*?[!-/:-@\\[-`]).*"));
    System.out.println(s);
  }
}

Kod minimalny:

class A {public static void main(String[] a){byte[] b=new byte[15];String s;do{new java.util.Random().nextBytes(b);s=new String(b);}while(!s.matches("(?=.*?[a-z])(?=.*?[A-Z])(?=.*?\\d)(?=.*?[!-/:-@\\[-`]).*"));System.out.println(s);}}
madoke
źródło
1

PowerShell


Wersja One Liner (143 bajty)

sal g random;1..11|%{$P+=[char](33..126|g)};(65..90|g),(97..122|g),(48..57|g),(33..47+58..64+123..126|g)|%{$P=$P.insert((1..11|g),[char]$_)};$P

Wersja mini (146 bajtów)

sal g random
1..11|%{$P+=[char](33..126|g)}
(65..90|g),(97..122|g),(48..57|g),(33..47+58..64+123..126|g)|%{$P=$P.insert((1..11|g),[char]$_)}
$P

Wersja do odczytu (860 bajtów)

function pwgen {

    # Fulfill Upper,Lower,Digit,Symbol requirement by predefining ASCII ranges for each
    # These will be added into the string beginning at line 24

    [array[]]$symbolrange = (33..47),(58..64),(123..126)

    [char]$upper = (get-random (65..90))
    [char]$lower = (get-random (97..122))
    [char]$digit = (get-random (48..57))
    [char]$symbol = $symbolrange | get-random

    [char[]]$requirement = $upper + $lower + $digit + $symbol

    # Create the first 11 characters using any ASCII character between 32 - 126

    foreach ($number in (1..11)) {
        [string]$pass += [char](get-random (33..126))
    }

    # Insert each requirement character at a random position in the string

    foreach ($char in $requirement) {
        [string]$pass = $pass.insert((Get-Random (1..11)),$char)
    }

    return $pass
}

Podziękowania dla Iszi za różne wskazówki dotyczące skrócenia kodu.

Wasilij Syrakis
źródło
1
Nie obejmuje to wszystkich permutacji. Jako przykład abcd1234ABCD{|}~nigdy nie pojawi się, ponieważ $symbolzmusza co najmniej jeden z symboli do umieszczenia między ASCII 33 a 47.
Hand-E-Food
Cholera! Czy musisz wskazać moje lenistwo !? Żartuję ... Zredagowałem to teraz. Zmusiłem też każdy znak „wymagania” do osobnego indeksu w ciągu, zamiast zlepiać cztery razem pod tym samym indeksem. Teraz gdybym tylko mógł to skrócić ...
Wasilij Syrakis
Czy jest jakiś powód, dla którego nie możesz ogolić jeszcze kilku postaci, skracając się $SRdo $Q?
Iszi
Powinieneś także być w stanie wyciąć takie rzeczy, jak „ foreach”, (g(65..90))aby 65..90|g'. And change the wypowiadać instrukcje do foreach-objectpętli za pomocą %aliasu. Przykład: foreach($N in (1..11)){... }powinno być wykonalne jak 1..11|%{... }. Jestem pewien, że istnieją inne optymalizacje, które są możliwe, ale tak naprawdę mam zupełnie inną implementację, którą zamierzam wypróbować później.
Iszi
Fajne wskazówki :) Skróciłem go do 213 bajtów, jeśli wyjmę zwrot karetki i zastąpię średnikami.
Vasili Syrakis
1

Współczynnik, 196 znaków

Ten sam algorytm jak MvG i łoś. Nie jest najkrótszy, ale powinien spełniać wszystkie (obecne) kryteria w pytaniu:

USING: io kernel math pcre random sequences sequences.repeating ;
[ 15 94 random-integers [ 33 + ] "" map-as dup 60 cycle
"[A-Z].*[a-z].*[0-9].*[\\W_]" findall { } = not ] [ drop ] until print
Björn Lindqvist
źródło
Być może błędnie interpretuje wyrażenie regularne, ale myślę, że coś takiego ~{}|1234abcdABCDzawiedzie wyrażenie regularne.
Hand-E-Food
1
Nie, to zadziała:"~{}|1234abcdABCD" 60 cycle "[A-Z].*[a-z].*[0-9].*[\\W_]" findall empty? not => t
Björn Lindqvist
Wierzę ci na słowo. :-)
Hand-E-Food
1

C - 154 znaków

char p[16],c,f,w;main(){srand(time());while(f^15){c=p[15]=f=0;while(c^15){w=33+rand()%94;f|=w
>96&&w<123?1:w>47&&w<59?2:w>64&&w<91?4:8;p[c++]=w;}}puts(p);}

Jak nienawidzę srand()? Pozwól mi policzyć drogi.

Oberon
źródło
1

Haskell, 192

import System.Random
main=getStdGen>>= \g->(print.(take 15))$until((\x->all(any(`elem`x))[['a'..'z'],['A'..'Z'],['0'..'9'],['!'..'/']++":;<=>?@[\\]^_`{|}~"]).(take 15))tail$randomRs('"','~')g

Wydrukowany ciąg ma wokół siebie cudzysłowy i ucieka przed odwrotnym ukośnikiem i znakami cudzysłowu; jeśli to niedopuszczalne, printmożna je zastąpićputStrLn przez 3 kolejne bajty. Oto bardziej czytelna wersja:

import System.Random

main = do
    g <- getStdGen
    let chars = randomRs ('"', '~') g
    let password = take 15 $ until (hasChars.(take 15)) tail chars
    print password

hasChars :: String -> Bool
hasChars x = all (any (`elem` x)) $
    [ ['a'..'z']
    , ['A'..'Z']
    , ['0'..'9']
    , "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
    ]

Jest to dość proste, to po prostu tworzy nieskończoną / lazy listę losowych znaków ASCII w przedziale '!'do '~', a następnie rzuca się pierwszy element aż 15 pierwszych znaków mieć co najmniej jeden znak z każdej struny wymaganych znaków.

użytkownik3175123
źródło
1

Excel VBA, 209 bajtów

For i = 1 To 15
x = x + Chr(Int(90 * Rnd + 33))
Next
p = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*(_|[^\w])).+$"
With New RegExp
.Pattern = p
Set m = .Execute(x)
If m.Count = 0 Then
MsgBox "redo"
Else
MsgBox x
End If
End With

Generuje losowo 15 znaków ASCII, dzięki czemu możliwe są wszystkie możliwe kombinacje. Następnie używa wzorca wyrażenia regularnego, aby sprawdzić, czy zawiera on przynajmniej jedno z każdego kryterium.

Jeśli tak, pojawi się hasło, jeśli nie, pojawi się „ponów”.

Podziękowania dla Barta Kiersa za wzór wyrażeń regularnych: https://stackoverflow.com/questions/1559751/regex-to-make-sure-that-the-string-contains-at-least-one-lower-case-char- górny

Wightboy
źródło
0

AutoHotkey 352

global o:={}
loop 4
o[c()]:=o(A_index-1)
loop 11
o[c()]:=o(m(r(),4))
loop 15
s.=o[A_index-1]
msgbox % s
r(){
Random,z
return z
}
m(z,r){
return mod(z,r)
}
c(){
while o[t:=m(r(),15)]!=""
j++
return t
}
o(f){
r0:=48,l0:=10,r1:=97,l1:=l2:=26,r2:=65
r := chr(r%f%+m(r(),l%f%))
if f=3
r:=Substr("``~!@#$%^&*()_+-={}|[]\:"";'<>?,./",m(r(),32)+1,1)
return r
}

Korzystanie - wystarczy uruchomić skrypt

Avi
źródło
0

Python (121 znaków)

Wykorzystuje fakt, że możesz pomnożyć listy w Pythonie [1,2,3] * 2 daje [1,2,3,1,2,3]. Importuje losowo. Liczby na liście pomnożone przez trzy są granicami między zakresami w tabeli ascii dla potrzebnych znaków, np. [65, 90] odwzorowuje na wielkie litery.

print "".join([random.choice([chr(i) for i in range(z[0],z[1])]) for z in [[33,48],[48,58],[58,65],[65,90],[90,123]]* 3])
Pawelmhm
źródło
1
„Musi być w stanie wygenerować wszystkie permutacje wszystkich dozwolonych znaków.”. Nie sądzę, żeby tak było, ponieważ zakresy są zawsze stosowane w tej samej kolejności ...?
Joachim Isaksson
Masz rację, dziękuję. Rzeczywiście nie zauważyłem, że zakresy powinny być stosowane w losowej kolejności, powinny być tasowane, za chwilę to wyedytuję.
Pawelmhm
To musi faktycznie zawierać się import randomw kodzie.
Mego
0

PHP 5.5 (230 bajtów)

echo preg_replace_callback('/./', function ($a)
{
  return chr([rand(33, 126), rand(48, 57), rand(65, 90), rand(97, 122), ord(str_shuffle('`~!@#$%^&*()_+-={}|[]\:";\'<>?,./')[0])][$a[0]]);
}
, str_shuffle(str_pad('1234', 15, 0)));

Lub w jednym wierszu (211 bajtów)

echo preg_replace_callback('/./',function($a){return chr([rand(33,126),rand(48,57),rand(65,90),rand(97,122),ord(str_shuffle('`~!@#$%^&*()_+-={}|[]\:";\'<>?,./')[0])][$a[0]]);},str_shuffle(str_pad('1234',15,0)));
MichaelRushton
źródło