SignedRequest.php 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace OC\Security\Signature\Model;
  8. use JsonSerializable;
  9. use NCU\Security\Signature\Enum\DigestAlgorithm;
  10. use NCU\Security\Signature\Exceptions\SignatoryNotFoundException;
  11. use NCU\Security\Signature\Exceptions\SignatureElementNotFoundException;
  12. use NCU\Security\Signature\ISignedRequest;
  13. use NCU\Security\Signature\Model\Signatory;
  14. /**
  15. * @inheritDoc
  16. *
  17. * @since 31.0.0
  18. */
  19. class SignedRequest implements ISignedRequest, JsonSerializable {
  20. private string $digest = '';
  21. private DigestAlgorithm $digestAlgorithm = DigestAlgorithm::SHA256;
  22. private array $signingElements = [];
  23. private array $signatureData = [];
  24. private string $signature = '';
  25. private ?Signatory $signatory = null;
  26. public function __construct(
  27. private readonly string $body,
  28. ) {
  29. }
  30. /**
  31. * @inheritDoc
  32. *
  33. * @return string
  34. * @since 31.0.0
  35. */
  36. public function getBody(): string {
  37. return $this->body;
  38. }
  39. /**
  40. * set algorithm used to generate digest
  41. *
  42. * @param DigestAlgorithm $algorithm
  43. *
  44. * @return self
  45. * @since 31.0.0
  46. */
  47. protected function setDigestAlgorithm(DigestAlgorithm $algorithm): self {
  48. $this->digestAlgorithm = $algorithm;
  49. return $this;
  50. }
  51. /**
  52. * @inheritDoc
  53. *
  54. * @return DigestAlgorithm
  55. * @since 31.0.0
  56. */
  57. public function getDigestAlgorithm(): DigestAlgorithm {
  58. return $this->digestAlgorithm;
  59. }
  60. /**
  61. * @inheritDoc
  62. *
  63. * @return string
  64. * @since 31.0.0
  65. */
  66. public function getDigest(): string {
  67. if ($this->digest === '') {
  68. $this->digest = $this->digestAlgorithm->value . '=' .
  69. base64_encode(hash($this->digestAlgorithm->getHashingAlgorithm(), $this->body, true));
  70. }
  71. return $this->digest;
  72. }
  73. /**
  74. * @inheritDoc
  75. *
  76. * @param array $elements
  77. *
  78. * @return self
  79. * @since 31.0.0
  80. */
  81. public function setSigningElements(array $elements): self {
  82. $this->signingElements = $elements;
  83. return $this;
  84. }
  85. /**
  86. * @inheritDoc
  87. *
  88. * @return array
  89. * @since 31.0.0
  90. */
  91. public function getSigningElements(): array {
  92. return $this->signingElements;
  93. }
  94. /**
  95. * @param string $key
  96. *
  97. * @return string
  98. * @throws SignatureElementNotFoundException
  99. * @since 31.0.0
  100. *
  101. */
  102. public function getSigningElement(string $key): string { // getSignatureDetail / getSignatureEntry() ?
  103. if (!array_key_exists($key, $this->signingElements)) {
  104. throw new SignatureElementNotFoundException('missing element ' . $key . ' in Signature header');
  105. }
  106. return $this->signingElements[$key];
  107. }
  108. /**
  109. * store data used to generate signature
  110. *
  111. * @param array $data
  112. *
  113. * @return self
  114. * @since 31.0.0
  115. */
  116. protected function setSignatureData(array $data): self {
  117. $this->signatureData = $data;
  118. return $this;
  119. }
  120. /**
  121. * @inheritDoc
  122. *
  123. * @return array
  124. * @since 31.0.0
  125. */
  126. public function getSignatureData(): array {
  127. return $this->signatureData;
  128. }
  129. /**
  130. * set the signed version of the signature
  131. *
  132. * @param string $signature
  133. *
  134. * @return self
  135. * @since 31.0.0
  136. */
  137. protected function setSignature(string $signature): self {
  138. $this->signature = $signature;
  139. return $this;
  140. }
  141. /**
  142. * @inheritDoc
  143. *
  144. * @return string
  145. * @since 31.0.0
  146. */
  147. public function getSignature(): string {
  148. return $this->signature;
  149. }
  150. /**
  151. * @inheritDoc
  152. *
  153. * @param Signatory $signatory
  154. * @return self
  155. * @since 31.0.0
  156. */
  157. public function setSignatory(Signatory $signatory): self {
  158. $this->signatory = $signatory;
  159. return $this;
  160. }
  161. /**
  162. * @inheritDoc
  163. *
  164. * @return Signatory
  165. * @throws SignatoryNotFoundException
  166. * @since 31.0.0
  167. */
  168. public function getSignatory(): Signatory {
  169. if ($this->signatory === null) {
  170. throw new SignatoryNotFoundException();
  171. }
  172. return $this->signatory;
  173. }
  174. /**
  175. * @inheritDoc
  176. *
  177. * @return bool
  178. * @since 31.0.0
  179. */
  180. public function hasSignatory(): bool {
  181. return ($this->signatory !== null);
  182. }
  183. public function jsonSerialize(): array {
  184. return [
  185. 'body' => $this->body,
  186. 'digest' => $this->getDigest(),
  187. 'digestAlgorithm' => $this->getDigestAlgorithm()->value,
  188. 'signingElements' => $this->signingElements,
  189. 'signatureData' => $this->signatureData,
  190. 'signature' => $this->signature,
  191. 'signatory' => $this->signatory ?? false,
  192. ];
  193. }
  194. }