3 ) { return new WP_Error( 'orderguard_signal_request_invalid', __( 'The network signal could not be prepared.', 'orderguard' ) ); } if ( 'none' === $signal_type ) { $order_ref = null; $order_created_at = null; } elseif ( ! is_string( $order_ref ) || ! preg_match( '/^[1-9][0-9]*$/D', $order_ref ) || ! is_int( $order_created_at ) || $order_created_at < 1 ) { return new WP_Error( 'orderguard_signal_request_invalid', __( 'The network signal could not be prepared.', 'orderguard' ) ); } return array( 'version' => self::VERSION, 'installation_id' => (string) $installation_id, 'operation_id' => (string) $operation_id, 'signal_key' => (string) $signal_key, 'source_revision' => (string) $revision, 'signal_type' => (string) $signal_type, 'hashes' => $hashes, 'order_ref' => $order_ref, 'order_created_at' => $order_created_at, ); } public static function encode( $request ) { if ( ! is_array( $request ) || array_keys( $request ) !== array( 'version', 'installation_id', 'operation_id', 'signal_key', 'source_revision', 'signal_type', 'hashes', 'order_ref', 'order_created_at' ) ) { return ''; } return self::canonical( $request ); } public static function digest( $request ) { $encoded = self::encode( $request ); return '' === $encoded ? '' : hash( 'sha256', "og-flag-signal-request-v1\n" . $encoded ); } public static function receipt( $request, $body, $trusted_keys, $environment = 'production' ) { $unknown = static function ( $reason ) { return array( 'status' => 'remote_unknown', 'reason' => $reason, 'retry_same_operation' => true ); }; if ( ! function_exists( 'sodium_crypto_sign_verify_detached' ) ) { return $unknown( 'verifier_unavailable' ); } $expected_prefix = 'production' === $environment ? 'prod-' : ( 'review' === $environment ? 'test-' : '' ); if ( '' === $expected_prefix || ! is_array( $trusted_keys ) || empty( $trusted_keys ) ) { return $unknown( 'no_keys' ); } try { $value = json_decode( (string) $body, true, 12, JSON_THROW_ON_ERROR ); } catch ( Throwable $throwable ) { return $unknown( 'invalid_json' ); } if ( ! is_array( $value ) || array_keys( $value ) !== array( 'format', 'algorithm', 'environment', 'key_id', 'receipt', 'signature' ) || self::FORMAT !== ( $value['format'] ?? null ) || 'Ed25519' !== ( $value['algorithm'] ?? null ) || $environment !== ( $value['environment'] ?? null ) || ! is_string( $value['key_id'] ?? null ) || 0 !== strpos( $value['key_id'], $expected_prefix ) || ! isset( $trusted_keys[ $value['key_id'] ] ) || ! is_array( $value['receipt'] ?? null ) || ! is_string( $value['signature'] ?? null ) ) { return $unknown( 'envelope_invalid' ); } $public = (string) $trusted_keys[ $value['key_id'] ]; if ( ! preg_match( '/^[a-f0-9]{64}$/D', $public ) || ! preg_match( '/^[a-f0-9]{128}$/D', $value['signature'] ) ) { return $unknown( 'key_invalid' ); } $unsigned = $value; unset( $unsigned['signature'] ); $message = "og-flag-signal-signature-v1\n" . self::canonical( $unsigned ); if ( ! sodium_crypto_sign_verify_detached( hex2bin( $value['signature'] ), $message, hex2bin( $public ) ) ) { return $unknown( 'bad_signature' ); } $receipt = $value['receipt']; if ( array_keys( $receipt ) !== array( 'version', 'status', 'installation_id', 'operation_id', 'signal_key', 'source_revision', 'request_digest', 'applied' ) || self::VERSION !== ( $receipt['version'] ?? null ) || 'committed' !== ( $receipt['status'] ?? null ) || ( $request['installation_id'] ?? null ) !== ( $receipt['installation_id'] ?? null ) || ( $request['operation_id'] ?? null ) !== ( $receipt['operation_id'] ?? null ) || ( $request['signal_key'] ?? null ) !== ( $receipt['signal_key'] ?? null ) || ( $request['source_revision'] ?? null ) !== ( $receipt['source_revision'] ?? null ) || self::digest( $request ) !== ( $receipt['request_digest'] ?? null ) || ! is_bool( $receipt['applied'] ?? null ) ) { return $unknown( 'binding_mismatch' ); } return array( 'status' => 'authenticated_signal_receipt', 'operation_id' => $receipt['operation_id'], 'source_revision' => (int) $receipt['source_revision'], 'applied' => $receipt['applied'], 'key_id' => $value['key_id'], ); } }