/** * Execute the start command, which will start a new hangar session. * * @param array $args * @return bool * @throws \Error */ public function fire(array $args = []) : bool { $file = $this->selectFile($args[0] ?? ''); if (!isset($this->config['salt']) && \count($args) < 2) { throw new \Error('No salt configured or passed'); } if (\count($args) > 2) { switch (\strtolower($args[2])) { case 'fast': case 'i': case 'interactive': case 'weak': $level = KeyFactory::INTERACTIVE; break; case 'm': case 'signing': case 'moderate': $level = KeyFactory::MODERATE; break; default: $level = KeyFactory::SENSITIVE; break; } } elseif (isset($this->config['keytype'])) { switch ($this->config['keytype']) { case 'fast': case 'i': case 'interactive': case 'weak': $level = KeyFactory::INTERACTIVE; break; case 'm': case 'signing': case 'moderate': $level = KeyFactory::MODERATE; break; default: $level = KeyFactory::SENSITIVE; break; } } else { $level = KeyFactory::SENSITIVE; } $salt = \Sodium\hex2bin($args[1] ?? $this->config['salt']); echo 'Generating a signature for: ', $file, "\n"; $password = $this->silentPrompt('Enter password: '******'false' in version 2.0.0 (with Halite 3) $sign_kp = KeyFactory::deriveSignatureKeyPair($password, $salt, false, $level); if (!$sign_kp instanceof SignatureKeyPair) { throw new \Error('Error during key derivation'); } $signature = File::sign($file, $sign_kp->getSecretKey()); if (isset($this->history)) { $this->config['build_history']['signed'] = true; } \file_put_contents($file . '.sig', $signature); echo 'File signed: ' . $file . '.sig', "\n"; echo 'Public key: ' . \Sodium\bin2hex($sign_kp->getPublicKey()->getRawKeyMaterial()), "\n"; return true; }
/** * Common signing process. User selects key, provides password. * * @param array $manifest * @return SignatureSecretKey * @throws \Exception */ protected function signPreamble(array $manifest) : SignatureSecretKey { $HTAB = \str_repeat(' ', \intdiv(self::TAB_SIZE, 2)); $supplier_name = $manifest['supplier']; // Sanity checks: if (!\array_key_exists('suppliers', $this->config)) { echo 'You are not authenticated for any suppliers.', "\n"; exit(255); } if (!\array_key_exists($supplier_name, $this->config['suppliers'])) { echo 'Check the supplier in the JSON file (', $supplier_name, ') for correctness.', 'Otherwise, you might need to log in.', "\n"; exit(255); } $supplier = $this->config['suppliers'][$supplier_name]; $numKeys = 0; if ($this->signWithMasterKeys) { $good_keys = []; // This should really not be used: $numKeys = \count($supplier['signing_keys']); foreach ($supplier['signing_keys'] as $k) { if (!empty($k['salt'])) { $good_keys[] = $k; ++$numKeys; } } } else { // This should be used instead: $good_keys = []; foreach ($supplier['signing_keys'] as $k) { if ($k['type'] === 'signing' && !empty($k['salt'])) { $good_keys[] = $k; ++$numKeys; } } } if ($numKeys > 1) { echo 'You have more than one signing key available.', "\n"; $n = 1; $size = (int) \floor(\log($numKeys, 10)); $key_associations = $HTAB . "ID\t Public Key " . \str_repeat(' ', 33) . "\t Type\n"; foreach ($supplier['signing_keys'] as $sign_key) { if (!$this->signWithMasterKeys && $sign_key['type'] === 'master') { continue; } $_n = \str_pad($n, $size, ' ', STR_PAD_LEFT); // Short format: $pk = Base64UrlSafe::encode(\Sodium\hex2bin($sign_key['public_key'])); $key_associations .= $HTAB . $_n . $HTAB . $pk . $HTAB . $sign_key['type'] . "\n"; ++$n; } // Let's ascertain the user's key selection do { echo $key_associations; $choice = (int) $this->prompt('Enter the ID for the key you wish to use: '); if ($choice < 1 || $choice > $numKeys) { $choice = null; } } while (empty($choice)); $supplierKey = $good_keys[$choice - 1]; echo "\n"; } else { $supplierKey = $good_keys[0]; } // The above !empty($k['salt']) check should have rendered this check redundant: if (empty($supplierKey['salt'])) { echo 'Salt not found for this key. It is not possible to reproduce it.', "\n"; exit(255); } // Short format: $pk = Base64UrlSafe::encode(\Sodium\hex2bin($supplierKey['public_key'])); // Color coded: Master keys are red, since they take longer. // We don't support signing packages with a master key, but // this decision could be undone in the future. $c = $supplierKey['type'] === 'master' ? $this->c['red'] : $this->c['yellow']; echo 'Selected ', $supplierKey['type'], ' key: ', $c, $pk, $this->c[''], "\n"; $password = $this->silentPrompt('Enter Password for Signing Key:'); // Derive and split the SignatureKeyPair from your password and salt $salt = \Sodium\hex2bin($supplierKey['salt']); switch ($supplierKey['type']) { case 'signing': $type = KeyFactory::MODERATE; echo 'Verifying (this may take a second or two)...'; break; case 'master': $type = KeyFactory::SENSITIVE; echo 'Verifying (this may take a few seconds)...'; break; default: $type = KeyFactory::INTERACTIVE; echo 'Verifying...'; } $keyPair = KeyFactory::deriveSignatureKeyPair($password, $salt, false, $type); $sign_secret = $keyPair->getSecretKey(); $sign_public = $keyPair->getPublicKey(); echo ' Done.', "\n"; // We don't need this anymore. \Sodium\memzero($password); // Check that the public key we derived from the password matches the one on file $pubKey = \Sodium\bin2hex($sign_public->getRawKeyMaterial()); if (!\hash_equals($supplierKey['public_key'], $pubKey)) { // Zero the memory ASAP unset($sign_secret); unset($sign_public); echo 'Invalid password for selected key', "\n"; exit(255); } // Zero the memory ASAP unset($sign_public); return $sign_secret; }