---
# System prepended metadata

title: Share Encryption/Decryption Documentation

---

# Share Encryption/Decryption Documentation

This document describes how wallet shares are encrypted and decrypted in the RTOS backend system.

## Overview

Wallet shares are encrypted using a **hybrid encryption scheme** that combines:
- **RSA-OAEP** (asymmetric encryption) for encrypting the AES key
- **AES-256-GCM** (symmetric encryption) for encrypting the actual share data

This approach provides both security and efficiency: RSA ensures only the holder of the private key can decrypt the AES key, while AES-GCM provides fast encryption of the share data with built-in authentication.

## Encryption Process

### Input Parameters
- `plaintext`: The share data to encrypt (typically a JSON string containing the share information)
- `publicKeyPEM`: RSA public key in PEM format

### Steps

1. **Generate AES-256 Key**
   - Generate a random 32-byte (256-bit) AES key
   - This key is unique for each encryption operation

2. **Generate Initialization Vector (IV)**
   - Generate a random 12-byte nonce/IV for AES-GCM
   - The IV ensures that encrypting the same data multiple times produces different ciphertexts

3. **Encrypt Share Data with AES-256-GCM**
   - Use the AES key and IV to encrypt the plaintext share data
   - AES-GCM provides both confidentiality and authenticity
   - Extract the 16-byte authentication tag from the GCM operation

4. **Encrypt AES Key with RSA-OAEP**
   - Use RSA-OAEP with SHA-256 hash function
   - Encrypt the AES key using the provided RSA public key
   - This ensures only the holder of the corresponding private key can decrypt the AES key

5. **Format Encrypted Data**
   - Create a JSON object with the following fields:
     - `encryptedAesKey`: Base64-encoded encrypted AES key
     - `iv`: Hex-encoded 12-byte initialization vector
     - `authTag`: Hex-encoded 16-byte GCM authentication tag
     - `encrypted`: Hex-encoded encrypted share data
   - Return the JSON object as a string

### Encrypted Data Structure

```json
{
  "encryptedAesKey": "<base64-encoded-rsa-encrypted-aes-key>",
  "iv": "<hex-encoded-12-byte-iv>",
  "authTag": "<hex-encoded-16-byte-auth-tag>",
  "encrypted": "<hex-encoded-encrypted-share-data>"
}
```

## Decryption Process

### Input Parameters
- `encryptedDataJSON`: JSON string containing the encrypted share data
- `privateKeyPEM`: RSA private key in PEM format (corresponding to the public key used for encryption)

### Steps

1. **Parse Encrypted Data**
   - Parse the JSON string to extract the encrypted share data structure
   - Handle potential double-encoded JSON (nested JSON strings)
   - Validate that all required fields are present:
     - `encryptedAesKey`
     - `iv`
     - `authTag`
     - `encrypted`

2. **Decode Binary Data**
   - Decode `encryptedAesKey` from Base64 to bytes
   - Decode `iv` from hex to bytes (must be exactly 12 bytes)
   - Decode `authTag` from hex to bytes (must be exactly 16 bytes)
   - Decode `encrypted` from hex to bytes

3. **Decrypt AES Key with RSA-OAEP**
   - Use RSA-OAEP with SHA-256 hash function
   - Decrypt the AES key using the provided RSA private key
   - Validate that the decrypted AES key is exactly 32 bytes

4. **Decrypt Share Data with AES-256-GCM**
   - Use the decrypted AES key and IV to initialize the AES-GCM decipher
   - Set the authentication tag for verification
   - Decrypt the encrypted share data
   - GCM automatically verifies authenticity during decryption

5. **Return Plaintext**
   - Convert the decrypted bytes to UTF-8 string
   - Return the plaintext share data

## Security Features

### Why Hybrid Encryption?

- **Efficiency**: RSA encryption is computationally expensive and has size limitations. By encrypting only a small AES key with RSA, we avoid these limitations.
- **Security**: The AES key is protected by RSA, ensuring only authorized parties can decrypt shares.
- **Authenticity**: AES-GCM provides built-in authentication, detecting any tampering with the encrypted data.

### Key Security Properties

1. **Confidentiality**: Only holders of the private key can decrypt shares
2. **Authenticity**: GCM authentication tag ensures data integrity
3. **Non-repudiation**: Each encryption uses unique random values (AES key, IV)
4. **Forward Secrecy**: Each encryption operation uses a new AES key

## Implementation Details

### Encryption Algorithm Specifications

- **RSA Padding**: PKCS#1 OAEP
- **OAEP Hash**: SHA-256
- **AES Mode**: GCM (Galois/Counter Mode)
- **AES Key Size**: 256 bits (32 bytes)
- **IV/Nonce Size**: 12 bytes (96 bits)
- **Auth Tag Size**: 16 bytes (128 bits)

### Encoding Formats

- **RSA-encrypted AES key**: Base64
- **IV**: Hexadecimal (lowercase)
- **Auth Tag**: Hexadecimal (lowercase)
- **Encrypted data**: Hexadecimal (lowercase)
- **Final output**: JSON string

## Usage in the System

### Share Types

The system uses three types of shares, each encrypted with a different public key:

1. **Social Share**: Encrypted with the social public key
2. **Device Share**: Encrypted with the device public key
3. **Recovery Share**: Encrypted with the recovery public key

### Typical Flow

1. **Share Generation** (in TEE enclave):
   - Generate Shamir secret shares
   - Serialize each share to JSON
   - Encrypt each share with its corresponding public key
   - Return encrypted shares to the backend

2. **Share Storing** (partner):
   - Retrieve encrypted share
   - Decrypt share using their respective private key
   - Result is pure share that needs to be encrypted and stored securly

3. **Using share** (partner):
    - Encrypt pure share with Sorted public key using desribed method
    - Send encrypted share with wanted API endpoint

    In Enclave
    -  Provided Share is decrypted and serialized in json
    -  Social share decrypted and serialized in json

## Examples

### Example: Complete Encryption/Decryption Flow

This example demonstrates the complete encryption and decryption process using real data.

#### 1. Pure Share (Plaintext Input)

This is the unencrypted share data that needs to be encrypted:

```json
{
  "share": {
    "share": "0xf9fab8da4c726ede2525291b4ecb7db7959e0b918624364643bffd2cb89123e4",
    "shareIndex": "0x263a98ab39c7bdd9c7b453f50d3a44b262c8d58e9f14a03a6cbbade5a78b8a83"
  },
  "polynomialID": "0465edb4ff30c9f6e234f36103e92840b602d73592f64fb3a4c45ae9223c6c1d"
}
```

**Note**: When encrypting, this JSON object is converted to a string (using `JSON.stringify()`).

#### 2. RSA Key Pair

**Public Key** (used for encryption):
```
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApFCMrE9cgHMPfj1IvsRj
OVTrr+87ekyTzi3ZNoh/lCCtMYPUlxBohzK4OYOLNDQnR3OWrxmuI5V/3+9oEaNl
RybBTZacnhyPm92NYOrcd8wDNnxXHlUGbQQ6JhVjYqnqRKQSM1NQQW85ekKLiSxg
98X+p7vFXNOTwYcUkg04wUytyD1V0AbN4Vvr9008Vnsukr5w+hgYibawmf363gy0
056QuBkwQtBw6ChVFsRPEPmnJD/emK7dyGuW384ByVuRXnLCwrZ3XHJDNiYps7oF
6KoYfe8Bb8Xi0kvA049sCvPMDpppFX8Kn2xwqF1ZqRmTteH1d5dfiQiVYGtuDjuS
ywIDAQAB
-----END PUBLIC KEY-----
```

**Private Key** (used for decryption):
```
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCkUIysT1yAcw9+
PUi+xGM5VOuv7zt6TJPOLdk2iH+UIK0xg9SXEGiHMrg5g4s0NCdHc5avGa4jlX/f
72gRo2VHJsFNlpyeHI+b3Y1g6tx3zAM2fFceVQZtBDomFWNiqepEpBIzU1BBbzl6
QouJLGD3xf6nu8Vc05PBhxSSDTjBTK3IPVXQBs3hW+v3TTxWey6SvnD6GBiJtrCZ
/freDLTTnpC4GTBC0HDoKFUWxE8Q+ackP96Yrt3Ia5bfzgHJW5FecsLCtndcckM2
JimzugXoqhh97wFvxeLSS8DTj2wK88wOmmkVfwqfbHCoXVmpGZO14fV3l1+JCJVg
a24OO5LLAgMBAAECggEAQ2uDlO4JQ/EyIbeuogu9tNf5ztoH2xIRsY8RabVPN63C
sqbnc0Z5xUOT4JFvbC+cEE7GvLj6QUKF0hIO7vHOzAeEWDwcrimE6UzaRjKH3KW6
icAyFNGJTfDmlRrAiUqzw49YonOkYfzrphUo4NNzRCca6qL6g7CSl11AHP9M0s7l
UtHa52NXVZoQVsul0opG/IqRv6Y1x2uLc+aDY/NSAK5UONsHSJ4F+U1Q5EA8RDh0
mcFceHFqNn35+2ET6i6X2qipaZfUYtGMPoFtOFt6Z0M/C2BROTimG7GlKh5o7/2f
rD53dwc83VfFW0zGw3dNs8NqmsDP7dHwDaI0O0aDOQKBgQDdOMD3fo47CiegICdV
S/My3f4vxwCUTC3MNKgqmuoapDwVycaGQzfdKxC+foWFdZX0Hbr1RuzIiaE8E2gp
8uQujhVFbMq8eIui9JhnUUYw0Ngvyl1jiMeFfkOVu6V0ypBEA5BIyFGIxNGP5jiR
tnPu3o+GViouq8UsZ9VIzYXTWQKBgQC+JYaHbHZ/AFY4vpTi+DHNZu2w+ka/FTMl
W1DNnbKh1tg9aQ20NkunZjfzMPSxR2CWg/rlgPDYHObhwukVk+z0OeUu+5xmOgZn
SY77AeNnxrxlOlJ/Zkrvr9YkQC7md/KM2OaSR+MuHoPoqgS8Xn1wYBANQL5gYLkG
9z3hm6OGwwKBgQCAYZxSxQYjqyv92EsbTB1SCtSlw3ZJmcAGPxTMzORJG8Cm4Fcu
ubmkx4ZLFr/ECM5CQjKqf0OQyZZhd92+YnAbuGcNyCoLoGVg51O+ucLjN4AOlAEI
b2tixDjSn3hQw/FfFzZXlrECjz4SpYd0wI6dNFnUmxKVzSd7MrLrMiKU2QKBgD+k
79KES2VXB4UWxV7zFDhcK1MBcoyi2+u1FOa90fy5nMdxUlTvl4MAg9mymTR9X6cB
pgkLj5HGmsEWLE48pSNZ/YwsS70rztjjligOCb/ClWyMiPB7nLl9cX/Qu+IIY/cg
4owz46acMCrAPbWPKw1iGf1VnmND/KqINv48yg9hAoGBAIkAky1RQh0yUQYfpEBS
N5dq3fV3cj9YEHuFs4wPDp4p+rzNylNuVQVJfRIXN/LIPgBeWe04X3zuArkfVJZv
LG5c5hagkIfhKIgqJ7d9t94ORWVt8xbCgDDd0omK2hom0ITn0IiH6qt9yZGZy+4b
73Vtw71JTIHraPml8kAlPD3p
-----END PRIVATE KEY-----
```

#### 3. Encrypted Share (Output)

After encrypting the pure share with the public key, the result is:

```json
{
  "encryptedAesKey": "M4Mh3VOaLDU0rXjL83Ok4gBVVtdOvmNbPuEd4RN/bNMskHX5iLR0zgwQq7CHlLM0lTyCXPovPc06i26OqeS40c1G76ie0UOZyjeJsbvn8IlVxdiXxNHXOhD1RhW/M5FJmXaESjzC5mjQJXXR7t6tdEgIjSrfdqHOCx84hZUmsJDKwhjCZW4VqoDLrDn2w3rQOLlLZygHLPeWU3hOMiw2cX+TIJTe2G2OV07YSTjSa1WlpEKbIdUQrdxT7kCI7XBNsR6nrhuG2i4XzmqfM9GhcPQR94yVpoPcpMqqjOHFU/I4K6/NkYDaQqK4Sp6C2N4Y+gJAEzMNSh4YNn35ofqjmg==",
  "iv": "08e54f87b4324500d9e9c14e",
  "authTag": "55c07236a0e590a9fd735e5bd63d84e4",
  "encrypted": "42416e485875e7411468d8297b50209e51341ee5c77cf15786b2eb63838fdad75a4c9f2303ff98691e8aa1bbf44b901c19bcf3cec8c089fafdd0005e6374dcf3cc4e5f07698131eb2f4bb6ff9a421c749d108fbe19bb5944f2bb20b38e50c2f1aed2034fca127d8236957abd02d272ed291b3b70b3253293c88ed6911b214e57a0d96949e3ab137bd6a8aa4a0d63d23ec3d186fdc326dba2287f7c5ffc5f405667d2bd6adf17dd94c6d750d2baedb660beb00fc551191543cb5bb1b078cffa1ff4189f4dae1cd6092a8b1b716fa150232aeb7eb4354f707a2a2a0fabd378c0daf1f3b1775cb4277d325950a4a52743de2b44ff8d83768d1a6f33bf43"
}
```

**Field Breakdown:**
- `encryptedAesKey`: Base64-encoded RSA-OAEP encrypted AES-256 key (256 bytes when decoded)
- `iv`: Hex-encoded 12-byte initialization vector (`08e54f87b4324500d9e9c14e` = 12 bytes)
- `authTag`: Hex-encoded 16-byte GCM authentication tag (`55c07236a0e590a9fd735e5bd63d84e4` = 16 bytes)
- `encrypted`: Hex-encoded AES-GCM encrypted share data

#### 4. Code Example

**Encryption** (TypeScript):
```typescript

const plaintextShare = JSON.stringify({
  share: {
    share: "0xf9fab8da4c726ede2525291b4ecb7db7959e0b918624364643bffd2cb89123e4",
    shareIndex: "0x263a98ab39c7bdd9c7b453f50d3a44b262c8d58e9f14a03a6cbbade5a78b8a83"
  },
  polynomialID: "0465edb4ff30c9f6e234f36103e92840b602d73592f64fb3a4c45ae9223c6c1d"
});

const publicKeyPEM = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApFCMrE9cgHMPfj1IvsRj
OVTrr+87ekyTzi3ZNoh/lCCtMYPUlxBohzK4OYOLNDQnR3OWrxmuI5V/3+9oEaNl
RybBTZacnhyPm92NYOrcd8wDNnxXHlUGbQQ6JhVjYqnqRKQSM1NQQW85ekKLiSxg
98X+p7vFXNOTwYcUkg04wUytyD1V0AbN4Vvr9008Vnsukr5w+hgYibawmf363gy0
056QuBkwQtBw6ChVFsRPEPmnJD/emK7dyGuW384ByVuRXnLCwrZ3XHJDNiYps7oF
6KoYfe8Bb8Xi0kvA049sCvPMDpppFX8Kn2xwqF1ZqRmTteH1d5dfiQiVYGtuDjuS
ywIDAQAB
-----END PUBLIC KEY-----`;

const encryptedShare = ShareEncryption.encryptShareWithPublicKey(
  plaintextShare,
  publicKeyPEM
);

console.log(encryptedShare);
```

**Decryption** (TypeScript):
```typescript

const encryptedShareJSON = `{
  "encryptedAesKey": "M4Mh3VOaLDU0rXjL83Ok4gBVVtdOvmNbPuEd4RN/bNMskHX5iLR0zgwQq7CHlLM0lTyCXPovPc06i26OqeS40c1G76ie0UOZyjeJsbvn8IlVxdiXxNHXOhD1RhW/M5FJmXaESjzC5mjQJXXR7t6tdEgIjSrfdqHOCx84hZUmsJDKwhjCZW4VqoDLrDn2w3rQOLlLZygHLPeWU3hOMiw2cX+TIJTe2G2OV07YSTjSa1WlpEKbIdUQrdxT7kCI7XBNsR6nrhuG2i4XzmqfM9GhcPQR94yVpoPcpMqqjOHFU/I4K6/NkYDaQqK4Sp6C2N4Y+gJAEzMNSh4YNn35ofqjmg==",
  "iv": "08e54f87b4324500d9e9c14e",
  "authTag": "55c07236a0e590a9fd735e5bd63d84e4",
  "encrypted": "42416e485875e7411468d8297b50209e51341ee5c77cf15786b2eb63838fdad75a4c9f2303ff98691e8aa1bbf44b901c19bcf3cec8c089fafdd0005e6374dcf3cc4e5f07698131eb2f4bb6ff9a421c749d108fbe19bb5944f2bb20b38e50c2f1aed2034fca127d8236957abd02d272ed291b3b70b3253293c88ed6911b214e57a0d96949e3ab137bd6a8aa4a0d63d23ec3d186fdc326dba2287f7c5ffc5f405667d2bd6adf17dd94c6d750d2baedb660beb00fc551191543cb5bb1b078cffa1ff4189f4dae1cd6092a8b1b716fa150232aeb7eb4354f707a2a2a0fabd378c0daf1f3b1775cb4277d325950a4a52743de2b44ff8d83768d1a6f33bf43"
}`;

const privateKeyPEM = `-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCkUIysT1yAcw9+
PUi+xGM5VOuv7zt6TJPOLdk2iH+UIK0xg9SXEGiHMrg5g4s0NCdHc5avGa4jlX/f
72gRo2VHJsFNlpyeHI+b3Y1g6tx3zAM2fFceVQZtBDomFWNiqepEpBIzU1BBbzl6
QouJLGD3xf6nu8Vc05PBhxSSDTjBTK3IPVXQBs3hW+v3TTxWey6SvnD6GBiJtrCZ
/freDLTTnpC4GTBC0HDoKFUWxE8Q+ackP96Yrt3Ia5bfzgHJW5FecsLCtndcckM2
JimzugXoqhh97wFvxeLSS8DTj2wK88wOmmkVfwqfbHCoXVmpGZO14fV3l1+JCJVg
a24OO5LLAgMBAAECggEAQ2uDlO4JQ/EyIbeuogu9tNf5ztoH2xIRsY8RabVPN63C
sqbnc0Z5xUOT4JFvbC+cEE7GvLj6QUKF0hIO7vHOzAeEWDwcrimE6UzaRjKH3KW6
icAyFNGJTfDmlRrAiUqzw49YonOkYfzrphUo4NNzRCca6qL6g7CSl11AHP9M0s7l
UtHa52NXVZoQVsul0opG/IqRv6Y1x2uLc+aDY/NSAK5UONsHSJ4F+U1Q5EA8RDh0
mcFceHFqNn35+2ET6i6X2qipaZfUYtGMPoFtOFt6Z0M/C2BROTimG7GlKh5o7/2f
rD53dwc83VfFW0zGw3dNs8NqmsDP7dHwDaI0O0aDOQKBgQDdOMD3fo47CiegICdV
S/My3f4vxwCUTC3MNKgqmuoapDwVycaGQzfdKxC+foWFdZX0Hbr1RuzIiaE8E2gp
8uQujhVFbMq8eIui9JhnUUYw0Ngvyl1jiMeFfkOVu6V0ypBEA5BIyFGIxNGP5jiR
tnPu3o+GViouq8UsZ9VIzYXTWQKBgQC+JYaHbHZ/AFY4vpTi+DHNZu2w+ka/FTMl
W1DNnbKh1tg9aQ20NkunZjfzMPSxR2CWg/rlgPDYHObhwukVk+z0OeUu+5xmOgZn
SY77AeNnxrxlOlJ/Zkrvr9YkQC7md/KM2OaSR+MuHoPoqgS8Xn1wYBANQL5gYLkG
9z3hm6OGwwKBgQCAYZxSxQYjqyv92EsbTB1SCtSlw3ZJmcAGPxTMzORJG8Cm4Fcu
ubmkx4ZLFr/ECM5CQjKqf0OQyZZhd92+YnAbuGcNyCoLoGVg51O+ucLjN4AOlAEI
b2tixDjSn3hQw/FfFzZXlrECjz4SpYd0wI6dNFnUmxKVzSd7MrLrMiKU2QKBgD+k
79KES2VXB4UWxV7zFDhcK1MBcoyi2+u1FOa90fy5nMdxUlTvl4MAg9mymTR9X6cB
pgkLj5HGmsEWLE48pSNZ/YwsS70rztjjligOCb/ClWyMiPB7nLl9cX/Qu+IIY/cg
4owz46acMCrAPbWPKw1iGf1VnmND/KqINv48yg9hAoGBAIkAky1RQh0yUQYfpEBS
N5dq3fV3cj9YEHuFs4wPDp4p+rzNylNuVQVJfRIXN/LIPgBeWe04X3zuArkfVJZv
LG5c5hagkIfhKIgqJ7d9t94ORWVt8xbCgDDd0omK2hom0ITn0IiH6qt9yZGZy+4b
73Vtw71JTIHraPml8kAlPD3p
-----END PRIVATE KEY-----`;

const decryptedShare = ShareEncryption.decryptShareWithPrivateKey(
  encryptedShareJSON,
  privateKeyPEM
);

const shareData = JSON.parse(decryptedShare);
console.log(shareData);
```

#### 5. Verification

When you decrypt the encrypted share using the private key, you should get back the original pure share:

```json
{
  "share": {
    "share": "0xf9fab8da4c726ede2525291b4ecb7db7959e0b918624364643bffd2cb89123e4",
    "shareIndex": "0x263a98ab39c7bdd9c7b453f50d3a44b262c8d58e9f14a03a6cbbade5a78b8a83"
  },
  "polynomialID": "0465edb4ff30c9f6e234f36103e92840b602d73592f64fb3a4c45ae9223c6c1d"
}
```

**Important Notes:**
- The encryption process generates a new random AES key and IV each time, so encrypting the same share multiple times will produce different encrypted outputs
- However, decrypting any of these encrypted outputs with the correct private key will always produce the same original share
- The `iv` and `authTag` values are unique for each encryption operation




#### 6. Fully working share encryption/decryption script
```
import crypto from 'crypto'

/**
 * Encrypted share data structure matching Go implementation
 */
interface EncryptedShareData {
  encryptedAesKey: string // Base64 encoded
  iv: string // Hex encoded (12 bytes)
  authTag: string // Hex encoded (16 bytes)
  encrypted: string // Hex encoded
}

/**
 * ShareEncryption service for encrypting/decrypting wallet shares
 * Matches the Go implementation in enclave/go/encryption.go
 */
export class ShareEncryption {
  /**
   * Encrypt share data using RSA-OAEP + AES-GCM
   * Matches Go EncryptShareWithPublicKey format
   *
   * @param plaintext - Plaintext data to encrypt (e.g., JSON string of share)
   * @param publicKeyPEM - RSA public key in PEM format
   * @returns JSON string containing encrypted share data
   */
  static encryptShareWithPublicKey(plaintext: string, publicKeyPEM: string): string {
    // Parse RSA public key
    const publicKey = crypto.createPublicKey(publicKeyPEM)

    // Generate random AES-256 key (32 bytes)
    const aesKey = crypto.randomBytes(32)

    // Generate random nonce (12 bytes for GCM)
    const iv = crypto.randomBytes(12)

    // Encrypt plaintext with AES-256-GCM
    const cipher = crypto.createCipheriv('aes-256-gcm', aesKey, iv)
    const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()])
    const authTag = cipher.getAuthTag()

    // Encrypt AES key with RSA-OAEP SHA256
    const encryptedAesKey = crypto.publicEncrypt(
      {
        key: publicKey,
        padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
        oaepHash: 'sha256',
      },
      aesKey
    )

    // Create encrypted share data structure (matching Go format)
    const encryptedShare: EncryptedShareData = {
      encryptedAesKey: encryptedAesKey.toString('base64'),
      iv: iv.toString('hex'),
      authTag: authTag.toString('hex'),
      encrypted: encrypted.toString('hex'),
    }

    // Convert to JSON string
    return JSON.stringify(encryptedShare)
  }

  /**
   * Decrypt share data encrypted with RSA-OAEP + AES-GCM
   * Matches Go DecryptShareWithPrivateKey format
   *
   * @param encryptedDataJSON - JSON string containing encrypted share data
   * @param privateKeyPEM - RSA private key in PEM format
   * @returns Decrypted plaintext string
   */
  static decryptShareWithPrivateKey(encryptedDataJSON: string, privateKeyPEM: string): string {
    // Parse encrypted share data
    let encryptedShare: EncryptedShareData

    try {
      encryptedShare = JSON.parse(encryptedDataJSON)
    } catch (error) {
      // Try parsing as string first (handle double-encoded JSON)
      try {
        const jsonStr = JSON.parse(encryptedDataJSON)
        encryptedShare = JSON.parse(jsonStr)
      } catch {
        throw new Error('Failed to parse encrypted data: invalid JSON format')
      }
    }

    // Validate required fields
    if (
      !encryptedShare.encryptedAesKey ||
      !encryptedShare.iv ||
      !encryptedShare.authTag ||
      !encryptedShare.encrypted
    ) {
      throw new Error(
        'Invalid encrypted data format: missing required fields (encryptedAesKey, iv, authTag, or encrypted)'
      )
    }

    // Decode base64 encrypted AES key
    const encryptedAesKeyBytes = Buffer.from(encryptedShare.encryptedAesKey, 'base64')
    if (encryptedAesKeyBytes.length === 0) {
      throw new Error('Invalid encrypted AES key: empty buffer')
    }

    // Decode hex nonce
    const iv = Buffer.from(encryptedShare.iv, 'hex')
    if (iv.length !== 12) {
      throw new Error(`Invalid nonce length: expected 12 bytes, got ${iv.length}`)
    }

    // Decode hex auth tag
    const authTag = Buffer.from(encryptedShare.authTag, 'hex')
    if (authTag.length !== 16) {
      throw new Error(`Invalid auth tag length: expected 16 bytes, got ${authTag.length}`)
    }

    // Decode hex encrypted data
    const encryptedData = Buffer.from(encryptedShare.encrypted, 'hex')

    // Parse RSA private key
    const privateKey = crypto.createPrivateKey(privateKeyPEM)

    // Decrypt AES key with RSA-OAEP SHA256
    let aesKey: Buffer
    try {
      aesKey = crypto.privateDecrypt(
        {
          key: privateKey,
          padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
          oaepHash: 'sha256',
        },
        encryptedAesKeyBytes
      )
    } catch (error) {
      throw new Error(
        'RSA OAEP decryption failed: The encrypted data was likely encrypted with a different public key'
      )
    }

    if (aesKey.length !== 32) {
      throw new Error(`Invalid decrypted AES key length: expected 32 bytes, got ${aesKey.length}`)
    }

    // Decrypt data with AES-256-GCM
    const decipher = crypto.createDecipheriv('aes-256-gcm', aesKey, iv)
    decipher.setAuthTag(authTag)

    const decrypted = Buffer.concat([decipher.update(encryptedData), decipher.final()])

    return decrypted.toString('utf8')
  }
}

```
````

