Derive a key pair for public key signatures from a password and salt
public static deriveSignatureKeyPair ( |
||
$password | ||
$salt | string | |
$level | string | Security level for KDF |
리턴 |
public function testDeriveSigningKey() { $keypair = KeyFactory::deriveSignatureKeyPair('apple', "\t\n\v\f\r"); $sign_secret = $keypair->getSecretKey(); $sign_public = $keypair->getPublicKey(); $this->assertTrue($sign_secret instanceof ASecretKey); $this->assertTrue($sign_public instanceof APublicKey); // Can this be used? $message = 'This is a test message'; $signed = Asymmetric::sign($message, $sign_secret); $this->assertTrue(Asymmetric::verify($message, $sign_public, $signed)); $this->assertEquals($sign_public->getRawKeyMaterial(), ".À>¸t\fÿ³ŽêßþŒé9" . "7}ýî¤mÉKƒëT sOº"); }
/** * We are revoking a key. * * @param array $args * @throws \Exception * @return mixed */ protected function handleKeyRevoke(array $args) { if (count($this->config['suppliers']) === 1) { $supplier = \count($args) > 0 ? $args[0] : \array_keys($this->config['suppliers'])[0]; } else { $supplier = \count($args) > 0 ? $args[0] : $this->prompt("Please enter the name of the supplier: "); } if (!\array_key_exists($supplier, $this->config['suppliers'])) { echo 'Please authenticate before attempting to revoke keys.', "\n"; echo 'Run this command: ', $this->c['yellow'], 'barge login', $this->c[''], "\n"; exit(255); } $masterKeys = []; $keyList = []; foreach ($this->config['suppliers'][$supplier]['signing_keys'] as $key) { if ($key['type'] === 'master') { if (!empty($key['salt'])) { $masterKeys[] = $key; } else { $keyList[] = $key; } } else { $keyList[] = $key; } } if (empty($masterKeys)) { echo 'No usable master keys found. Make sure the salt is loaded locally.', "\n"; exit(255); } if (empty($keyList)) { // If and only if you have nothing more to revoke, allow revoking the master key: $keyList = $masterKeys; } if (\count($masterKeys) === 1) { $masterKey = $masterKeys[0]; } else { $masterKey = $this->selectKeyFromList('Select your master key: ', $masterKeys); // Add other master keys to the list foreach ($masterKeys as $key) { if ($key['public_key'] !== $masterKey['public_key']) { $keyList[] = $key; } } } if (\count($keyList) === 1) { $revokingKey = $keyList[0]; } else { $revokingKey = $this->selectKeyFromList('Select which key to revoke: ', $keyList); } $confirm_revoke = null; while ($confirm_revoke === null) { $choice = $this->prompt('Are you sure you wish to revoke this key? (y/N): '); switch ($choice) { case 'YES': case 'yes': case 'Y': case 'y': $confirm_revoke = true; break; case 'N': case 'NO': case 'n': case 'no': case '': // Just pressing enter means "don't store it"! $confirm_revoke = false; break; default: echo "\n", $this->c['yellow'], 'Invalid response. Please enter yes or no.', $this->c[''], "\n"; } } // This is what get signed by our master key: $message = ['action' => 'REVOKE', 'date_revoked' => \date('Y-m-d\\TH:i:s'), 'public_key' => $revokingKey['public_key'], 'supplier' => $supplier]; $messageToSign = \json_encode($message); $iter = false; do { if ($iter) { echo 'Incorrect password.', "\n"; } $password = $this->silentPrompt('Enter the password for your master key: '); if (empty($password)) { // Okay, let's cancel. throw new \Exception('Aborted.'); } $masterKeyPair = KeyFactory::deriveSignatureKeyPair($password, \Sodium\hex2bin($masterKey['salt']), false, KeyFactory::SENSITIVE); \Sodium\memzero($password); $masterPublicKeyString = \Sodium\bin2hex($masterKeyPair->getPublicKey()->getRawKeyMaterial()); $iter = true; } while (!\hash_equals($masterKey['public_key'], $masterPublicKeyString)); $signature = Asymmetric::sign($messageToSign, $masterKeyPair->getSecretKey()); $response = $this->sendRevocation($supplier, $message, $signature, $masterPublicKeyString); if ($response['status'] === 'OK') { foreach ($this->config['suppliers'][$supplier]['signing_keys'] as $i => $key) { if ($key['public_key'] === $message['public_key']) { unset($this->config['suppliers'][$supplier]['signing_keys'][$i]); } } } return $response; }
/** * Sign the new key with our current master key * * @param string $supplier * @param string $messageToSign * @return string[] * @throws \Exception */ protected function signNewKeyWithMasterKey(string $supplier, string $messageToSign) : array { $master_keys = []; foreach ($this->config['suppliers'][$supplier]['signing_keys'] as $key) { if ($key['type'] === 'master' && !empty($key['salt'])) { $master_keys[] = $key; } } // This shouldn't happen, but just in case: if (empty($master_keys)) { throw new \Exception('You cannot generate another key unless you already have a master key with the salt loaded locally.'); } // Select the correct master key. if (\count($master_keys) === 1) { $signingKey = $master_keys[0]; } else { echo 'Select which master key to use:'; do { foreach ($master_keys as $index => $key) { $pk = Base64UrlSafe::encode(\Sodium\hex2bin($key['public_key'])); echo $index + 1, "\t", $pk, "\n"; } $keyIndex = $this->prompt('Enter a number: '); if (empty($keyIndex)) { // Okay, let's cancel. throw new \Exception('Aborted.'); } if ($keyIndex < 1 || $keyIndex > \count($master_keys)) { $keyIndex = 0; echo 'Please enter a number between 1 and ', \count($master_keys), ".\n"; } } while ($keyIndex < 1); $signingKey = $master_keys[--$keyIndex]; } $signature = ''; $masterSalt = \Sodium\hex2bin($signingKey['salt']); do { $password = $this->silentPrompt('Enter the password for your master key: '); if (empty($password)) { // Okay, let's cancel. throw new \Exception('Aborted.'); } $masterKeyPair = KeyFactory::deriveSignatureKeyPair($password, $masterSalt, false, KeyFactory::SENSITIVE); // We must verify the public key matches: $masterPublicKey = $masterKeyPair->getPublicKey(); if (\hash_equals($masterPublicKey->getRawKeyMaterial(), \Sodium\hex2bin($signingKey['public_key']))) { $masterSecretKey = $masterKeyPair->getSecretKey(); // Setting $signature exits the loop $signature = Asymmetric::sign($messageToSign, $masterSecretKey); } else { echo 'Incorrect master key passphrase!', "\n"; } } while (!$signature); // We are returning two strings: return [$signature, $signingKey['public_key']]; }
public function testLegacySignKeyStorage() { $sign_keypair = KeyFactory::deriveSignatureKeyPair('apple', "\t\n\v\f\r" . "", true); $sign_secret = $sign_keypair->getSecretKey(); $sign_public = $sign_keypair->getPublicKey(); $file_secret = \tempnam(__DIR__ . '/tmp', 'key'); $file_public = \tempnam(__DIR__ . '/tmp', 'key'); $this->assertTrue(KeyFactory::save($sign_secret, $file_secret) !== false); $this->assertTrue(KeyFactory::save($sign_public, $file_public) !== false); $load_public = KeyFactory::loadSignaturePublicKey($file_public); $this->assertTrue($load_public instanceof SignaturePublicKey); $this->assertTrue(\hash_equals($sign_public->getRawKeyMaterial(), $load_public->getRawKeyMaterial())); \unlink($file_secret); \unlink($file_public); }