ajout nur/passwd

This commit is contained in:
Jephté Clain 2024-04-04 16:47:29 +04:00
parent 86ce10cf95
commit 5ca55e1919
9 changed files with 344 additions and 0 deletions

View File

@ -0,0 +1,68 @@
<?php
namespace nur\passwd;
abstract class AbstractGenerator {
const NB_BLOCKS = 4;
const NB_PUNCTS = 2;
const CONSONNES = "bcdfghjklmnpqrstvwxz";
const VOYELLES = "aeiuoy";
const PUNCTS = "+-*/=!@?:#";
const MAPPINGS = [
# éviter certaines combinaisons potentiellement gênantes
"sexe" => "xese",
"sexy" => "xyse",
"suce" => "cesu",
"bite" => "tebi",
"cabo" => "boca",
"pipe" => "pepi",
"cocu" => "cuco",
"pute" => "tepu",
];
function __construct(?int $nbBlocks=null, ?int $nbPuncts=null) {
if ($nbBlocks === null) $nbBlocks = static::NB_BLOCKS;
if ($nbPuncts === null) $nbPuncts = static::NB_PUNCTS;
if ($nbBlocks < 1) $nbBlocks = 1;
if ($nbPuncts > $nbBlocks) $nbPuncts = $nbBlocks;
$this->nbBlocks = $nbBlocks;
$this->nbPuncts = $nbPuncts;
}
/** @var int */
protected $nbBlocks, $nbPuncts;
function generate(?int $nbBlocks=null, ?int $nbPuncts=null, ?int $seed=null): string {
if ($nbBlocks === null) $nbBlocks = $this->nbBlocks;
if ($nbPuncts === null) $nbPuncts = $this->nbPuncts;
if ($seed !== null) mt_srand($seed);
$maxc = strlen(self::CONSONNES) - 1;
$maxv = strlen(self::VOYELLES) - 1;
$maxp = strlen(self::PUNCTS) - 1;
$size = $nbBlocks * 2;
$sindex = 0;
$syllabes = [];
while ($sindex < $size) {
$lindex = count($syllabes) - 1;
do {
$consonne = self::CONSONNES[mt_rand(0, $maxc)];
$voyelle = self::VOYELLES[mt_rand(0, $maxv)];
$syllabe = $consonne.$voyelle;
# pas deux fois la même syllabe de suite
$repetition = ($lindex >= 0 && $syllabes[$lindex] == $syllabe)
|| ($lindex >= 1 && $syllabes[$lindex - 1] == $syllabe);
} while ($repetition);
$syllabes[] = $syllabe;
$sindex++;
if ($nbPuncts > 0 && $sindex % 2 == 0 && mt_rand() % 2 == 0) {
$syllabes[] = self::PUNCTS[mt_rand(0, $maxp)];
$nbPuncts--;
}
}
$password = implode("", $syllabes);
foreach (self::MAPPINGS as $from => $to) {
$password = str_replace($from, $to, $password);
}
return $password;
}
}

View File

@ -0,0 +1,76 @@
<?php
namespace nur\passwd;
use nur\b\IllegalAccessException;
use nur\b\ValueException;
class EtuPasswordGenerator {
const V2 = 2, V3 = 3, V4 = 4;
function __construct(?int $version=null) {
if ($version === null) $version = self::V4;
$this->version = $version;
}
private $version;
function getVersion(): int {
return $this->version;
}
function generate(int $codEtu, ?int $version=null): string {
if ($version === null) $version = $this->version;
switch ($version) {
case self::V2: return $this->generate2($codEtu);
case self::V3: return $this->generate3($codEtu);
case self::V4: return $this->generate4($codEtu);
default: throw ValueException::invalid_value($version, "version");
}
}
private static $generate2_groups;
static function init_generate2(array $groups): void {
self::$generate2_groups = $groups;
}
protected function generate2(int $codEtu): string {
$groups = self::$generate2_groups;
if ($groups == null) {
throw IllegalAccessException::unexpected_state("init_generate2");
}
$pass = "";
for ($i = 0; $i < 8; $i++) {
$group = $groups[$i];
$grouplen = strlen($group);
$pass .= $group[$codEtu % $grouplen];
}
return $pass;
}
/** @var PasswordGenerator */
private $pg;
/** @var callable */
private static $gen3seed;
static function init_gen3seed(callable $seed): void {
self::$gen3seed = $seed;
}
protected function generate3(int $codEtu): string {
if ($this->pg === null) $this->pg = new PasswordGenerator();
return $this->pg->generate(3, 1, (self::$gen3seed)($codEtu));
}
/** @var callable */
private static $gen4seed;
static function init_gen4seed(callable $seed): void {
self::$gen4seed = $seed;
}
protected function generate4(int $codEtu): string {
if ($this->pg === null) $this->pg = new PasswordGenerator();
return $this->pg->generate(4, 1, (self::$gen4seed)($codEtu));
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace nur\passwd;
class PasswordGenerator extends AbstractGenerator {
function generate(?int $nbBlocks=null, ?int $nbPuncts=null, ?int $seed=null): string {
# toujours réinitialiser le seed
if ($seed === null) mt_srand();
return parent::generate($nbBlocks, $nbPuncts, $seed);
}
}

View File

@ -0,0 +1,100 @@
<?php
namespace nur\passwd;
use nur\b\IllegalAccessException;
/**
* Class passwords; outils pour gérer les mots de passe
*/
class passwords {
/** obtenir le salt d'un mot de passe hashé en CRYPT */
static function get_des_crypt_salt(string $crypt): string {
return substr($crypt, 0, 2);
}
static function des_crypt(string $clear, ?string $salt=null): string {
if ($salt === null) $salt = "AA";
return crypt($clear, $salt);
}
static function sha(string $clear): string {
return base64_encode(sha1($clear, true));
}
static function xsha(string $clear): string {
return strtoupper(sha1($clear));
}
/** obtenir le salt d'un mot de passe hashé en SSHA */
static function get_ssha_salt(string $ssha): string {
return substr(base64_decode($ssha), 20);
}
static function ssha(string $clear, ?string $salt=null): string {
if ($salt === null) $salt = random_bytes(20);
return base64_encode(sha1("$clear$salt", true).$salt);
}
static function ntlm(string $clear): string {
$string = iconv("UTF-8", "UTF-16LE", $clear);
return strtoupper(bin2hex(hash("md4", $string, true)));
}
private static function lm_des_encrypt(string $string) {
$len = strlen($string);
$tmp = [];
for ($i = 0; $i < 7; $i++) {
$tmp[] = $i < $len? ord($string[$i]): 0;
}
$key = [];
$key[] = $tmp[0] & 254;
$key[] = ($tmp[0] << 7) | ($tmp[1] >> 1);
$key[] = ($tmp[1] << 6) | ($tmp[2] >> 2);
$key[] = ($tmp[2] << 5) | ($tmp[3] >> 3);
$key[] = ($tmp[3] << 4) | ($tmp[4] >> 4);
$key[] = ($tmp[4] << 3) | ($tmp[5] >> 5);
$key[] = ($tmp[5] << 2) | ($tmp[6] >> 6);
$key[] = $tmp[6] << 1;
$key0 = "";
foreach ($key as $k) {
$key0 .= chr($k);
}
$crypt = openssl_encrypt("KGS!@#$%", "des-ecb", $key0
, OPENSSL_RAW_DATA + OPENSSL_ZERO_PADDING);
return bin2hex($crypt);
}
static function lm(string $clear): string {
$string = strtoupper(substr($clear,0,14));
$part1 = self::lm_des_encrypt(substr($string, 0, 7));
$part2 = self::lm_des_encrypt(substr($string, 7, 7));
return strtoupper($part1.$part2);
}
private static $lsc_key;
static function init_lsc(string $key): void {
self::$lsc_key = hex2bin($key);
}
private static function lsc_key(): string {
$lsc_key = self::$lsc_key;
if ($lsc_key === null) {
throw IllegalAccessException::unexpected_state("init_lsc");
}
return $lsc_key;
}
static function is_lsc_available(): bool {
return self::$lsc_key !== null;
}
static function decrypt_lsc(string $lsc): string {
return openssl_decrypt($lsc, "aes-128-ecb", self::lsc_key());
}
static function encrypt_lsc(string $clear): string {
return openssl_encrypt($clear, "aes-128-ecb", self::lsc_key());
}
}

51
nur_tbin/dump-passwords.php Executable file
View File

@ -0,0 +1,51 @@
#!/usr/bin/php
<?php
require __DIR__.'/../vendor/autoload.php';
use nur\A;
use nur\cli\Application;
use nur\passwd\EtuPasswordGenerator;
Application::run(new class extends Application {
const ARGS = [
["-2", "name" => "version", "value" => 2],
["-3", "name" => "version", "value" => 3],
["-4", "name" => "version", "value" => 4],
];
protected $version;
protected $args;
function main() {
$version = $this->version;
if ($version === null) $version = 4;
$pg = new class($version) extends EtuPasswordGenerator {
function gen3seed(int $codEtu): int {
return parent::gen3seed($codEtu);
}
function gen4seed(int $codEtu): int {
return parent::gen4seed($codEtu);
}
};
$year = intval(date("y"));
$prefix = intval(A::get($this->args, 0, $year + 20)) % 100;
$codetu = $prefix * 1000000 + 1;
$max = ($prefix + 1) * 1000000;
$prev = 0;
echo "codetu,seed,delta,password\n";
while ($codetu < $max) {
switch ($version) {
case 3: $seed = $pg->gen3seed($codetu); break;
case 4: $seed = $pg->gen4seed($codetu); break;
default: $seed = null; break;
}
$delta = $seed - $prev;
$password = $pg->generate($codetu);
echo "$codetu,$seed,$delta,\"$password\"\n";
$prev = $seed;
$codetu++;
}
}
});

View File

@ -0,0 +1,12 @@
<?php
namespace nur\passwd;
use nur\t\TestCase;
class PasswordGeneratorTest extends TestCase {
function testGenerate(): void {
$pg = new PasswordGenerator();
self::assertSame("gurazefecivuvali@", $pg->generate(null, null, 0));
self::assertSame("gurazefe", $pg->generate(2, null, 0));
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace nur\passwd;
use PHPUnit\Framework\TestCase;
class passwordsTest extends TestCase {
function testNtlmPasswords(): void {
$password = "tagada";
self::assertSame("A725F22CD78658E45CF6E08670BFBA70", passwords::ntlm($password));
self::assertSame("93F202BB581683A7AAD3B435B51404EE", passwords::lm($password));
$password = "tagadatsointsoinpouetpouet";
self::assertSame("F25F2A1C672FEE777A480411AC0D68F6", passwords::ntlm($password));
self::assertSame("06E39E786C60859072548ACC457CF244", passwords::lm($password));
}
}

4
run-tests Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
mydir="$(dirname -- "$0")"
"$mydir/vendor/bin/phpunit" --bootstrap "$mydir/vendor/autoload.php" "$@" "$mydir/nur_tests"

View File

@ -46,3 +46,10 @@ FROM=../nur-mapper
sy src/ nur_src/mapper/
sy tests/ nur_tests/mapper/
sy tbin/ nur_tbin/
FROM=../nur-passwd
sy src/ nur_src/passwd/
sy tests/ nur_tests/passwd/
sy tbin/ nur_tbin/
./nur_sbin/update_classes.php