/** * Transform key * * @param info.keepass.Header $header * @return string */ public function transform($header) { $hash = hash(self::ALGORITHM, hash(self::ALGORITHM, $this->passphrase->reveal(), true), true); // Rounds is a 64-bit integer, which cannot be handled by PHP. Use the // four 16-bit unsigned ints into ones, tens and hundreds, then iterate. $rounds = $header->rounds; $ones = $rounds[0] | ($rounds[1] & 0x3fff) << 0x10; $tens = ($rounds[1] & 0xc000) >> 0xe | $rounds[2] << 0x2 | ($rounds[3] & 0xfff) << 0x12; $hundreds = ($rounds[3] & 0xf000) >> 0xc; $cipher = new Cipher('aes-256-ecb', $header->transformSeed, ''); do { $hash = $cipher->encrypt($hash, $ones); if ($tens > 0) { $tens--; $ones = 0x40000000; } else { if ($hundreds > 0) { $hundreds--; $tens = 0x3fffffff; $ones = 0x40000000; } else { $ones = 0; } } } while ($ones); return hash(self::ALGORITHM, $header->masterSeed . hash(self::ALGORITHM, $hash, true), true); }
/** * Opens a KeePass database file * * @param io.streams.InputStream $input * @param info.keepass.Key $key * @return self * @throws lang.FormatException * @throws info.keepass.CannotDecrypt */ public static function open($input, Key $key) { $self = new self(); with(new Reader($input, 'sha256'), function ($reader) use($self, $key) { $self->version = $reader->version(); $self->header = $reader->header(); $cipher = new Cipher($self->header->algorithm(), $key->transform($self->header), $self->header->encryptionIV); $decrypted = $cipher->decrypt($reader->remaining()); $start = strlen($self->header->startBytes); if (0 !== strncmp($decrypted, $self->header->startBytes, $start)) { throw new CannotDecrypt('Incorrect passphrase?'); } $self->blocks = new Blocks($decrypted, $start); }); return $self; }