Repository: Azure/ARO-RP
Pull request #1467 (2021-04-27 19:11:23)

Enable DB to use AEAD_AES_256_CBC_HMAC_SHA_512
### Which issue this PR addresses:

>Related to https://msazure.visualstudio.com/AzureRedHatOpenShift/_workitems/edit/8971702
Builds on https://github.com/Azure/ARO-RP/pull/1226

Continued from https://github.com/Azure/ARO-RP/pull/1228

### What this PR does / why we need it:

>Adds a multi reader which can read based on multiple keys and algorithms but only writes using the latest key and algorithm

### Test plan for issue:

>CI secrets and permissions updated so this should be green.

Added a new unit test.

### Is there any documentation that needs to be updated for this PR?

>No.

nilsanderselde: E2E failure looks like a flake
mjudeikis: Linter is failing
mjudeikis: /azp run ci
azure-pipelines[bot]: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>
mjudeikis: /azp run ci
azure-pipelines[bot]: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>
mjudeikis: /azp run e2e
azure-pipelines[bot]: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>
nilsanderselde: Marking as draft until we've finalized plans for rolling this out
github-actions[bot]: Please rebase pull request.
nilsanderselde: Just need E2E to succeed - corresponding config change should allow that soon.
nilsanderselde: /azp run e2e
azure-pipelines[bot]: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>
github-actions[bot]: Please rebase pull request.
nilsanderselde: @jewzaam @mjudeikis I think this is ready to merge. Can you please take a look next week?
nilsanderselde: @m1kola I've adopted all your suggested changes as is. They make the test much more straightforward (and thorough, in the case of the added return value check).
mjudeikis: @nilsanderselde I think this is ok now. 
I think it could be good to have cosmosdb backups enabled when we roll this out just to be on the safe side. wdyt?
nilsanderselde: @mjudeikis Yeah, I think waiting for the backups to be enabled is a good idea.
github-actions[bot]: Please rebase pull request.
nilsanderselde: Once https://github.com/Azure/ARO-RP/pull/1561 is rolled out to prod, this will be unblocked
github-actions[bot]: Please rebase pull request.
github-actions[bot]: Please rebase pull request.

Patch:
diff --git a/cmd/aro/monitor.go b/cmd/aro/monitor.go
index 0246c77dc..d7ad76b30 100644
--- a/cmd/aro/monitor.go
+++ b/cmd/aro/monitor.go
@@ -79,12 +79,7 @@ func monitor(ctx context.Context, log *logrus.Entry) error {
 
 	serviceKeyvault := keyvault.NewManager(msiKVAuthorizer, serviceKeyvaultURI)
 
-	key, err := serviceKeyvault.GetBase64Secret(ctx, env.EncryptionSecretName)
-	if err != nil {
-		return err
-	}
-
-	aead, err := encryption.NewXChaCha20Poly1305(ctx, key)
+	aead, err := encryption.NewMulti(ctx, serviceKeyvault, env.EncryptionSecretV2Name, env.EncryptionSecretName)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/aro/portal.go b/cmd/aro/portal.go
index 28ead9f5e..e10f44f25 100644
--- a/cmd/aro/portal.go
+++ b/cmd/aro/portal.go
@@ -90,12 +90,7 @@ func portal(ctx context.Context, log *logrus.Entry, audit *logrus.Entry) error {
 
 	serviceKeyvault := keyvault.NewManager(msiKVAuthorizer, serviceKeyvaultURI)
 
-	key, err := serviceKeyvault.GetBase64Secret(ctx, env.EncryptionSecretName)
-	if err != nil {
-		return err
-	}
-
-	aead, err := encryption.NewXChaCha20Poly1305(ctx, key)
+	aead, err := encryption.NewMulti(ctx, serviceKeyvault, env.EncryptionSecretV2Name, env.EncryptionSecretName)
 	if err != nil {
 		return err
 	}
@@ -137,12 +132,12 @@ func portal(ctx context.Context, log *logrus.Entry, audit *logrus.Entry) error {
 		return err
 	}
 
-	sessionKey, err := portalKeyvault.GetBase64Secret(ctx, env.PortalServerSessionKeySecretName)
+	sessionKey, err := portalKeyvault.GetBase64Secret(ctx, env.PortalServerSessionKeySecretName, "")
 	if err != nil {
 		return err
 	}
 
-	b, err := portalKeyvault.GetBase64Secret(ctx, env.PortalServerSSHKeySecretName)
+	b, err := portalKeyvault.GetBase64Secret(ctx, env.PortalServerSSHKeySecretName, "")
 	if err != nil {
 		return err
 	}
diff --git a/cmd/aro/rp.go b/cmd/aro/rp.go
index 38262a45a..5b38002ea 100644
--- a/cmd/aro/rp.go
+++ b/cmd/aro/rp.go
@@ -88,12 +88,7 @@ func rp(ctx context.Context, log, audit *logrus.Entry) error {
 		return err
 	}
 
-	dbKey, err := _env.ServiceKeyvault().GetBase64Secret(ctx, env.EncryptionSecretName)
-	if err != nil {
-		return err
-	}
-
-	aead, err := encryption.NewXChaCha20Poly1305(ctx, dbKey)
+	aead, err := encryption.NewMulti(ctx, _env.ServiceKeyvault(), env.EncryptionSecretV2Name, env.EncryptionSecretName)
 	if err != nil {
 		return err
 	}
@@ -135,12 +130,7 @@ func rp(ctx context.Context, log, audit *logrus.Entry) error {
 
 	go database.EmitMetrics(ctx, log, dbOpenShiftClusters, m)
 
-	feKey, err := _env.ServiceKeyvault().GetBase64Secret(ctx, env.FrontendEncryptionSecretName)
-	if err != nil {
-		return err
-	}
-
-	feAead, err := encryption.NewXChaCha20Poly1305(ctx, feKey)
+	feAead, err := encryption.NewMulti(ctx, _env.ServiceKeyvault(), env.FrontendEncryptionSecretV2Name, env.FrontendEncryptionSecretName)
 	if err != nil {
 		return err
 	}
diff --git a/deploy/rp-development-predeploy.json b/deploy/rp-development-predeploy.json
index e1bc7ba91..ca72319c9 100644
--- a/deploy/rp-development-predeploy.json
+++ b/deploy/rp-development-predeploy.json
@@ -256,7 +256,8 @@
                         "objectId": "[parameters('rpServicePrincipalId')]",
                         "permissions": {
                             "secrets": [
-                                "get"
+                                "get",
+                                "list"
                             ]
                         }
                     },
diff --git a/deploy/rp-production-predeploy.json b/deploy/rp-production-predeploy.json
index 250684c7e..d6fc30425 100644
--- a/deploy/rp-production-predeploy.json
+++ b/deploy/rp-production-predeploy.json
@@ -47,7 +47,8 @@
                 "objectId": "[parameters('rpServicePrincipalId')]",
                 "permissions": {
                     "secrets": [
-                        "get"
+                        "get",
+                        "list"
                     ]
                 }
             }
diff --git a/hack/db/db.go b/hack/db/db.go
index cb090d7c2..6eef94a71 100644
--- a/hack/db/db.go
+++ b/hack/db/db.go
@@ -48,12 +48,7 @@ func run(ctx context.Context, log *logrus.Entry) error {
 
 	serviceKeyvault := keyvault.NewManager(msiKVAuthorizer, serviceKeyvaultURI)
 
-	key, err := serviceKeyvault.GetBase64Secret(ctx, env.EncryptionSecretName)
-	if err != nil {
-		return err
-	}
-
-	aead, err := encryption.NewXChaCha20Poly1305(ctx, key)
+	aead, err := encryption.NewMulti(ctx, serviceKeyvault, env.EncryptionSecretV2Name, env.EncryptionSecretName)
 	if err != nil {
 		return err
 	}
diff --git a/hack/devtools/deploy-shared-env.sh b/hack/devtools/deploy-shared-env.sh
index 5cf292da5..16b01252b 100644
--- a/hack/devtools/deploy-shared-env.sh
+++ b/hack/devtools/deploy-shared-env.sh
@@ -117,6 +117,14 @@ import_certs_secrets() {
         --vault-name "$KEYVAULT_PREFIX-svc" \
         --name encryption-key \
         --value "$(openssl rand -base64 32)" >/dev/null
+    az keyvault secret list \
+        --vault-name "$KEYVAULT_PREFIX-svc" \
+        --query '[].name' \
+        -o tsv | grep -q ^encryption-key-v2$ || \
+    az keyvault secret set \
+        --vault-name "$KEYVAULT_PREFIX-svc" \
+        --name encryption-key-v2 \
+        --value "$(openssl rand -base64 64)"
     az keyvault secret list \
         --vault-name "$KEYVAULT_PREFIX-svc" \
         --query '[].name' \
@@ -125,6 +133,14 @@ import_certs_secrets() {
         --vault-name "$KEYVAULT_PREFIX-svc" \
         --name fe-encryption-key \
         --value "$(openssl rand -base64 32)" >/dev/null
+    az keyvault secret list \
+        --vault-name "$KEYVAULT_PREFIX-svc" \
+        --query '[].name' \
+        -o tsv | grep -q ^fe-encryption-key-v2$ || \
+    az keyvault secret set \
+        --vault-name "$KEYVAULT_PREFIX-svc" \
+        --name fe-encryption-key-v2 \
+        --value "$(openssl rand -base64 64)"
     az keyvault secret list \
         --vault-name "$KEYVAULT_PREFIX-por" \
         --query '[].name' \
diff --git a/pkg/deploy/bindata.go b/pkg/deploy/bindata.go
index 96288be41..c579d4538 100644
--- a/pkg/deploy/bindata.go
+++ b/pkg/deploy/bindata.go
@@ -276,7 +276,7 @@ func rbacDevelopmentJson() (*asset, error) {
 	return a, nil
 }
 
-var _rpDevelopmentPredeployJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x58\x5b\x6f\xdb\x36\x14\x7e\xf7\xaf\x10\xb8\x01\x76\x06\x5f\x64\x2f\x05\x56\xbf\x15\xe8\x30\x04\xbb\x34\x68\x8a\xbe\x18\x41\x40\x53\xc7\x0e\x1b\x8a\x24\x48\xca\xad\x37\xe4\xbf\x0f\x94\x2c\x5f\x24\xea\xd6\x28\x43\xe2\x49\x79\x08\x2c\x9e\x0b\x79\xf8\x9d\xef\xa3\xf8\x4f\xcf\xf3\x3c\x0f\xfd\xa8\xc9\x3d\x84\x18\xcd\x3d\x74\x6f\x8c\xd4\xf3\xc9\x24\x79\x33\x0e\x31\xc7\x6b\x08\x81\x9b\x31\xfe\x3b\x52\x30\x26\x22\xdc\x8d\xe9\xc9\xcc\x9f\xbe\x19\xf9\xd3\x91\x3f\x9d\x04\x20\x99\xd8\x5a\xbb\x4f\x10\x4a\x86\x0d\x8c\xbf\x68\xc1\x7f\x40\xc3\x24\x03\x11\xdc\x00\x37\x9f\x41\x69\x2a\xb8\x4d\x34\x1d\xfb\xf6\x2f\x35\x90\x58\xe1\x10\x0c\x28\x8d\xe6\x5e\x32\xad\xf8\x3d\x0e\x42\xca\x3f\x2c\xbf\x00\x31\x57\xc1\xc9\x50\x3c\x6c\xb6\x12\x6c\x34\x6d\x14\xe5\x6b\xb4\x1f\x7c\x1c\x1e\x42\xac\xe4\x0d\xa8\x0d\x25\x70\xad\x28\x27\x54\x62\xf6\xbd\x91\x1e\x60\xbb\xc1\x11\x33\xd7\x0a\x56\xf4\x5b\x65\x8c\xe1\xe9\x68\x88\xbf\xfd\x01\x7c\x6d\xee\xd1\xdc\x9b\xf9\xce\x04\xea\x69\x53\xed\x1d\xc5\x43\x0a\xb4\x88\x14\x01\x5b\xd0\xc5\xde\x26\x13\x4a\x2a\x21\x41\x19\x0a\x3a\x97\x26\x1e\xd7\x40\x22\x45\xcd\xf6\x63\xc4\x32\x81\x8e\x9f\xbc\x63\xdd\x04\x59\x5b\x23\x88\x60\x76\x6d\x9f\x88\xcc\xd4\x2f\x3f\xb7\x78\x79\xd7\x42\x99\x8f\x98\xaf\xe3\x8a\xfc\x54\xe5\x13\x80\x36\x94\x63\x43\x05\x3f\x71\xbc\xbc\xfc\xb9\x5e\xba\x77\x41\xa0\x40\xeb\x3d\x02\x1a\xa5\x6c\xee\x8c\x09\x01\x6d\x4b\x87\xde\x31\x26\xbe\x56\x99\x4b\x45\x85\xdd\x2e\x34\xf7\xa6\x33\xbf\x6a\x62\x54\x01\x31\xbb\x76\xbc\xe2\x4b\x11\xf1\x00\x15\xba\x3c\x16\x47\x43\x1c\x87\x71\x15\x95\xbc\xa3\xfc\x0e\xab\xd0\x1d\xa6\x20\xc4\xeb\x47\xcf\x6c\x76\x76\xe0\x79\xf3\x9f\x83\x47\xeb\xfb\x3b\xca\x0b\x90\x93\x7b\x7b\xdb\x2b\x89\x7f\x04\xc8\x11\xd7\x39\x22\x4e\xf9\xf3\x4f\x4a\x94\xd0\x62\x65\xc6\x7f\x81\xf9\x2a\xd4\xc3\x84\x27\xff\x6f\x76\xac\xf7\x9b\x12\x91\xd4\x59\x77\x26\x08\x4e\x57\xbe\x48\x59\x36\x36\x1d\x5c\x8c\xd3\xc1\xdb\xac\x17\x96\xf4\x48\xfd\x66\xfe\xcc\x1f\xf9\xbf\x8c\xfc\xa9\x53\x69\x4a\x59\xba\x64\xb1\x12\xce\x70\xbd\x79\x55\xc2\x49\x2b\xdc\x48\x4c\xa0\x90\x18\x52\xab\xa4\x61\x4a\xd4\x2b\x36\x9e\xfa\xc9\x79\x64\x32\xbb\x74\x43\xf0\x36\xf7\xd6\x01\x6a\xa4\xa3\x25\x07\xf3\xec\x4a\x89\xb3\x5c\x70\x3c\xfd\x8a\xce\x75\xed\x79\x65\xc6\xd8\x93\x06\x27\x10\xb8\x0a\x06\xfd\x9a\x90\xea\x0f\xbd\x7e\xd2\x8b\xfd\x8b\x2c\x52\x9c\xa9\x0c\x5e\xdb\x32\xf0\x88\xb1\x52\xe3\x3c\x31\xec\x47\x6a\xe9\xd5\x28\xd9\xb0\xd6\x59\x67\x63\x83\xd6\x6e\xc3\x0d\x55\x26\xc2\x6c\xf7\xf3\xb9\x1b\x30\x63\x17\x80\x04\x1e\xe8\x0f\xdc\x09\xda\x16\xb6\xbb\xe7\xae\xe0\xcb\x6b\xff\x4b\xdb\x3f\xb3\x57\xdb\xfe\xbb\xe9\xbf\xdc\xf6\x4f\xd4\xa9\x6d\x06\xa8\x3e\xd5\x6c\xb0\x81\x5f\x79\x20\x05\xe5\x66\x37\xcf\x6b\xc1\x28\x49\x6a\x8d\xde\x53\x8d\x97\x0c\x9e\x7c\xf6\xb5\xeb\x7b\x26\x3a\x91\x10\x33\xca\xc8\xcf\xb7\xef\xff\x8e\x55\x0e\x28\x6a\x4c\x2c\x69\x45\x17\x44\x70\x82\xcd\xe0\x70\xe9\x30\xe8\x9f\x7e\xdb\xf7\x2f\x86\x5e\x7f\x44\x98\xce\xa3\xd5\x51\xf1\xdf\x61\xfb\xd9\xfa\x4e\xe2\x08\x2d\x55\xba\x8a\x05\x0d\x70\xcc\x93\x4b\x11\xb4\xd0\xd1\x52\x13\x45\xa5\x8d\x34\xb8\x18\xa7\x63\xae\x4e\x43\xfa\x21\x2a\x66\xcd\x15\x0e\x29\xdb\xc6\xdf\x0c\x05\x5d\x7a\x38\xb0\x1b\xcc\x03\xac\x1c\x7d\xe3\xa2\xc6\xe4\x63\xe4\xa8\xef\x1a\x33\xe4\xf7\x2e\x78\x1f\x40\x1c\xae\x91\xd0\xe2\x78\xef\x5d\x37\x44\xe5\x34\x85\x24\xa8\x90\x6a\xdb\x07\x35\xf8\x5a\x03\x51\x65\xb2\x70\x62\xbc\x2e\xe2\x8f\xf4\xb9\xad\x60\x3c\x62\x51\xb3\xa2\x04\x9b\x0a\xd5\x3b\x78\x28\xc0\x06\xea\xb0\x72\x00\x0c\xea\x59\xae\x73\x07\x20\xa7\x59\x24\x03\x9b\xba\x7c\xc1\xc5\xc4\xdc\xd2\x3d\xc3\xf3\x41\xeb\xe4\xfe\xb2\x4d\x4c\x35\xdf\xe5\x9a\x3b\xc2\xa8\xae\x02\x60\xd3\xfd\xc8\x0b\x9f\x83\x20\x80\x5b\x15\xbe\x11\x2b\xf3\x3e\x01\xd9\xdc\x33\x2a\x82\x52\x89\xcc\xaa\xd1\xf4\xed\xc8\x7f\x5b\xf3\x23\xb3\xa9\x18\x04\x4b\xd3\x89\x41\xce\xe4\xfc\xc4\xc0\x75\x07\xff\x82\xc5\xa0\xe3\xc6\xbc\x71\xa3\x12\xeb\xd6\x68\xb1\x75\x5d\x6e\x5d\x6d\x69\x28\x85\xea\x54\xe0\xc8\xb7\xa9\x0a\x48\xa1\x3a\x15\xc8\x99\x74\x2a\xd0\xa9\x40\xa7\x02\xf1\xd3\xa9\xc0\xee\x39\x67\x15\xd0\x1b\xd2\xa9\x40\xce\xa4\x53\x81\x4e\x05\x5e\xb7\x0a\xd4\x24\xd0\x4e\x2c\x5c\x96\x67\x26\x16\xbd\x64\xc6\x8f\xbd\x7f\x03\x00\x00\xff\xff\x03\xe9\xdc\x71\x25\x29\x00\x00")
+var _rpDevelopmentPredeployJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x58\xdb\x6f\xdb\xb6\x17\x7e\xf7\x5f\x21\xf0\xf7\x03\xec\x0c\xbe\xc8\x5e\x0a\xac\x7e\x2b\xd0\x61\x08\x76\x69\xd0\x14\x7d\x31\x82\x80\xa6\x8e\x1d\x36\x14\x49\x90\x94\x5b\x6f\xc8\xff\x3e\x50\xb2\x7c\x91\xa8\x5b\x2b\x6f\xa9\x27\xe5\x21\xb0\x78\x2e\xe4\xe1\x77\xbe\x8f\xe2\x5f\x3d\xcf\xf3\x3c\xf4\x7f\x4d\x1e\x21\xc4\x68\xee\xa1\x47\x63\xa4\x9e\x4f\x26\xc9\x9b\x71\x88\x39\x5e\x43\x08\xdc\x8c\xf1\x9f\x91\x82\x31\x11\xe1\x6e\x4c\x4f\x66\xfe\xf4\xd5\xc8\x9f\x8e\xfc\xe9\x24\x00\xc9\xc4\xd6\xda\x7d\x80\x50\x32\x6c\x60\xfc\x49\x0b\xfe\x3f\x34\x4c\x32\x10\xc1\x0d\x70\xf3\x11\x94\xa6\x82\xdb\x44\xd3\xb1\x6f\xff\x52\x03\x89\x15\x0e\xc1\x80\xd2\x68\xee\x25\xd3\x8a\xdf\xe3\x20\xa4\xfc\xdd\xf2\x13\x10\x73\x13\x9c\x0c\xc5\xc3\x66\x2b\xc1\x46\xd3\x46\x51\xbe\x46\xfb\xc1\xe7\xe1\x21\xc4\x4a\xde\x81\xda\x50\x02\xb7\x8a\x72\x42\x25\x66\x5f\x1b\xe9\x09\xb6\x1b\x1c\x31\x73\xab\x60\x45\xbf\x54\xc6\x18\x9e\x8e\x86\xf8\xcb\x6f\xc0\xd7\xe6\x11\xcd\xbd\x99\xef\x4c\xa0\xbe\x6d\xaa\xbd\xa3\x78\x48\x81\x16\x91\x22\x60\x0b\xba\xd8\xdb\x64\x42\x49\x25\x24\x28\x43\x41\xe7\xd2\xc4\xe3\x1a\x48\xa4\xa8\xd9\xbe\x8f\x58\x26\xd0\xf1\x93\x77\xac\x9b\x20\x6b\x6b\x04\x11\xcc\xae\xed\x03\x91\x99\xfa\xe5\xe7\x16\x2f\xef\x56\x28\xf3\x1e\xf3\x75\x5c\x91\x1f\xaa\x7c\x02\xd0\x86\x72\x6c\xa8\xe0\x27\x8e\xd7\xd7\x3f\xd6\x4b\xf7\x26\x08\x14\x68\xbd\x47\x40\xa3\x94\xcd\x9d\x31\x21\xa0\x6d\xe9\xd0\x1b\xc6\xc4\xe7\x2a\x73\xa9\xa8\xb0\xdb\x85\xe6\xde\x74\xe6\x57\x4d\x8c\x2a\x20\x66\xd7\x8e\x37\x7c\x29\x22\x1e\xa0\x42\x97\xe7\xe2\x68\x88\xe3\x30\xae\xa2\x92\x0f\x94\x3f\x60\x15\xba\xc3\x14\x84\xf8\xfe\xd1\x33\x9b\x5d\x1c\x78\x5e\xfd\xe3\xe0\xd1\xfa\xf1\x81\xf2\x02\xe4\xe4\xde\xde\xf7\x4a\xe2\x1f\x01\x72\xc4\x75\x8e\x88\x53\xfe\xfc\x9d\x12\x25\xb4\x58\x99\xf1\x1f\x60\x3e\x0b\xf5\x34\xe1\xc9\xff\xbb\x1d\xeb\xfd\xa2\x44\x24\x75\xd6\x9d\x09\x82\xd3\x95\x2f\x52\x96\x8d\x4d\x07\x57\xe3\x74\xf0\x3e\xeb\x85\x25\x3d\x52\xbf\x99\x3f\xf3\x47\xfe\x4f\x23\x7f\xea\x54\x9a\x52\x96\x2e\x59\xac\x84\x0b\x5c\x6f\x5e\x95\x70\xd2\x0a\x77\x12\x13\x28\x24\x86\xd4\x2a\x69\x98\x12\xf5\x8a\x8d\xa7\x7e\x72\x1e\x99\xcc\xae\xdd\x10\xbc\xcf\xbd\x75\x80\x1a\xe9\x68\xc9\xc1\x9c\x5d\x29\x71\x96\x0b\x8e\xa7\x5f\xd1\xb9\xae\x3d\xaf\xcc\x18\x7b\xd2\xe0\x04\x02\x37\xc1\xa0\x5f\x13\x52\xfd\xa1\xd7\x4f\x7a\xb1\x7f\x95\x45\x8a\x33\x95\xc1\x6b\x5b\x06\x1e\x31\x56\x6a\x9c\x27\x86\xfd\x48\x2d\xbd\x1a\x25\x1b\xd6\x3a\xeb\x6c\x6c\xd0\xda\x6d\xb8\xa1\xca\x44\x98\xed\x7e\x9e\xbb\x01\x33\x76\x01\x48\xe0\x81\x7e\xc7\x9d\xa0\x6d\x61\xbb\x7b\xee\x0a\xbe\xbc\xf6\xbf\xb6\xfd\x33\xfb\x6e\xdb\x7f\x37\xfd\x97\xdb\xfe\x89\x3a\xb5\xcd\x00\xd5\xa7\x9a\x0d\x36\xf0\x33\x0f\xa4\xa0\xdc\xec\xe6\x79\x2b\x18\x25\x49\xad\xd1\x5b\xaa\xf1\x92\xc1\x37\x9f\x7d\xed\xfa\xce\x44\x27\x12\x62\x46\x19\xf9\xf9\xf6\xfd\xcf\xb1\xca\x01\x45\x8d\x89\x25\xad\xe8\x82\x08\x4e\xb0\x19\x1c\x2e\x1d\x06\xfd\xd3\x6f\xfb\xfe\xd5\xd0\xeb\x8f\x08\xd3\x79\xb4\x3a\x2a\xfe\x2b\x6c\x3f\x5a\xdf\x49\x1c\xa1\xa5\x4a\x57\xb1\xa0\x01\x8e\x79\x72\x29\x82\x16\x3a\x5a\x6a\xa2\xa8\xb4\x91\x06\x57\xe3\x74\xcc\xd5\x69\x48\x3f\x45\xc5\xac\xb9\xc2\x21\x65\xdb\xf8\x9b\xa1\xa0\x4b\x0f\x07\x76\x83\x79\x80\x95\xa3\x6f\x5c\xd4\x98\x7c\x8c\x1c\xf5\x5d\x63\x86\xfc\xda\x05\xef\x03\x88\xc3\x35\x12\x5a\x1c\xef\xbd\xeb\x86\xa8\x9c\xa6\x90\x04\x15\x52\x6d\xfb\xa0\x06\x5f\x6b\x20\xaa\x4c\x16\x4e\x8c\xd7\x45\xfc\x91\x3e\xf7\x15\x8c\x47\x2c\x6a\x56\x94\x60\x53\xa1\x7a\x07\x0f\x05\xd8\x40\x1d\x56\x0e\x80\x41\x3d\xcb\x75\xee\x00\xe4\x34\x8b\x64\x60\x53\x97\x2f\xb8\x98\x98\x5b\xba\x67\x38\x1f\xb4\x4e\xee\x2f\xdb\xc4\x54\xf3\x5d\xae\xb9\x23\x8c\xea\x2a\x00\x36\xdd\x8f\xbc\xf0\x39\x08\x02\xb8\x55\xe1\x3b\xb1\x32\x6f\x13\x90\xcd\x3d\xa3\x22\x28\x95\xc8\xac\x1a\x4d\x5f\x8f\xfc\xd7\x35\x3f\x32\x9b\x8a\x41\xb0\x34\x9d\x18\xe4\x4c\x2e\x4f\x0c\x5c\x77\xf0\x2f\x58\x0c\x3a\x6e\xcc\x1b\x37\x2a\xb1\x6e\x8d\x16\x5b\xd7\xe5\xd6\xd5\x96\x86\x52\xa8\x4e\x05\x8e\x7c\x9b\xaa\x80\x14\xaa\x53\x81\x9c\x49\xa7\x02\x9d\x0a\x74\x2a\x10\x3f\x9d\x0a\xec\x9e\x4b\x56\x01\xbd\x21\x9d\x0a\xe4\x4c\x3a\x15\x38\xbb\x0a\xfc\x3b\x3d\xdb\xa9\x45\xc6\xb8\xe6\x56\x74\xa2\xe2\xb2\xbc\x30\x51\xe9\x25\x33\x7e\xee\xfd\x1d\x00\x00\xff\xff\xe9\x07\x8c\x5b\x4d\x29\x00\x00")
 
 func rpDevelopmentPredeployJsonBytes() ([]byte, error) {
 	return bindataRead(
@@ -436,7 +436,7 @@ func rpProductionPredeployParametersJson() (*asset, error) {
 	return a, nil
 }
 
-var _rpProductionPredeployJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x5a\x5f\x4f\x1b\x39\x10\x7f\xe7\x53\xac\xf6\x4e\x4a\xa8\x9a\x64\x93\x52\x89\xe6\xad\x2d\xa7\x0a\xf5\xca\xa1\x06\xf1\x82\x10\x72\xbc\x93\xe0\xe2\xb5\x2d\xdb\x1b\xc8\x55\x7c\xf7\x93\x77\x37\x61\xff\x78\xbd\x81\x26\x5c\x28\x59\x1e\x48\xb2\xe3\x19\xcf\xcf\x3f\xcf\x78\x6c\xff\xdc\xf3\x3c\xcf\xf3\xff\x54\xf8\x1a\x22\xe4\x0f\x3d\xff\x5a\x6b\xa1\x86\xbd\x5e\xfa\x4b\x37\x42\x0c\x4d\x21\x02\xa6\xbb\xe8\xdf\x58\x42\x17\xf3\x28\x7b\xa7\x7a\x83\xa0\xff\xbe\x13\xf4\x3b\x41\xbf\x17\x82\xa0\x7c\x6e\xe4\xce\x20\x12\x14\x69\xe8\xfe\x50\x9c\xfd\xe1\xbf\x4d\x2d\x60\xce\x34\x30\x7d\x0e\x52\x11\xce\x8c\xa1\x7e\x37\x30\x7f\x0b\x81\x19\x92\x04\x8d\x29\x28\x7f\xe8\xa5\xbd\x4a\xdb\xd1\x58\x69\x90\x5f\x61\x3e\x43\x31\xd5\x1f\x31\x06\xa5\x4e\x39\x25\x98\x24\xa2\x17\x4b\x51\xf3\xfc\x2c\x7c\x4b\x14\x68\x60\x88\xe9\xe3\xd0\x98\xbc\x50\xf1\x58\x61\x49\x84\x26\x9c\xb5\xf7\xbb\x8b\x77\x97\x59\x27\x0a\x0d\xf9\xf8\x07\xe0\x45\x43\x81\x24\x8a\x40\x83\x54\xed\xd6\x44\x8c\x40\xce\x08\x86\x53\x49\x18\x26\x02\xd1\xe3\xb0\xb5\x6f\xd5\x21\x40\x46\x44\x19\x8f\x8b\x6e\x15\x84\x14\x60\x09\xba\xea\x4c\x41\x68\x0a\xda\xb7\xbe\xbd\xac\x9a\x4d\x81\x03\xa9\xc9\x84\x60\xa4\x2d\x38\x15\x25\x25\x20\x0d\x96\xee\x2f\x25\x42\xa0\xe0\x96\x30\xdd\x73\xbc\x8e\x45\x68\x4c\xd8\x1d\xa8\xfc\x7a\xbf\x67\xff\x96\x73\xd5\x0f\xc7\x67\xfc\x06\xd8\x36\xf1\x42\x6e\x17\x2f\x9e\x04\xab\xe0\x52\x23\xba\x43\x75\xbd\xa8\xaa\xd4\x83\x1d\xac\x6b\x80\x35\xf9\x74\x9f\x25\x8d\x07\x8f\x8a\x59\x23\xcd\x46\x27\xa3\x2f\xd5\x1e\xfb\x7a\x2e\xc0\xc0\x31\xe6\x9c\x96\xdc\xf5\x43\x98\x98\x01\x3a\x47\x34\x36\x32\x13\x44\x15\x2c\x25\xee\x73\x23\x0a\x77\x5a\xa2\xcf\x0d\xb9\xa9\xc6\x30\x92\x12\xcd\x1b\x2c\x5f\x5c\xd6\x9b\x3d\xfa\xe4\x0e\x7d\x1b\x32\x7b\xea\x0e\x0d\x1b\xb2\x3a\x6a\x98\x3a\xeb\x37\x6b\x4b\xef\xf5\x86\x94\x96\x84\x4d\x7d\xab\xa6\x29\xd2\x70\x8b\xe6\xdf\x41\xf1\x58\x62\xf8\x22\x79\x2c\x4e\x50\x04\x4f\xd3\x76\x93\x41\x70\x2a\x61\x42\xee\x1a\x75\x94\x7c\x8f\xd0\xdd\xdf\xc0\xa6\xfa\xda\x1f\x7a\x83\xc0\x6a\x40\x8a\x13\x35\x1d\x25\x5d\xfd\x18\x86\xd2\xe0\x9c\x98\xda\x10\xce\xb6\x08\xf4\x08\x64\x0a\x71\x40\x66\x10\x17\xe3\x4f\x49\x95\x90\x5c\x98\x45\x91\xc5\x1f\x2f\x0b\x61\xb1\x24\x7a\xfe\x3d\xa6\x8e\x25\x93\x3d\xfe\xad\x62\xa0\x2c\xab\x39\xe6\xd4\xf8\x76\x86\x85\x63\xf1\x94\xf6\x2d\x71\xcf\x4c\xc1\xef\x88\x4d\x13\x44\xde\x34\xb5\x09\x41\x69\xc2\x90\x49\x13\x85\x86\x07\x07\xef\x56\x33\x57\x60\x81\x69\xf9\xd1\x54\x00\x0b\x3a\x7f\x4b\x2a\x03\xf9\x88\x5e\x54\xf4\x35\xba\x80\x92\xe9\x9e\x98\xa6\x94\xdf\x36\x89\x0b\x49\xb8\x19\x41\x7f\xe8\xf5\x07\x41\x53\xc7\x88\x04\xac\xb3\x62\xe4\x98\x8d\x79\xcc\x42\x7b\x76\xf2\x8a\xcc\xad\x68\x62\xe9\x9c\xf6\xa5\xb8\x22\xec\x0a\xc9\xc8\xae\xa6\x46\xc5\xef\x40\xa8\xf7\x4f\x24\x54\xdf\x94\x80\x87\xdd\xa0\x37\x38\xd8\x56\x1e\x1d\x3c\x3f\x8f\xc2\xb1\x36\x09\xde\x90\x29\x4b\x21\x57\x06\xe2\xd7\x45\xaa\xc3\xdf\x9b\x55\xfd\x6d\x60\xd5\xe1\xeb\xa3\x55\x63\xc3\xad\xe1\xd4\x11\xb0\xf2\xca\xaa\x22\x5d\xa0\xd4\xfb\xe7\xa7\x14\xb0\x79\x8e\x4f\xaf\x8c\x4a\x4f\x5b\x47\x25\x4e\x95\x6b\xf1\xba\x85\xb7\xbd\x22\xaf\xeb\xdd\xb3\x46\xb0\x77\xff\xd7\xfa\x6a\x0a\x0c\x66\xa8\x86\x6b\x95\x5f\x8b\x3b\x0b\x25\x2b\x39\xcd\x1d\xa6\x2a\x35\xd3\xa2\xf6\xf8\x46\xb0\xe4\x8a\x4f\x74\xf7\x04\xf4\x2d\x97\x37\x3d\x96\xfe\x1f\x65\x15\x43\x52\xd9\xa9\x72\x73\xca\x31\x5a\xf8\x7f\x21\xf3\x45\x60\x7b\xbf\xbb\x78\x59\x1e\x5f\x1f\x73\x16\x92\x65\xb3\x3c\x4d\x1e\x36\x35\xaa\xb4\xf0\x91\x20\xb9\xbd\xf5\x41\x30\x08\x3a\xc1\x61\x27\xe8\x5b\x6b\x49\x67\x61\xe4\xc0\x48\xc0\x0e\xa6\xfa\xfa\x11\xa5\xd3\x6f\x24\x10\xae\x96\xf8\x65\xa9\x5c\x30\x70\x6c\x98\x25\x59\x27\x48\xb3\x8e\x55\xca\xb2\x71\x66\xd9\xc1\x53\xf1\x98\xb9\x36\xe7\xd6\x14\x8b\x91\x35\x69\x06\x2b\x25\x4d\x1b\x55\x1a\x2d\x26\x2d\x49\x58\x60\xce\x71\xd8\x6e\xad\xc8\xc4\xd6\x5b\xaf\x95\xce\xfc\xe6\x38\x9b\x98\xd2\x68\x6a\x60\x60\x31\xa5\x4e\x61\x47\x30\xf3\x72\xfb\xc1\x7f\xb1\x50\x70\xc2\x1a\x76\x4d\x17\x4f\x33\x14\x79\xdd\xc5\x09\xf9\x15\xe6\xe7\x28\xa6\xae\x33\x9a\x82\x92\xc5\xb4\x5b\xad\x67\xcb\x66\x6f\xea\xe3\x7b\xfe\xa9\x92\xb6\xfc\x34\x00\xe8\xfd\x22\x1c\xc9\x3e\xc6\x67\xae\x22\xae\x8e\x3e\xbd\x18\x4c\x9c\x12\xf5\xed\x57\xcb\xac\x9d\x34\x46\xac\x3d\xad\xce\x58\xe5\x64\xd0\x91\x30\x66\x44\xea\x18\xd1\xec\xeb\x9a\x52\x45\x6d\xcc\xaf\x6c\x5b\x0a\x60\xa1\xfa\x87\x59\x07\x78\x0d\x11\x66\xcf\x8e\xe0\xf6\x65\x9c\x03\x13\xb2\x07\x2f\x36\xe3\x64\xdd\xdf\xde\x8c\x93\xae\xa3\x9e\x39\xe9\x08\x49\x66\x48\x2f\x93\x4e\xd6\xcf\xdc\x51\x8a\x7f\x44\x14\x1a\x53\xf8\xe5\x55\xba\xf1\x6f\x43\xe1\x44\x40\x12\x51\x3a\x41\x75\xfa\xbe\xba\xa8\xf2\xc0\xa2\x47\x07\x96\x05\xa2\x17\x98\x33\x8c\x74\x3b\xbf\x7e\x2e\x9e\x33\xb5\xf6\xdf\x7a\xad\x0e\xa6\xb6\xc5\x74\x15\xf1\xc5\x3a\xa3\x97\x68\x58\x13\xd2\x4d\x51\xf0\xc9\x87\xe3\xea\x26\xae\x8f\x9a\x13\x14\x11\x3a\x4f\x4a\xe3\x9a\x59\xba\x44\x51\x69\xc4\x42\x24\x2d\xf3\xc6\x16\x1a\x51\xf9\x08\x73\x39\x0a\xcb\x2b\x50\xed\x96\xf3\xda\x93\x19\x93\xfc\x88\x35\x1f\x46\xb7\xf6\xed\x47\xfb\xc0\x8c\xbd\x11\x9f\xe8\xa3\xf4\x8a\xcf\xd0\xd3\x32\x06\xe7\x6c\x2c\x13\xbf\xff\xa1\x13\x7c\x58\xb1\x84\x7a\x2c\xef\xc2\xb1\xde\xf1\xae\x22\xb2\x41\xde\x39\xaf\x55\x59\x79\xe7\xbc\x8d\xf0\x52\x79\x27\xb8\xdc\xf1\xae\x22\xb2\x41\xde\xb9\xee\x9d\x59\x69\xe7\xba\x8d\xf2\x52\x59\xa7\x66\x78\xc7\xba\x8a\xc8\x06\x59\xe7\xbc\x97\x67\xa5\x9d\xf3\x3a\xd2\x33\xf3\x2e\xf9\x74\xb9\x77\xbf\xf7\x5f\x00\x00\x00\xff\xff\x5d\x80\x79\xc0\xc5\x2d\x00\x00")
+var _rpProductionPredeployJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x5a\x5f\x4f\x1b\x39\x10\x7f\xe7\x53\xac\xf6\x4e\x4a\xa8\x9a\x64\x93\x52\x89\xe6\xad\x2d\xa7\x0a\xf5\xca\xa1\xa6\xe2\x05\x21\xe4\x78\x27\xc1\xc5\x6b\x5b\xb6\x37\x90\xab\xf8\xee\x27\xef\x6e\xc2\xfe\xf1\x7a\x03\x24\x5c\x28\x59\x1e\x48\xb2\xe3\x19\xcf\xcf\x3f\xcf\x78\x6c\xff\xda\xf3\x3c\xcf\xf3\xff\x54\xf8\x0a\x22\xe4\x0f\x3d\xff\x4a\x6b\xa1\x86\xbd\x5e\xfa\x4b\x37\x42\x0c\x4d\x21\x02\xa6\xbb\xe8\xdf\x58\x42\x17\xf3\x28\x7b\xa7\x7a\x83\xa0\xff\xbe\x13\xf4\x3b\x41\xbf\x17\x82\xa0\x7c\x6e\xe4\x7e\x40\x24\x28\xd2\xd0\xfd\xa9\x38\xfb\xc3\x7f\x9b\x5a\xc0\x9c\x69\x60\xfa\x0c\xa4\x22\x9c\x19\x43\xfd\x6e\x60\xfe\x16\x02\x33\x24\x09\x1a\x53\x50\xfe\xd0\x4b\x7b\x95\xb6\xa3\xb1\xd2\x20\xbf\xc2\x7c\x86\x62\xaa\x3f\x62\x0c\x4a\x9d\x72\x4a\x30\x49\x44\xcf\x97\xa2\xe6\xf9\x55\xf8\x96\x28\xd0\xc0\x10\xd3\xc7\xa1\x31\x79\xae\xe2\xb1\xc2\x92\x08\x4d\x38\x6b\xef\x77\x17\xef\x2e\xb2\x4e\x14\x1a\xf2\xf1\x4f\xc0\x8b\x86\x02\x49\x14\x81\x06\xa9\xda\xad\x89\x18\x81\x9c\x11\x0c\xa7\x92\x30\x4c\x04\xa2\xc7\x61\x6b\xdf\xaa\x43\x80\x8c\x88\x32\x1e\x17\xdd\x2a\x08\x29\xc0\x12\x74\xd5\x99\x82\xd0\x14\xb4\x6f\x7d\x7b\x51\x35\x9b\x02\x07\x52\x93\x09\xc1\x48\x5b\x70\x2a\x4a\x4a\x40\x1a\x2c\xdd\x5f\x4a\x84\x40\xc1\x2d\x61\xba\xe7\x78\x1d\x8b\xd0\x98\xb0\x3b\x50\xf9\xf5\x6e\xcf\xfe\x2d\xe7\xaa\x1f\x8e\x7f\xf0\x6b\x60\xdb\xc4\x0b\xb9\x5d\xbc\x78\x14\xac\x82\x4b\x8d\xe8\x0e\xd5\xf5\xa2\xaa\x52\x0f\x76\xb0\xba\xa3\x04\x25\xea\xe9\xb0\x27\x9f\xee\xb2\xa4\x72\xef\x71\x31\xab\xa4\xd9\xea\x64\xf4\xa5\xea\x91\xaf\xe7\x02\x0c\x5c\x63\xce\x69\xa9\xb7\x7e\x08\x13\x33\x80\x67\x88\xc6\x46\x66\x82\xa8\x82\xa5\xc4\x5d\x6e\xc4\xe1\x56\x4b\xf4\xb9\x21\x77\xd5\x18\x46\x52\xa2\x79\x83\xe5\xf3\x8b\x7a\xb3\x47\x9f\xdc\xa1\x71\x43\x66\x4f\xdd\xa1\x63\x43\x56\x47\x0d\x53\x6b\xfd\x66\x6d\xe9\xbf\xde\x90\xd2\x92\xb0\xa9\x6f\xd5\x34\x45\x1a\x6e\xd0\xfc\x3b\x28\x1e\x4b\x0c\x5f\x24\x8f\xc5\x09\x8a\xe0\x71\xda\xae\x33\x08\x4e\x25\x4c\xc8\x6d\xa3\x8e\x92\xef\x11\xba\xfd\x1b\xd8\x54\x5f\xf9\x43\x6f\x10\x58\x0d\x48\x71\xa2\xa6\xa3\xa4\xab\x1f\xc3\x50\x1a\x9c\x13\x53\x1b\xc2\xd9\x16\xa1\x1e\x80\x4c\x21\x0e\xc8\x0c\xe2\x62\x7c\x2a\xa9\x12\x92\x0b\xb3\x68\xb2\xf8\xe3\x65\x21\x2e\x96\x44\xcf\xbf\xc7\xd4\xb1\xa4\xb2\xc7\xc7\x55\x0c\x94\x65\x35\xc7\x9c\x1a\xdf\x7e\x60\xe1\x08\x9b\x69\xdf\x12\xf7\xcc\x14\xfc\x8e\xd8\x34\x41\xe4\x4d\x53\x9b\x10\x94\x26\x0c\x99\x34\x52\x68\x78\x70\xf0\x6e\x35\x73\x05\x16\x98\x96\x1f\x4d\x85\xb0\xa0\xf3\xb7\xa4\x72\x90\x0f\xe8\x45\x45\x5f\xa3\x0b\x28\x99\xee\x89\x69\x4a\xf9\x4d\x93\xb8\x90\x84\x9b\x11\xf4\x87\x5e\x7f\x10\x34\x75\x8c\x48\xc0\x3a\x2b\x56\x8e\xd9\x98\xc7\x2c\xb4\x67\x27\xaf\xc8\xdc\x8a\x26\x96\xce\x69\x5f\x8a\x4b\xc2\x2e\x91\x8c\xec\x6a\x6a\x54\xfc\x0e\x84\x7a\xff\x48\x42\xf5\x4d\x89\x78\xd8\x0d\x7a\x83\x83\x6d\xe5\xd1\xc1\xf3\xf3\x28\x1c\x6b\x93\xe0\x0d\x99\xb2\x14\x72\x69\x20\x7e\x5d\xa4\x3a\xfc\xbd\x59\xd5\xdf\x06\x56\x1d\xbe\x3e\x5a\x35\x36\xdc\x1a\x4e\x1d\x01\x2b\xaf\xac\x2a\xd2\x05\x4a\xbd\x7f\x7e\x4a\x01\x9b\xe7\xf8\xf4\xca\xa8\xf4\xb8\x75\x54\xe2\x54\xb9\x56\xaf\x5b\x78\xdb\x2b\xf6\xba\xde\x3d\x6b\x04\x7b\xf7\x7f\xad\xaf\xa6\xc0\x60\x86\x6a\xb8\x56\xf9\xb5\xb8\xb3\x50\xb2\x92\xd3\xdc\x61\xaa\x52\x33\x2d\x6a\x8f\x6f\x04\x4b\xae\xf8\x44\x77\x4f\x40\xdf\x70\x79\xdd\x63\xe9\xff\x51\x56\x31\x24\x95\x9d\x2a\x37\xa7\x1c\xa3\x85\xff\xe7\x32\x5f\x04\xb6\xf7\xbb\x8b\x97\xe5\xf1\xf5\x31\x67\x21\x59\x36\xcb\xd3\xe4\x7e\x53\xa3\x4a\x0b\x1f\x09\x92\xdb\x7b\x1f\x04\x83\xa0\x13\x1c\x76\x82\xbe\xb5\x96\x74\x16\x46\x0e\x8c\x04\xec\x60\xaa\xaf\x1f\x51\x3a\xfd\x46\x02\xe1\x6a\x89\x5f\x96\xca\x05\x03\xc7\x86\x5a\x92\x75\x82\x34\xeb\x58\xa5\x2c\x1b\x67\x96\x1d\x3e\x15\x8f\x99\x6b\xf3\x6e\x4d\xb1\x18\x59\x93\x66\xb0\x52\xd2\xb4\x51\xa5\xd1\x62\xd2\x92\x84\x05\xe6\x1c\x87\xed\xd6\x8a\x4c\x6c\xbd\xf5\x5a\xe9\xcc\x6f\x8e\xb3\x89\x29\x8d\xa6\x06\x06\x16\x53\xea\x14\x76\x04\x33\x2f\xb7\x5f\xfc\x17\x0b\x05\x27\xac\x61\x57\x75\xf1\x34\x43\x91\xd7\x5d\x9c\x90\x5f\x61\x7e\x86\x62\xea\xda\x9d\x2d\x28\x59\x4c\xbb\xd5\x7a\xb6\x6c\xf6\xa6\x3e\xbe\xe7\x9f\x2a\x69\xcb\x4f\x03\x80\xde\x13\xe1\x48\xf6\x31\x3e\x73\x15\x71\x75\xf4\xe9\xc5\x60\xe2\x94\xa8\x6f\xbf\x5a\x66\xed\xa4\x31\x62\xed\x69\x75\xc6\x2a\x67\x02\x8e\x84\x31\x23\x52\xc7\x88\x66\x5f\xd7\x94\x2a\x6a\x63\x7e\x65\xdb\x52\x00\x0b\xd5\x3f\xcc\x3a\xc0\x6b\x88\x30\x7b\x76\x04\xb7\x2f\xe3\x1c\x98\x90\x3d\x78\xb1\x19\x27\xeb\xfe\xf6\x66\x9c\x74\x1d\xf5\xcc\x49\x47\x48\x32\x43\x7a\x99\x74\xb2\x7e\xe6\x8e\x52\xfc\x23\xa2\xd0\x98\xc2\x93\x57\xe9\xc6\xbf\x0d\x85\x13\x01\x49\x44\xe9\x04\xd5\xe9\xfb\xea\xa2\xca\x3d\x8b\x1e\x1c\x58\x16\x88\x9e\x63\xce\x30\xd2\xed\xfc\xfa\xb9\x78\xce\xd4\xda\x7f\xeb\xb5\x3a\x98\xda\x16\xd3\x55\xc4\x17\xeb\x8c\x5e\xa2\x61\x4d\x48\x37\x45\xc1\x47\x1f\x9e\xab\xeb\xb8\x3e\x6a\x4e\x50\x44\xe8\x3c\x29\x8d\x6b\x66\xe9\x12\x45\xa5\x11\x0b\x91\xb4\xcc\x1b\x5b\x68\x44\xe5\x23\xcc\xe5\x28\x2c\xaf\x48\xb5\x5b\xce\x6b\x51\x66\x4c\xf2\x23\xd6\x7c\x18\xdd\xda\xb7\x1f\xfd\x03\x33\xf6\x46\x7c\xa2\x8f\xd2\x2b\x40\x43\x4f\xcb\x18\x9c\xb3\xb1\x4c\xfc\xfe\x87\x4e\xf0\x61\xc5\x12\xea\xa1\xbc\x0b\xc7\x7a\xc7\xbb\x8a\xc8\x06\x79\xe7\xbc\x76\x65\xe5\x9d\xf3\x36\xc2\x4b\xe5\x9d\xe0\x72\xc7\xbb\x8a\xc8\x06\x79\xe7\xba\x97\x66\xa5\x9d\xeb\x36\xca\x4b\x65\x9d\x9a\xe1\x1d\xeb\x2a\x22\x1b\x64\x9d\xf3\xde\x9e\x95\x76\xce\xeb\x48\xcf\xcc\xbb\xe4\xd3\xc5\xde\xdd\xde\x7f\x01\x00\x00\xff\xff\xa8\x8f\xbc\x95\xe5\x2d\x00\x00")
 
 func rpProductionPredeployJsonBytes() ([]byte, error) {
 	return bindataRead(
diff --git a/pkg/deploy/generator/resources_rp.go b/pkg/deploy/generator/resources_rp.go
index 714b7a19f..1db3e2f84 100644
--- a/pkg/deploy/generator/resources_rp.go
+++ b/pkg/deploy/generator/resources_rp.go
@@ -1209,6 +1209,7 @@ func (g *generator) rpServiceKeyvaultAccessPolicies() []mgmtkeyvault.AccessPolic
 			Permissions: &mgmtkeyvault.Permissions{
 				Secrets: &[]mgmtkeyvault.SecretPermissions{
 					mgmtkeyvault.SecretPermissionsGet,
+					mgmtkeyvault.SecretPermissionsList,
 				},
 			},
 		},
diff --git a/pkg/deploy/predeploy.go b/pkg/deploy/predeploy.go
index 1bec9f3bc..2aed16ed7 100644
--- a/pkg/deploy/predeploy.go
+++ b/pkg/deploy/predeploy.go
@@ -385,25 +385,27 @@ func (d *deployer) configureKeyvaultIssuers(ctx context.Context) error {
 }
 
 func (d *deployer) configureServiceSecrets(ctx context.Context) error {
-	err := d.ensureSecret(ctx, d.serviceKeyvault, env.EncryptionSecretName)
-	if err != nil {
-		return err
-	}
-
-	err = d.ensureSecret(ctx, d.serviceKeyvault, env.FrontendEncryptionSecretName)
-	if err != nil {
-		return err
-	}
-
-	err = d.ensureSecret(ctx, d.portalKeyvault, env.PortalServerSessionKeySecretName)
-	if err != nil {
-		return err
+	for _, s := range []struct {
+		kv         keyvault.Manager
+		secretName string
+		len        int
+	}{
+		{d.serviceKeyvault, env.EncryptionSecretName, 32},
+		{d.serviceKeyvault, env.EncryptionSecretV2Name, 64},
+		{d.serviceKeyvault, env.FrontendEncryptionSecretName, 32},
+		{d.serviceKeyvault, env.FrontendEncryptionSecretV2Name, 64},
+		{d.portalKeyvault, env.PortalServerSessionKeySecretName, 32},
+	} {
+		err := d.ensureSecret(ctx, s.kv, s.secretName, s.len)
+		if err != nil {
+			return err
+		}
 	}
 
 	return d.ensureSecretKey(ctx, d.portalKeyvault, env.PortalServerSSHKeySecretName)
 }
 
-func (d *deployer) ensureSecret(ctx context.Context, kv keyvault.Manager, secretName string) error {
+func (d *deployer) ensureSecret(ctx context.Context, kv keyvault.Manager, secretName string, len int) error {
 	existingSecrets, err := kv.GetSecrets(ctx)
 	if err != nil {
 		return err
@@ -415,7 +417,7 @@ func (d *deployer) ensureSecret(ctx context.Context, kv keyvault.Manager, secret
 		}
 	}
 
-	key := make([]byte, 32)
+	key := make([]byte, len)
 	_, err = rand.Read(key)
 	if err != nil {
 		return err
diff --git a/pkg/env/env.go b/pkg/env/env.go
index 30dd6d32a..8e17ad101 100644
--- a/pkg/env/env.go
+++ b/pkg/env/env.go
@@ -38,7 +38,9 @@ const (
 	RPServerSecretName               = "rp-server"
 	ClusterLoggingSecretName         = "cluster-mdsd"
 	EncryptionSecretName             = "encryption-key"
+	EncryptionSecretV2Name           = "encryption-key-v2"
 	FrontendEncryptionSecretName     = "fe-encryption-key"
+	FrontendEncryptionSecretV2Name   = "fe-encryption-key-v2"
 	DBTokenServerSecretName          = "dbtoken-server"
 	PortalServerSecretName           = "portal-server"
 	PortalServerClientSecretName     = "portal-client"
diff --git a/pkg/util/azureclient/keyvault/base_addons.go b/pkg/util/azureclient/keyvault/base_addons.go
index 6c57e115c..e88c1bdce 100644
--- a/pkg/util/azureclient/keyvault/base_addons.go
+++ b/pkg/util/azureclient/keyvault/base_addons.go
@@ -12,6 +12,7 @@ import (
 // BaseClientAddons contains addons for BaseClient
 type BaseClientAddons interface {
 	GetSecrets(ctx context.Context, vaultBaseURL string, maxresults *int32) (secrets []azkeyvault.SecretItem, err error)
+	GetSecretVersions(ctx context.Context, vaultBaseURL string, secretName string, maxresults *int32) (result []azkeyvault.SecretItem, err error)
 }
 
 func (c *baseClient) GetSecrets(ctx context.Context, vaultBaseURL string, maxresults *int32) (secrets []azkeyvault.SecretItem, err error) {
@@ -31,3 +32,21 @@ func (c *baseClient) GetSecrets(ctx context.Context, vaultBaseURL string, maxres
 
 	return secrets, nil
 }
+
+func (c *baseClient) GetSecretVersions(ctx context.Context, vaultBaseURL string, secretName string, maxresults *int32) (secrets []azkeyvault.SecretItem, err error) {
+	page, err := c.BaseClient.GetSecretVersions(ctx, vaultBaseURL, secretName, maxresults)
+	if err != nil {
+		return nil, err
+	}
+
+	for page.NotDone() {
+		secrets = append(secrets, page.Values()...)
+
+		err = page.NextWithContext(ctx)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	return secrets, nil
+}
diff --git a/pkg/util/encryption/generate.go b/pkg/util/encryption/generate.go
new file mode 100644
index 000000000..4aed7da48
--- /dev/null
+++ b/pkg/util/encryption/generate.go
@@ -0,0 +1,8 @@
+package encryption
+
+// Copyright (c) Microsoft Corporation.
+// Licensed under the Apache License 2.0.
+
+//go:generate rm -rf ../mocks/$GOPACKAGE
+//go:generate go run ../../../vendor/github.com/golang/mock/mockgen -destination=../mocks/$GOPACKAGE/$GOPACKAGE.go github.com/Azure/ARO-RP/pkg/util/$GOPACKAGE AEAD
+//go:generate go run ../../../vendor/golang.org/x/tools/cmd/goimports -local=github.com/Azure/ARO-RP -e -w ../mocks/$GOPACKAGE/$GOPACKAGE.go
diff --git a/pkg/util/encryption/multi.go b/pkg/util/encryption/multi.go
new file mode 100644
index 000000000..f1f875e97
--- /dev/null
+++ b/pkg/util/encryption/multi.go
@@ -0,0 +1,72 @@
+package encryption
+
+// Copyright (c) Microsoft Corporation.
+// Licensed under the Apache License 2.0.
+
+import (
+	"context"
+
+	"github.com/Azure/ARO-RP/pkg/util/keyvault"
+)
+
+type multi struct {
+	sealer  AEAD
+	openers []AEAD
+}
+
+var _ AEAD = (*multi)(nil)
+
+func NewMulti(ctx context.Context, serviceKeyvault keyvault.Manager, secretName, legacySecretName string) (AEAD, error) {
+	key, err := serviceKeyvault.GetBase64Secret(ctx, secretName, "")
+	if err != nil {
+		return nil, err
+	}
+
+	aead, err := NewAES256SHA512(ctx, key)
+	if err != nil {
+		return nil, err
+	}
+
+	m := &multi{
+		sealer: aead,
+	}
+
+	for _, x := range []struct {
+		secretName  string
+		aeadFactory func(context.Context, []byte) (AEAD, error)
+	}{
+		{secretName, NewAES256SHA512},
+		{legacySecretName, NewXChaCha20Poly1305},
+	} {
+		keys, err := serviceKeyvault.GetBase64Secrets(ctx, x.secretName)
+		if err != nil {
+			return nil, err
+		}
+
+		for _, key := range keys {
+			aead, err = x.aeadFactory(ctx, key)
+			if err != nil {
+				return nil, err
+			}
+
+			m.openers = append(m.openers, aead)
+		}
+	}
+
+	return m, nil
+}
+
+func (c *multi) Open(input []byte) (b []byte, err error) {
+	for _, opener := range c.openers {
+		b, err = opener.Open(input)
+		if err == nil {
+			return
+		}
+	}
+
+	return nil, err
+}
+
+func (c *multi) Seal(input []byte) ([]byte, error) {
+	return c.sealer.Seal(input)
+}
diff --git a/pkg/util/keyvault/keyvault.go b/pkg/util/keyvault/keyvault.go
index f43c51bab..5c96a1d95 100644
--- a/pkg/util/keyvault/keyvault.go
+++ b/pkg/util/keyvault/keyvault.go
@@ -10,6 +10,7 @@ import (
 	"crypto/x509/pkix"
 	"encoding/base64"
 	"fmt"
+	"path/filepath"
 	"strings"
 	"time"
 
@@ -33,7 +34,8 @@ const (
 type Manager interface {
 	CreateSignedCertificate(context.Context, string, string, string, Eku) error
 	EnsureCertificateDeleted(context.Context, string) error
-	GetBase64Secret(context.Context, string) ([]byte, error)
+	GetBase64Secret(context.Context, string, string) ([]byte, error)
+	GetBase64Secrets(context.Context, string) ([][]byte, error)
 	GetCertificateSecret(context.Context, string) (*rsa.PrivateKey, []*x509.Certificate, error)
 	GetSecret(context.Context, string) (azkeyvault.SecretBundle, error)
 	GetSecrets(context.Context) ([]azkeyvault.SecretItem, error)
@@ -141,8 +143,8 @@ func (m *manager) EnsureCertificateDeleted(ctx context.Context, certificateName
 	return err
 }
 
-func (m *manager) GetBase64Secret(ctx context.Context, secretName string) ([]byte, error) {
-	bundle, err := m.kv.GetSecret(ctx, m.keyvaultURI, secretName, "")
+func (m *manager) GetBase64Secret(ctx context.Context, secretName string, secretVersion string) ([]byte, error) {
+	bundle, err := m.kv.GetSecret(ctx, m.keyvaultURI, secretName, secretVersion)
 	if err != nil {
 		return nil, err
 	}
@@ -150,6 +152,28 @@ func (m *manager) GetBase64Secret(ctx context.Context, secretName string) ([]byt
 	return base64.StdEncoding.DecodeString(*bundle.Value)
 }
 
+func (m *manager) GetBase64Secrets(ctx context.Context, secretName string) (bs [][]byte, err error) {
+	versions, err := m.kv.GetSecretVersions(ctx, m.keyvaultURI, secretName, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	for _, version := range versions {
+		if !*version.Attributes.Enabled {
+			continue
+		}
+
+		b, err := m.GetBase64Secret(ctx, secretName, filepath.Base(*version.ID))
+		if err != nil {
+			return nil, err
+		}
+
+		bs = append(bs, b)
+	}
+
+	return bs, nil
+}
+
 func (m *manager) GetCertificateSecret(ctx context.Context, secretName string) (*rsa.PrivateKey, []*x509.Certificate, error) {
 	bundle, err := m.kv.GetSecret(ctx, m.keyvaultURI, secretName, "")
 	if err != nil {
diff --git a/pkg/util/mocks/azureclient/keyvault/keyvault.go b/pkg/util/mocks/azureclient/keyvault/keyvault.go
index 941b21c0d..528fdb2ec 100644
--- a/pkg/util/mocks/azureclient/keyvault/keyvault.go
+++ b/pkg/util/mocks/azureclient/keyvault/keyvault.go
@@ -110,6 +110,21 @@ func (mr *MockBaseClientMockRecorder) GetSecret(arg0, arg1, arg2, arg3 interface
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSecret", reflect.TypeOf((*MockBaseClient)(nil).GetSecret), arg0, arg1, arg2, arg3)
 }
 
+// GetSecretVersions mocks base method.
+func (m *MockBaseClient) GetSecretVersions(arg0 context.Context, arg1, arg2 string, arg3 *int32) ([]keyvault.SecretItem, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetSecretVersions", arg0, arg1, arg2, arg3)
+	ret0, _ := ret[0].([]keyvault.SecretItem)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// GetSecretVersions indicates an expected call of GetSecretVersions.
+func (mr *MockBaseClientMockRecorder) GetSecretVersions(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSecretVersions", reflect.TypeOf((*MockBaseClient)(nil).GetSecretVersions), arg0, arg1, arg2, arg3)
+}
+
 // GetSecrets mocks base method.
 func (m *MockBaseClient) GetSecrets(arg0 context.Context, arg1 string, arg2 *int32) ([]keyvault.SecretItem, error) {
 	m.ctrl.T.Helper()
diff --git a/pkg/util/mocks/encryption/encryption.go b/pkg/util/mocks/encryption/encryption.go
new file mode 100644
index 000000000..e1d454bba
--- /dev/null
+++ b/pkg/util/mocks/encryption/encryption.go
@@ -0,0 +1,64 @@
+// Code generated by MockGen. DO NOT EDIT.
+// Source: github.com/Azure/ARO-RP/pkg/util/encryption (interfaces: AEAD)
+
+// Package mock_encryption is a generated GoMock package.
+package mock_encryption
+
+import (
+	reflect "reflect"
+
+	gomock "github.com/golang/mock/gomock"
+)
+
+// MockAEAD is a mock of AEAD interface.
+type MockAEAD struct {
+	ctrl     *gomock.Controller
+	recorder *MockAEADMockRecorder
+}
+
+// MockAEADMockRecorder is the mock recorder for MockAEAD.
+type MockAEADMockRecorder struct {
+	mock *MockAEAD
+}
+
+// NewMockAEAD creates a new mock instance.
+func NewMockAEAD(ctrl *gomock.Controller) *MockAEAD {
+	mock := &MockAEAD{ctrl: ctrl}
+	mock.recorder = &MockAEADMockRecorder{mock}
+	return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockAEAD) EXPECT() *MockAEADMockRecorder {
+	return m.recorder
+}
+
+// Open mocks base method.
+func (m *MockAEAD) Open(arg0 []byte) ([]byte, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Open", arg0)
+	ret0, _ := ret[0].([]byte)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// Open indicates an expected call of Open.
+func (mr *MockAEADMockRecorder) Open(arg0 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Open", reflect.TypeOf((*MockAEAD)(nil).Open), arg0)
+}
+
+// Seal mocks base method.
+func (m *MockAEAD) Seal(arg0 []byte) ([]byte, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Seal", arg0)
+	ret0, _ := ret[0].([]byte)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// Seal indicates an expected call of Seal.
+func (mr *MockAEADMockRecorder) Seal(arg0 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Seal", reflect.TypeOf((*MockAEAD)(nil).Seal), arg0)
+}
diff --git a/pkg/util/mocks/keyvault/keyvault.go b/pkg/util/mocks/keyvault/keyvault.go
index 8c5f8a71a..3fc9ed3b4 100644
--- a/pkg/util/mocks/keyvault/keyvault.go
+++ b/pkg/util/mocks/keyvault/keyvault.go
@@ -68,18 +68,33 @@ func (mr *MockManagerMockRecorder) EnsureCertificateDeleted(arg0, arg1 interface
 }
 
 // GetBase64Secret mocks base method.
-func (m *MockManager) GetBase64Secret(arg0 context.Context, arg1 string) ([]byte, error) {
+func (m *MockManager) GetBase64Secret(arg0 context.Context, arg1, arg2 string) ([]byte, error) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetBase64Secret", arg0, arg1)
+	ret := m.ctrl.Call(m, "GetBase64Secret", arg0, arg1, arg2)
 	ret0, _ := ret[0].([]byte)
 	ret1, _ := ret[1].(error)
 	return ret0, ret1
 }
 
 // GetBase64Secret indicates an expected call of GetBase64Secret.
-func (mr *MockManagerMockRecorder) GetBase64Secret(arg0, arg1 interface{}) *gomock.Call {
+func (mr *MockManagerMockRecorder) GetBase64Secret(arg0, arg1, arg2 interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBase64Secret", reflect.TypeOf((*MockManager)(nil).GetBase64Secret), arg0, arg1)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBase64Secret", reflect.TypeOf((*MockManager)(nil).GetBase64Secret), arg0, arg1, arg2)
+}
+
+// GetBase64Secrets mocks base method.
+func (m *MockManager) GetBase64Secrets(arg0 context.Context, arg1 string) ([][]byte, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetBase64Secrets", arg0, arg1)
+	ret0, _ := ret[0].([][]byte)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// GetBase64Secrets indicates an expected call of GetBase64Secrets.
+func (mr *MockManagerMockRecorder) GetBase64Secrets(arg0, arg1 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBase64Secrets", reflect.TypeOf((*MockManager)(nil).GetBase64Secrets), arg0, arg1)
 }
 
 // GetCertificateSecret mocks base method.


Test patch:
diff --git a/pkg/util/encryption/multi_test.go b/pkg/util/encryption/multi_test.go
new file mode 100644
index 000000000..f3bdb0b8e
--- /dev/null
+++ b/pkg/util/encryption/multi_test.go
@@ -0,0 +1,78 @@
+package encryption
+
+// Copyright (c) Microsoft Corporation.
+// Licensed under the Apache License 2.0.
+
+import (
+	"errors"
+	"reflect"
+	"testing"
+
+	"github.com/golang/mock/gomock"
+
+	mock_encryption "github.com/Azure/ARO-RP/pkg/util/mocks/encryption"
+)
+
+func TestOpen(t *testing.T) {
+	mockInput := []byte("fakeInput")
+
+	type test struct {
+		name       string
+		mocks      func(firstOpener *mock_encryption.MockAEAD, secondOpener *mock_encryption.MockAEAD)
+		wantResult []byte
+		wantErr    string
+	}
+
+	for _, tt := range []*test{
+		{
+			name: "first opener succeeds, do not try second",
+			mocks: func(firstOpener *mock_encryption.MockAEAD, secondOpener *mock_encryption.MockAEAD) {
+				firstOpener.EXPECT().Open(mockInput).Return([]byte("result from the first opener"), nil)
+			},
+			wantResult: []byte("result from the first opener"),
+		},
+		{
+			name: "first opener errors, but second succeeds",
+			mocks: func(firstOpener *mock_encryption.MockAEAD, secondOpener *mock_encryption.MockAEAD) {
+				firstOpener.EXPECT().Open(mockInput).Return(nil, errors.New("fake error from the first opener"))
+				secondOpener.EXPECT().Open(mockInput).Return([]byte("result from the second opener"), nil)
+			},
+			wantResult: []byte("result from the second opener"),
+		},
+		{
+			name: "all openers error",
+			mocks: func(firstOpener *mock_encryption.MockAEAD, secondOpener *mock_encryption.MockAEAD) {
+				firstOpener.EXPECT().Open(mockInput).Return(nil, errors.New("fake error from the first opener"))
+				secondOpener.EXPECT().Open(mockInput).Return(nil, errors.New("fake error from the second opener"))
+			},
+			wantErr: "fake error from the second opener",
+		},
+	} {
+		t.Run(tt.name, func(t *testing.T) {
+			controller := gomock.NewController(t)
+			defer controller.Finish()
+
+			firstOpener := mock_encryption.NewMockAEAD(controller)
+			secondOpener := mock_encryption.NewMockAEAD(controller)
+
+			multi := multi{
+				openers: []AEAD{
+					firstOpener,
+					secondOpener,
+				},
+			}
+
+			tt.mocks(firstOpener, secondOpener)
+
+			b, err := multi.Open(mockInput)
+			if err != nil && err.Error() != tt.wantErr ||
+				err == nil && tt.wantErr != "" {
+				t.Error(err)
+			}
+			if b != nil && !reflect.DeepEqual(tt.wantResult, b) ||
+				b == nil && tt.wantResult != nil {
+				t.Error(b)
+			}
+		})
+	}
+}