public function testDeriveSigningKey() { list($sign_secret, $sign_public) = ASecretKey::deriveFromPassword('apple', "\t\n\v\f\r" . "", Key::CRYPTO_SIGN); $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->get(), "þ\t†E·õÂbÈagÖ\t" . "•§”\\æÕU–ãuˆ4"); }
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º"); }
public function testLegacyDeriveSigningKey() { $keypair = KeyFactory::deriveSignatureKeyPair('apple', "\t\n\v\f\r" . "", true); $sign_secret = $keypair->getSecretKey(); $sign_public = $keypair->getPublicKey(); $this->assertTrue($sign_secret instanceof SignatureSecretKey); $this->assertTrue($sign_public instanceof SignaturePublicKey); // 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�E���b�ag�\t" . "���\\��U��u�4"); }
/** * 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; }
/** * Unseal a (file handle) * * @param $input * @param $output * @param \ParagonIE\Halite\Contract\CryptoKeyInterface $secretkey */ public static function unsealResource($input, $output, \ParagonIE\Halite\Contract\CryptoKeyInterface $secretkey) { // Input validation if (!\is_resource($input)) { throw new \ParagonIE\Halite\Alerts\InvalidType('Expected input handle to be a resource'); } if (!\is_resource($output)) { throw new \ParagonIE\Halite\Alerts\InvalidType('Expected output handle to be a resource'); } if (!$secretkey->isSecretKey()) { throw new CryptoException\InvalidKey('Expected a secret key'); } if (!$secretkey->isAsymmetricKey()) { throw new CryptoException\InvalidKey('Expected a key intended for asymmetric-key cryptography'); } $secret_key = $secretkey->get(); $public_key = \Sodium\crypto_box_publickey_from_secretkey($secret_key); // Parse the header, ensuring we get 4 bytes $header = self::readBytes($input, Halite::VERSION_TAG_LEN); // Load the config $config = self::getConfig($header, 'seal'); // Let's grab the public key and salt $eph_public = self::readBytes($input, $config['PUBLICKEY_BYTES']); $hkdfsalt = self::readBytes($input, $config['HKDF_SALT_LEN']); $nonce = \Sodium\crypto_generichash($eph_public . $public_key, null, \Sodium\CRYPTO_STREAM_NONCEBYTES); $ephemeral = new Key($eph_public, true, false, true); $key = Asymmetric::getSharedSecret($secretkey, $ephemeral, true); list($encKey, $authKey) = self::splitKeys($key, $hkdfsalt); // We no longer need the original key after we split it unset($key); $mac = \hash_init('sha256', HASH_HMAC, $authKey); \hash_update($mac, $header); \hash_update($mac, $eph_public); \hash_update($mac, $hkdfsalt); // This will throw an exception if it fails. $old_macs = self::streamVerify($input, \hash_copy($mac), $config); $ret = self::streamDecrypt($input, $output, new Key($encKey), $nonce, $mac, $config, $old_macs); unset($encKey); unset($authKey); unset($nonce); unset($mac); unset($config); unset($old_macs); return $ret; }
/** * 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']]; }
/** * Verify the contents of a file * * @param $input (file handle) * @param SignaturePublicKey $publickey * @param string $signature * @param bool $raw_binary Don't hex encode? * * @return bool */ public static function verifyStream(ReadOnlyFile $input, KeyInterface $publickey, $signature, $raw_binary = false) { if (!$publickey instanceof SignaturePublicKey) { throw new \ParagonIE\Halite\Alerts\InvalidKey('Argument 2: Expected an instance of SignaturePublicKey'); } $csum = self::checksumStream($input, null, true); return AsymmetricCrypto::verify($csum, $publickey, $signature, $raw_binary); }
/** * @covers Asymmetric::sign() * @covers Asymmetric::verify() */ public function testSignFail() { $alice = KeyFactory::generateSignatureKeyPair(); $message = 'test message'; $signature = Asymmetric::sign($message, $alice->getSecretKey(), true); $this->assertFalse(Asymmetric::verify('wrongmessage', $alice->getPublicKey(), $signature, true)); $_signature = $signature; // Let's flip one bit, randomly: $r = \Sodium\randombytes_uniform(\mb_strlen($signature, '8bit')); $_signature[$r] = \chr(\ord($_signature[$r]) ^ 1 << \Sodium\randombytes_uniform(8)); $this->assertFalse(Asymmetric::verify($message, $alice->getPublicKey(), $_signature, true)); }