@@ -9,7 +9,9 @@ package api
9
9
import (
10
10
"bytes"
11
11
"context"
12
+ "crypto"
12
13
"crypto/ecdsa"
14
+ "crypto/rand"
13
15
"crypto/rsa"
14
16
"crypto/sha256"
15
17
"crypto/tls"
@@ -159,38 +161,33 @@ func VerifyMarbleRunDeployment(ctx context.Context, endpoint string, opts Verify
159
161
}
160
162
161
163
// Recover performs recovery on a Coordinator instance by setting the decrypted recoverySecret.
164
+ // The signer is used to generate a signature over the recoverySecret.
165
+ // The Coordinator will verify this signature matches one of the recovery public keys set in the manifest.
162
166
// On success, it returns the number of remaining recovery secrets to be set,
163
167
// as well as the verified SGX quote.
164
168
//
165
169
// If this function is called from inside an EGo enclave, the "marblerun_ego_enclave" build tag must be set when building the binary.
166
- func Recover (ctx context.Context , endpoint string , opts VerifyOptions , recoverySecret []byte ) (remaining int , sgxQuote []byte , err error ) {
167
- opts .setDefaults ()
168
-
169
- rootCert , _ , sgxQuote , err := VerifyCoordinator (ctx , endpoint , opts )
170
+ func Recover (ctx context.Context , endpoint string , opts VerifyOptions , recoverySecret []byte , signer crypto.Signer ) (remaining int , sgxQuote []byte , err error ) {
171
+ signature , err := util .SignPKCS1v15 (signer , recoverySecret )
170
172
if err != nil {
171
173
return - 1 , nil , err
172
174
}
175
+ return recoverCoordinator (ctx , endpoint , opts , recoverySecret , signature )
176
+ }
173
177
174
- client , err := rest .NewClient (endpoint , rootCert , nil )
175
- if err != nil {
176
- return - 1 , nil , fmt .Errorf ("setting up client: %w" , err )
177
- }
178
-
179
- // Attempt recovery using the v2 API first
180
- remaining , err = recoverV2 (ctx , client , recoverySecret )
181
- if rest .IsNotAllowedErr (err ) {
182
- remaining , err = recoverV1 (ctx , client , recoverySecret )
183
- }
184
- if err != nil {
185
- return - 1 , nil , fmt .Errorf ("sending recovery request: %w" , err )
186
- }
187
-
188
- return remaining , sgxQuote , err
178
+ // RecoverWithSignature performs recovery on a Coordinator instance by setting the decrypted recoverySecret.
179
+ // This is the same as [Recover], but allows passing in the recoverySecretSignature directly,
180
+ // instead of generating it using a [crypto.Signer].
181
+ // The recoveryKeySignature must be a PKCS#1 v1.5 signature over the SHA-256 hash of recoverySecret.
182
+ //
183
+ // If this function is called from inside an EGo enclave, the "marblerun_ego_enclave" build tag must be set when building the binary.
184
+ func RecoverWithSignature (ctx context.Context , endpoint string , opts VerifyOptions , recoverySecret , recoverySecretSignature []byte ) (remaining int , sgxQuote []byte , err error ) {
185
+ return recoverCoordinator (ctx , endpoint , opts , recoverySecret , recoverySecretSignature )
189
186
}
190
187
191
188
// DecryptRecoveryData decrypts recovery data returned by a Coordinator during [ManifestSet] using a parties private recovery key.
192
- func DecryptRecoveryData (recoveryData []byte , recoveryPrivateKey * rsa. PrivateKey ) ([]byte , error ) {
193
- return util . DecryptOAEP ( recoveryPrivateKey , recoveryData )
189
+ func DecryptRecoveryData (recoveryData []byte , recoveryPrivateKey crypto. Decrypter ) ([]byte , error ) {
190
+ return recoveryPrivateKey . Decrypt ( rand . Reader , recoveryData , & rsa. OAEPOptions { Hash : crypto . SHA256 } )
194
191
}
195
192
196
193
// GetStatus retrieves the status of a MarbleRun Coordinator instance.
@@ -577,3 +574,30 @@ func getMarbleCredentialsFromEnv() (tls.Certificate, *x509.Certificate, error) {
577
574
578
575
return tlsCert , coordinatorRoot , nil
579
576
}
577
+
578
+ // recoverCoordinator performs recovery on a Coordinator instance by setting the decrypted recoverySecret.
579
+ // The signer is used to generate a signature over the recoverySecret.
580
+ // The Coordinator will verify this signature matches one of the recovery public keys set in the manifest.
581
+ // On success, it returns the number of remaining recovery secrets to be set,
582
+ // as well as the verified SGX quote.
583
+ func recoverCoordinator (ctx context.Context , endpoint string , opts VerifyOptions , recoverySecret , recoverySecretSignature []byte ) (remaining int , sgxQuote []byte , err error ) {
584
+ opts .setDefaults ()
585
+
586
+ rootCert , _ , sgxQuote , err := VerifyCoordinator (ctx , endpoint , opts )
587
+ if err != nil {
588
+ return - 1 , nil , err
589
+ }
590
+
591
+ client , err := rest .NewClient (endpoint , rootCert , nil )
592
+ if err != nil {
593
+ return - 1 , nil , fmt .Errorf ("setting up client: %w" , err )
594
+ }
595
+
596
+ // The v1 API does not support recovery, therefore only attempt the v2 API
597
+ remaining , err = recoverV2 (ctx , client , recoverySecret , recoverySecretSignature )
598
+ if err != nil {
599
+ return - 1 , nil , fmt .Errorf ("sending recovery request: %w" , err )
600
+ }
601
+
602
+ return remaining , sgxQuote , err
603
+ }
0 commit comments