Merge pull request #23190 from eileenmcnaughton/pradeep
[civicrm-core.git] / Civi / Crypto / CryptoToken.php
index 2a478fd7ce4ef715962569f5b885ae1f47c91293..afe415d844f85b62315ef5cc5de36c775d5e9aa8 100644 (file)
@@ -55,11 +55,19 @@ class CryptoToken {
    */
   protected $delim;
 
+  /**
+   * @var \Civi\Crypto\CryptoRegistry|null
+   */
+  private $registry;
+
   /**
    * CryptoToken constructor.
+   *
+   * @param CryptoRegistry $registry
    */
-  public function __construct() {
+  public function __construct($registry = NULL) {
     $this->delim = chr(2);
+    $this->registry = $registry;
   }
 
   /**
@@ -69,7 +77,7 @@ class CryptoToken {
    * @return bool
    */
   public function isPlainText($plainText) {
-    return is_string($plainText) && ($plainText === '' || $plainText{0} !== $this->delim);
+    return is_string($plainText) && ($plainText === '' || $plainText[0] !== $this->delim);
   }
 
   /**
@@ -85,7 +93,7 @@ class CryptoToken {
    */
   public function encrypt($plainText, $keyIdOrTag) {
     /** @var CryptoRegistry $registry */
-    $registry = \Civi::service('crypto.registry');
+    $registry = $this->getRegistry();
 
     $key = $registry->findKey($keyIdOrTag);
     if ($key['suite'] === 'plain') {
@@ -128,7 +136,7 @@ class CryptoToken {
     }
 
     /** @var CryptoRegistry $registry */
-    $registry = \Civi::service('crypto.registry');
+    $registry = $this->getRegistry();
 
     $tokenData = $this->parse($token);
 
@@ -143,6 +151,40 @@ class CryptoToken {
     return $plainText;
   }
 
+  /**
+   * Re-encrypt an existing token with a newer version of the key.
+   *
+   * @param string $oldToken
+   * @param string $keyTag
+   *   Ex: 'CRED'
+   *
+   * @return string|null
+   *   A re-encrypted version of $oldToken, or NULL if there should be no change.
+   * @throws \Civi\Crypto\Exception\CryptoException
+   */
+  public function rekey($oldToken, $keyTag) {
+    /** @var \Civi\Crypto\CryptoRegistry $registry */
+    $registry = $this->getRegistry();
+
+    $sourceKeys = $registry->findKeysByTag($keyTag);
+    $targetKey = array_shift($sourceKeys);
+
+    if ($this->isPlainText($oldToken)) {
+      if ($targetKey['suite'] === 'plain') {
+        return NULL;
+      }
+    }
+    else {
+      $tokenData = $this->parse($oldToken);
+      if ($tokenData['k'] === $targetKey['id'] || !isset($sourceKeys[$tokenData['k']])) {
+        return NULL;
+      }
+    }
+
+    $decrypted = $this->decrypt($oldToken);
+    return $this->encrypt($decrypted, $targetKey['id']);
+  }
+
   /**
    * Parse the content of a token (without decrypting it).
    *
@@ -166,4 +208,14 @@ class CryptoToken {
     return $tokenData;
   }
 
+  /**
+   * @return CryptoRegistry
+   */
+  protected function getRegistry(): CryptoRegistry {
+    if ($this->registry === NULL) {
+      $this->registry = \Civi::service('crypto.registry');
+    }
+    return $this->registry;
+  }
+
 }