PHP Classes

File: src/HPKE.php

Recommend this page to a friend!
  Classes of Scott Arciszewski   HPKE PHP   src/HPKE.php   Download  
File: src/HPKE.php
Role: Class source
Content type: text/plain
Description: Class source
Class: HPKE PHP
Encrypt and decrypt data using hybrid public keys
Author: By
Last change:
Date: 8 days ago
Size: 11,953 bytes
 

Contents

Class file image Download
<?php declare(strict_types=1); namespace ParagonIE\HPKE; use ParagonIE\HPKE\Context\{ Receiver, Sender }; use ParagonIE\HPKE\Interfaces\{ AEADInterface, DecapsKeyInterface, EncapsKeyInterface, KDFInterface, KemInterface, SymmetricKeyInterface }; use SensitiveParameter; use TypeError; class HPKE { /** * @param KemInterface $kem * @param KDFInterface $kdf * @param AEADInterface $aead */ public function __construct( public KemInterface $kem, public KDFInterface $kdf, public AEADInterface $aead ) {} /** * @return string */ public function getSuiteId(): string { return 'HPKE' . $this->kem->getKemId() . $this->kdf->getKdfId() . $this->aead->getAeadId(); } /** * @param EncapsKeyInterface $pk * @param string $plaintext * @param string $aad * @param string $info * @return string * * @throws HPKEException */ public function sealBase( EncapsKeyInterface $pk, string $plaintext, string $aad = '', string $info = '' ): string { [$enc, $ctx] = $this->setupBaseSender($pk, $info); return $enc . $ctx->seal($plaintext, $aad); } /** * @param DecapsKeyInterface $sk * @param string $ciphertext * @param string $aad * @param string $info * @return string * @throws HPKEException */ public function openBase( DecapsKeyInterface $sk, string $ciphertext, string $aad = '', string $info = '' ): string { $len = $this->kem->getHeaderLength(); $enc = substr($ciphertext, 0, $len); $ct = substr($ciphertext, $len); $ctx = $this->setupBaseReceiver($sk, $enc, $info); return $ctx->open($ct, $aad); } /** * @param EncapsKeyInterface $pk * @param string $info * @return array{0: string, 1: Sender} * * @throws HPKEException */ public function setupBaseSender(EncapsKeyInterface $pk, string $info = ''): array { [$shared_secret, $enc] = $this->kem->withHPKE($this)->encapsulate($pk); return [ $enc, $this->keySchedule(Role::Sender, Mode::Base, $shared_secret, $info) ]; } /** * @param DecapsKeyInterface $sk * @param string $enc * @param string $info * @return Receiver * * @throws HPKEException */ public function setupBaseReceiver( #[SensitiveParameter] DecapsKeyInterface $sk, string $enc, string $info = '' ): Receiver { $shared_secret = $this->kem->withHPKE($this)->decapsulate($sk, $enc); $recv = $this->keySchedule(Role::Receiver, Mode::Base, $shared_secret, $info); if (!$recv instanceof Receiver) { throw new TypeError(); } return $recv; } /** * @param EncapsKeyInterface $pk * @param string $psk * @param string $pskID * @param string $info * @return array * * @throws HPKEException */ public function setupPSKSender( EncapsKeyInterface $pk, #[SensitiveParameter] string $psk, string $pskID, string $info = '' ): array { [$shared_secret, $enc] = $this->kem->withHPKE($this)->encapsulate($pk); return [ $enc, $this->keySchedule(Role::Sender, Mode::PSK, $shared_secret, $info, $psk, $pskID) ]; } /** * @param DecapsKeyInterface $sk * @param string $enc * @param string $psk * @param string $pskID * @param string $info * @return Receiver * * @throws HPKEException */ public function setupPSKReceiver( #[SensitiveParameter] DecapsKeyInterface $sk, string $enc, #[SensitiveParameter] string $psk, string $pskID, string $info = '' ): Receiver { $shared_secret = $this->kem->withHPKE($this)->decapsulate($sk, $enc); $recv = $this->keySchedule(Role::Receiver, Mode::PSK, $shared_secret, $info, $psk, $pskID); if (!$recv instanceof Receiver) { throw new TypeError(); } return $recv; } /** * @param EncapsKeyInterface $pk * @param DecapsKeyInterface $sk * @param string $info * @return array * * @throws HPKEException */ public function setupAuthSender( EncapsKeyInterface $pk, #[SensitiveParameter] DecapsKeyInterface $sk, string $info = '' ): array { [$shared_secret, $enc] = $this->kem->withHPKE($this)->authEncaps($pk, $sk); return [ $enc, $this->keySchedule(Role::Sender, Mode::Auth, $shared_secret, $info) ]; } /** * @param DecapsKeyInterface $sk * @param EncapsKeyInterface $pk * @param string $enc * @param string $info * @return Receiver * * @throws HPKEException */ public function setupAuthReceiver( #[SensitiveParameter] DecapsKeyInterface $sk, EncapsKeyInterface $pk, string $enc, string $info = '' ): Receiver { $shared_secret = $this->kem->withHPKE($this)->authDecaps($sk, $pk, $enc); $recv = $this->keySchedule(Role::Receiver, Mode::Auth, $shared_secret, $info); if (!$recv instanceof Receiver) { throw new TypeError('Expected a receiver, did not get one'); } return $recv; } public function setupAuthPSKSender( EncapsKeyInterface $pk, #[SensitiveParameter] DecapsKeyInterface $sk, #[SensitiveParameter] string $psk, string $pskID, string $info = '' ): array { [$shared_secret, $enc] = $this->kem->withHPKE($this)->authEncaps($pk, $sk); return [ $enc, $this->keySchedule(Role::Sender, Mode::AuthPSK, $shared_secret, $info, $psk, $pskID) ]; } public function setupAuthPSKReceiver( #[SensitiveParameter] DecapsKeyInterface $sk, EncapsKeyInterface $pk, string $enc, #[SensitiveParameter] string $psk, string $pskID, string $info = '' ): Receiver { $shared_secret = $this->kem->withHPKE($this)->authDecaps($sk, $pk, $enc); $recv = $this->keySchedule(Role::Receiver, Mode::Auth, $shared_secret, $info, $psk, $pskID); if (!$recv instanceof Receiver) { throw new TypeError(); } return $recv; } /** * @param string|SymmetricKeyInterface $ikm * @param string $label * @param ?string $salt * @return string */ public function labeledExtract( #[SensitiveParameter] string|SymmetricKeyInterface $ikm, #[SensitiveParameter] string $label, #[SensitiveParameter] ?string $salt = null ): string { return $this->kdf->labeledExtract($this->getSuiteId(), $ikm, $label, $salt); } /** * @param string|SymmetricKeyInterface $prk * @param string $label * @param string $info * @param int $length * @return string */ public function labeledExpand( #[SensitiveParameter] string|SymmetricKeyInterface $prk, #[SensitiveParameter] string $label, #[SensitiveParameter] string $info, int $length ): string { return $this->kdf->labeledExpand($this->getSuiteId(), $prk, $label, $info, $length); } /** * @param string $dh * @param string $kemContext * @return string */ public function extractAndExpand( #[SensitiveParameter] string $dh, string $kemContext ): string { return $this->kdf->extractAndExpand( $this->getSuiteId(), $dh, $kemContext, $this->aead->keyLength() ); } /** * @param Role $role * @param Mode $mode * @param string|SymmetricKeyInterface $sharedSecret * @param string $info * @param string|SymmetricKeyInterface $psk * @param string $pskID * @return Context * * @throws HPKEException */ private function keySchedule( Role $role, Mode $mode, #[SensitiveParameter] string|SymmetricKeyInterface $sharedSecret, #[SensitiveParameter] string $info = '', #[SensitiveParameter] string|SymmetricKeyInterface $psk = '', #[SensitiveParameter] string $pskID = '', ): Context { // Coerce string $_psk = $psk instanceof SymmetricKeyInterface ? $psk->bytes : $psk; $this->verifyPSKInputs($mode, $_psk, $pskID); $psk_id_hash = $this->labeledExtract(ikm: $pskID, label: 'psk_id_hash'); $info_hash = $this->labeledExtract(ikm: $info, label: 'info_hash'); $key_schedule_context = $mode->value . $psk_id_hash . $info_hash; $secret = $this->labeledExtract( ikm: $_psk, label: 'secret', salt: $sharedSecret instanceof SymmetricKeyInterface ? $sharedSecret->bytes : $sharedSecret ); $key = new SymmetricKey($this->labeledExpand( prk: $secret, label: 'key', info: $key_schedule_context, length: $this->aead->keyLength() )); $baseNonce = $this->labeledExpand( prk: $secret, label: 'base_nonce', info: $key_schedule_context, length: $this->aead->nonceLength() ); $exporterSecret = $this->labeledExpand( prk: $secret, label: 'exp', info: $key_schedule_context, length: $this->kdf->getHashLength() ); return match ($role) { Role::Receiver => new Receiver( $this, $key, $baseNonce, 0, $exporterSecret ), Role::Sender => new Sender( $this, $key, $baseNonce, 0, $exporterSecret ), }; } /** * @param Mode $mode * @param string $psk * @param string $pskId * @return void * * @throws HPKEException */ private function verifyPSKInputs( Mode $mode, string $psk = '', string $pskId = '' ): void { $maxLength = (PHP_INT_SIZE << 3) - 1; $gotPsk = ~((strlen($psk) - 1) >> $maxLength) & 1; $gotPskId = ~((strlen($pskId) - 1) >> $maxLength) & 1; if ($gotPsk !== $gotPskId) { throw new HPKEException('Inconsistent PSK Inputs'); } if ($gotPsk && in_array($mode, [Mode::Base, Mode::Auth])) { throw new HPKEException('PSK input provided when not needed'); } if (!$gotPskId && in_array($mode, [Mode::PSK, Mode::AuthPSK])) { throw new HPKEException('Missing required PSK input'); } } }