public function testVerify() { $value = 'value'; $signature = 'signature'; $publicKey = 'public_key'; $encryptedValue = 'encrypted_value'; $this->encryption->setPublicKey($publicKey); $this->algorithm->expects($this->once())->method('verify')->with($value, $signature, $publicKey)->will($this->returnValue($encryptedValue)); $this->encryption->verify($value, $signature); }
/** * This method stores the necessary bits of data in this object. * * @param Channel $chan * @param array $updateData * @return void * @throws CouldNotUpdate * @throws NoSupplier */ protected function unpackMessageUpdate(Channel $chan, array $updateData) { // This is the JSON message from the tree node, stored as an array: $this->updateMessage = $updateData; if ($this->isPackageUpdate() || $this->isAirshipUpdate()) { // These aren't signed for updating the tree. return; } // We need a precise format: $dateGen = (new \DateTime($this->stored['date_generated']))->format(\AIRSHIP_DATE_FORMAT); $messageToSign = ['action' => $this->action, 'date_generated' => $dateGen, 'public_key' => $updateData['public_key'], 'supplier' => $updateData['supplier'], 'type' => $updateData['type']]; try { $this->supplier = $this->loadSupplier($chan, $updateData); } catch (NoSupplier $ex) { if (!$this->isNewSupplier) { throw $ex; } } // If this isn't a new supplier, we need to verify the key if ($this->isNewSupplier) { return; } if ($updateData['master'] === null) { throw new CouldNotUpdate(\__('The master data is NULL, but the supplier exists.')); } $master = \json_decode($updateData['master'], true); foreach ($this->supplier->getSigningKeys() as $supKey) { // Yes, this is (in fact) a SignaturePublicKey: if (IDE_HACKS) { $supKey['key'] = new SignaturePublicKey(); } if ($supKey['type'] !== 'master') { continue; } $pub = \Sodium\bin2hex($supKey['key']->getRawKeyMaterial()); // Is this the key we're looking for? if (\hash_equals($pub, $master['public_key'])) { // Store the public key $this->supplierMasterKeyUsed = $supKey['key']; break; } } if (empty($this->supplierMasterKeyUsed)) { throw new CouldNotUpdate(\__('The provided public key does not match any known master key.')); } $encoded = \json_encode($messageToSign); if (!Asymmetric::verify($encoded, $this->supplierMasterKeyUsed, $master['signature'])) { throw new CouldNotUpdate(\__('Invalid signature for this master key.')); } }
/** * Get/verify/parse a JSON response * * The _server_ is the one that signs the message. * We're just verifying the Ed25519 signature. * * @param string $url * @param SignaturePublicKey $publicKey * @param array $args * @param array $options * @return array * @throws \Exception */ public static function postSignedJSON(string $url, SignaturePublicKey $publicKey, array $args = [], array $options = []) : array { $body = self::post($url, $args, $options); if (empty($body)) { throw new \Exception('Empty response from ' . $url); } if (self::$debug) { \var_dump($body); } $firstNewLine = \strpos($body, "\n"); // There should be a newline immediately after the base64urlsafe-encoded signature if ($firstNewLine !== self::ENCODED_SIGNATURE_LENGTH) { throw new \Exception('Invalid Signature'); } $sig = Base64UrlSafe::decode(Binary::safeSubstr($body, 0, self::ENCODED_SIGNATURE_LENGTH)); $msg = Binary::safeSubstr($body, self::ENCODED_SIGNATURE_LENGTH + 1); if (!Asymmetric::verify($msg, $publicKey, $sig, true)) { throw new \Exception('Invalid Signature'); } return \json_decode($msg, true); }
/** * Parse a signed JSON response * * @param Response $response * @param SignaturePublicKey $publicKey * @return mixed * @throws SignatureFailed * @throws TransferException */ public function parseSignedJSON(Response $response, SignaturePublicKey $publicKey) { $code = $response->getStatusCode(); if ($code >= 200 && $code < 300) { $body = (string) $response->getBody(); $firstNewLine = \strpos($body, "\n"); // There should be a newline immediately after the base64urlsafe-encoded signature if ($firstNewLine !== self::ENCODED_SIGNATURE_LENGTH) { throw new SignatureFailed(\sprintf("First newline found at position %s, expected %d.\n%s", \print_r($firstNewLine, true), \print_r(self::ENCODED_SIGNATURE_LENGTH, true), Base64::encode($body))); } $sig = Base64UrlSafe::decode(Binary::safeSubstr($body, 0, 88)); $msg = Binary::safeSubstr($body, 89); if (!Asymmetric::verify($msg, $publicKey, $sig, true)) { throw new SignatureFailed(); } return \Airship\parseJSON($msg, true); } throw new TransferException(); }
/** * @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)); }