Create MPC Keys - EdDSA
Create MPC key shares for EdDSA assets by exchanging shares between parties. See the Guide.
-
Set up the BitGo SDK to generate EdDSA key shares. You initialize the MPC library, generate a GPG key pair for encrypting private share material, and retrieve the BitGo public key for secure key exchange.
Prerequisites: You must have a BitGo account. See Get Started.
-
Generate the user key shares. The user key share (party index 1) contains a
uShare(your private share) andySharesto distribute to the backup and BitGo parties. -
Generate the backup key shares. The backup key share (party index 2) contains a
uShareandySharesto distribute to the user and BitGo parties. Unlike multisignature wallets, you must manage the backup key yourself. -
Send the user and backup key shares to BitGo so it can create its own key share (party index 3). You encrypt the private shares using the BitGo GPG public key before sending. BitGo returns BitGo-to-user and BitGo-to-backup shares, the
commonKeychain, and a keyid.This response contains critical key material. Save it in a secure place.
-
Combine the user's own share with the backup and BitGo shares to create the user keychain. You must verify that the derived
commonKeychainmatches the one BitGo returned. The SDK encrypts the combined key material with your passphrase and uploads it to BitGo.This response contains critical key material. Save it in a secure place.
-
Combine the backup share with the user and BitGo shares to create the backup keychain. Verify the derived
commonKeychainmatches. Store the backup signing material locally — do not upload it to BitGo. BitGo registers only the publiccommonKeychainreference.This response contains critical key material. Save it in a secure place.
// 1. Set Up SDK
// Use the JavaScript SDK for this step (see JavaScript tab)
// 2. Create User Key Shares
// Use the JavaScript SDK for this step (see JavaScript tab)
// 3. Create Backup Key Shares
// Use the JavaScript SDK for this step (see JavaScript tab)
// 4. Create BitGo Keychain
export COIN="<ASSET_ID>"
export ACCESS_TOKEN="<YOUR_ACCESS_TOKEN>"
export ENTERPRISE_ID="<ENTERPRISE_ID>"
curl -X POST \
https://app.bitgo-test.com/api/v2/$COIN/key \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-d '{
"keyType":"tss",
"source": "bitgo",
"enterprise": "'"$ENTERPRISE_ID"'",
"keyShares": [
{
"from": "user",
"to": "bitgo",
"publicShare": "<USER_PUBLIC_SHARE>",
"privateShare": "<PGP_ENCRYPTED_PRIVATE_SHARE>",
"privateShareProof": "<PGP_PUBLIC_KEY_BLOCK>"
},
{
"from": "backup",
"to": "bitgo",
"publicShare": "<BACKUP_PUBLIC_SHARE>",
"privateShare": "<PGP_ENCRYPTED_PRIVATE_SHARE>",
"privateShareProof": "<PGP_PUBLIC_KEY_BLOCK>"
}
],
"userGPGPublicKey": "<USER_PGP_PUBLIC_KEY>",
"backupGPGPublicKey": "<BACKUP_PGP_PUBLIC_KEY>"
}'
// 5. Create User Keychain
curl -X POST \
https://app.bitgo-test.com/api/v2/$COIN/key \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-d '{
"commonKeychain": "<COMMON_KEYCHAIN>",
"encryptedPrv": "<ENCRYPTED_PRV>",
"keyType":"tss",
"source": "user",
"originalPasscodeEncryptionCode": "<CODE>"
}'
// 6. Create Backup Keychain
curl -X POST \
https://app.bitgo-test.com/api/v2/$COIN/key \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-d '{
"commonKeychain": "<COMMON_KEYCHAIN>",
"encryptedPrv": "<ENCRYPTED_PRV>",
"keyType":"tss",
"source": "backup"
}'
// 1. Set Up SDK
const bitgo = new BitGo({
accessToken: "my access token",
env: "test",
});
const enterprise = "my enterprise id";
const passphrase = "my very secure wallet passphrase";
const originalPasscodeEncryptionCode = "1234";
const coin = "tsol";
const m = 2;
const n = 3;
openpgp.config.rejectCurves = new Set();
async function main() {
const uploadKeyUrl = bitgo.microservicesUrl(`/api/v2/${coin}/key`);
const MPC = await Eddsa.initialize();
const randomHexString = crypto.randomBytes(12).toString("hex");
const userGpgKey = await openpgp.generateKey({
userIDs: [
{
name: randomHexString,
email: `user-${randomHexString}@${randomHexString}.com`,
},
],
curve: "secp256k1",
});
const bitgoPublicKeyStr = (await bitgo.fetchConstants()).mpc
.bitgoPublicKey as string;
const bitgoKey = await openpgp.readKey({ armoredKey: bitgoPublicKeyStr });
console.log("\n\nbitgo public key");
console.log(bitgoPublicKeyStr);
// 2. Create User Key Shares
const userKeyShare = MPC.keyShare(1, m, n);
console.log("\n\ncreated user key share");
console.log(JSON.stringify(userKeyShare, undefined, 2));
// 3. Create Backup Key Shares
const backupKeyShare = MPC.keyShare(2, m, n);
console.log("\n\ncreated backup key share");
console.log(JSON.stringify(backupKeyShare, undefined, 2));
// 4. Create BitGo Keychain
const userToBitgoPublicShare = Buffer.concat([
Buffer.from(userKeyShare.uShare.y, "hex"),
Buffer.from(userKeyShare.uShare.chaincode, "hex"),
]).toString("hex");
const userToBitgoPrivateShare = Buffer.concat([
Buffer.from(userKeyShare.yShares[3].u, "hex"),
Buffer.from(userKeyShare.yShares[3].chaincode, "hex"),
]).toString("hex");
const userToBitgoKeyShare = {
publicShare: userToBitgoPublicShare,
privateShare: userToBitgoPrivateShare,
privateShareProof: await createShareProof(
userGpgKey.privateKey,
userToBitgoPrivateShare.slice(0, 64)
),
};
const backupToBitgoPublicShare = Buffer.concat([
Buffer.from(backupKeyShare.uShare.y, "hex"),
Buffer.from(backupKeyShare.uShare.chaincode, "hex"),
]).toString("hex");
const backupToBitgoPrivateShare = Buffer.concat([
Buffer.from(backupKeyShare.yShares[3].u, "hex"),
Buffer.from(backupKeyShare.yShares[3].chaincode, "hex"),
]).toString("hex");
const backupToBitgoKeyShare = {
publicShare: backupToBitgoPublicShare,
privateShare: backupToBitgoPrivateShare,
privateShareProof: await createShareProof(
userGpgKey.privateKey,
backupToBitgoPrivateShare.slice(0, 64)
),
};
const encUserToBitGoMessage = await encryptText(
userToBitgoKeyShare.privateShare,
bitgoKey
);
const encBackupToBitGoMessage = await encryptText(
userToBitgoKeyShare.privateShare,
bitgoKey
);
const createBitGoMPCParams = {
keyType: "MPC",
source: "bitgo",
keyShares: [
{
from: "user",
to: "bitgo",
publicShare: userToBitgoKeyShare.publicShare,
privateShare: encUserToBitGoMessage,
privateShareProof: userToBitgoKeyShare.privateShareProof,
},
{
from: "backup",
to: "bitgo",
publicShare: backupToBitgoKeyShare.publicShare,
privateShare: encBackupToBitGoMessage,
privateShareProof: backupToBitgoKeyShare.privateShareProof,
},
],
userGPGPublicKey: userGpgKey.publicKey,
backupGPGPublicKey: userGpgKey.publicKey,
enterprise: enterprise,
};
const bitgoKeychain = (await bitgo
.post(uploadKeyUrl)
.send(createBitGoMPCParams)
.result()) as any as Keychain;
console.log("\n\ncreated bitgo keychain");
console.log(JSON.stringify(bitgoKeychain, undefined, 2));
// 5. Create User Keychain
const bitgoKeyShares = bitgoKeychain.keyShares;
const bitGoToUserShare = bitgoKeyShares!.find(
(keyShare) => keyShare.from === "bitgo" && keyShare.to === "user"
);
const bitGoToUserPrivateShare = await decryptPrivateShare(
bitGoToUserShare!.privateShare,
userGpgKey
);
const bitgoToUser = {
i: 1,
j: 3,
y: bitGoToUserShare!.publicShare.slice(0, 64),
u: bitGoToUserPrivateShare.slice(0, 64).toString(),
chaincode: bitGoToUserPrivateShare.slice(64).toString(),
};
const userCombined = MPC.keyCombine(userKeyShare.uShare, [
backupKeyShare.yShares[1],
bitgoToUser,
]);
const commonKeychain =
userCombined.pShare.y + userCombined.pShare.chaincode;
if (commonKeychain !== bitgoKeychain.commonKeychain) {
throw new Error(
"Failed to create user keychain - commonKeychains do not match."
);
}
const userSigningMaterial = {
uShare: userKeyShare.uShare,
bitgoYShare: bitgoToUser,
backupYShare: backupKeyShare.yShares[1],
};
const userKeychainParams: AddKeychainOptions = {
source: "user",
keyType: "MPC",
commonKeychain: bitgoKeychain.commonKeychain,
encryptedPrv: bitgo.encrypt({
input: JSON.stringify(userSigningMaterial),
password: passphrase,
}),
originalPasscodeEncryptionCode,
};
const userKeychain = (await bitgo
.post(uploadKeyUrl)
.send(userKeychainParams)
.result()) as any as Keychain;
console.log("\n\ncreated user keychain");
console.log(JSON.stringify(userKeychain, undefined, 2));
// 6. Create Backup Keychain
const bitGoToBackupShare = bitgoKeyShares!.find(
(keyShare) => keyShare.from === "bitgo" && keyShare.to === "backup"
);
const bitGoToBackupPrivateShare = await decryptPrivateShare(
bitGoToBackupShare!.privateShare,
userGpgKey
);
const bitgoToBackup = {
i: 2,
j: 3,
y: bitGoToBackupShare!.publicShare.slice(0, 64),
u: bitGoToBackupPrivateShare.slice(0, 64).toString(),
chaincode: bitGoToBackupPrivateShare.slice(64).toString(),
};
const backupCombined = MPC.keyCombine(backupKeyShare.uShare, [
userKeyShare.yShares[2],
bitgoToBackup,
]);
const backupCommonKeychain =
backupCombined.pShare.y + backupCombined.pShare.chaincode;
if (backupCommonKeychain !== bitgoKeychain.commonKeychain) {
throw new Error(
"Failed to create backup keychain - commonKeychains do not match."
);
}
const backupSigningMaterial = {
uShare: backupKeyShare.uShare,
bitgoYShare: bitgoToBackup,
userYShare: userKeyShare.yShares[2],
};
console.log(
"\n\ncreated backup keychain (not uploaded to bitgo, must be stored locally)"
);
console.log(JSON.stringify(backupSigningMaterial, undefined, 2));
const backupKeychainParams: AddKeychainOptions = {
source: "backup",
keyType: "MPC",
commonKeychain: backupCommonKeychain,
};
const backupKeychain = (await bitgo
.post(uploadKeyUrl)
.send(backupKeychainParams)
.result()) as any as Keychain;
console.log("\n\ncreated backup keychain");
console.log(JSON.stringify(backupKeychain, undefined, 2));
}
main().catch((err) => console.error(err));
// 2. Create User Key Shares Response
{
"uShare": {
"i": 1,
"t": 2,
"n": 3,
"y": "9a9c57f85a74eae15a57401b6467a3198743d56768d1273bf1d6cf5db147bea9",
"seed": "0350cb2e4d7409745d84b79ca5fa7f5ca6a18a743ad61ea1e5b405828e66882f",
"chaincode": "03c10c0e45befd366720ea8ac8fb0f4c313f99ef9b08a1e18e474d05a8e8c522"
},
"yShares": {
"2": {
"i": 2,
"j": 1,
"y": "9a9c57f85a74eae15a57401b6467a3198743d56768d1273bf1d6cf5db147bea9",
"u": "1b9932507b5d4b522dfd37f6341f58afe85dacb8e5208df18a59e6420e269b03",
"chaincode": "03c10c0e45befd366720ea8ac8fb0f4c313f99ef9b08a1e18e474d05a8e8c522"
},
"3": {
"i": 3,
"j": 1,
"y": "9a9c57f85a74eae15a57401b6467a3198743d56768d1273bf1d6cf5db147bea9",
"u": "da1db2fb700e1d17511176da6d3c42089c5da339bc4a1018a1547f5972afc201",
"chaincode": "03c10c0e45befd366720ea8ac8fb0f4c313f99ef9b08a1e18e474d05a8e8c522"
}
}
}
// 3. Create Backup Key Shares Response
{
"uShare": {
"i": 2,
"t": 2,
"n": 3,
"y": "c882eaf251d330d9871de9abc12723067951febb0f6c3c2aa4413460d1ecec82",
"seed": "90251953dd94f6d79136b5b85691e909592f8f01711607b80054e2b3c3399db6",
"chaincode": "280b166e7e9b8ebff854721906f438d3f54bd7f68838be4059008687c9a2948b"
},
"yShares": {
"1": {
"i": 1,
"j": 2,
"y": "c882eaf251d330d9871de9abc12723067951febb0f6c3c2aa4413460d1ecec82",
"u": "1f06d6d087a5b9e9d9dd681daf4d54a2c26ead32c4c5af1a6ed3087a394e330f",
"chaincode": "280b166e7e9b8ebff854721906f438d3f54bd7f68838be4059008687c9a2948b"
},
"3": {
"i": 3,
"j": 2,
"y": "c882eaf251d330d9871de9abc12723067951febb0f6c3c2aa4413460d1ecec82",
"u": "461c7ab528d4fe0048a351733fae0fd728ada5fd2d87a0dbc2f42f494f9cb801",
"chaincode": "280b166e7e9b8ebff854721906f438d3f54bd7f68838be4059008687c9a2948b"
}
}
}
// 4. Create BitGo Keychain Response
{
"id": "62fd86fcf9ce8e00083e1f6ea215960f",
"source": "bitgo",
"keyType": "tss",
"commonKeychain": "2b4a206676f076cdd3586716ee6f39c15218f3c4f4b6fdfa97f1e088891903c89c4b412a8a9eaec9461b64e25c4c024eb043aca4d3d41c4bd043981e7da019f8",
"isBitGo": true,
"keyShares": [
{
"from": "user",
"to": "bitgo",
"publicShare": "<PUBLIC_SHARE_HEX>",
"privateShare": "<PGP_ENCRYPTED_CONTENT>"
},
{
"from": "backup",
"to": "bitgo",
"publicShare": "<PUBLIC_SHARE_HEX>",
"privateShare": "<PGP_ENCRYPTED_CONTENT>"
},
{
"from": "bitgo",
"to": "user",
"publicShare": "<PUBLIC_SHARE_HEX>",
"privateShare": "<PGP_ENCRYPTED_CONTENT>"
},
{
"from": "bitgo",
"to": "backup",
"publicShare": "<PUBLIC_SHARE_HEX>",
"privateShare": "<PGP_ENCRYPTED_CONTENT>"
}
]
}
// 5. Create User Keychain Response
{
"id": "62fd86fc61fd1f00085664fa62f19d64",
"source": "user",
"keyType": "tss",
"commonKeychain": "2b4a206676f076cdd3586716ee6f39c15218f3c4f4b6fdfa97f1e088891903c89c4b412a8a9eaec9461b64e25c4c024eb043aca4d3d41c4bd043981e7da019f8",
"encryptedPrv": "<ENCRYPTED_PRV>"
}
// 6. Create Backup Keychain Response
{
"id": "62fd86fc33275f000855f457021b5976",
"source": "backup",
"keyType": "tss",
"commonKeychain": "2b4a206676f076cdd3586716ee6f39c15218f3c4f4b6fdfa97f1e088891903c89c4b412a8a9eaec9461b64e25c4c024eb043aca4d3d41c4bd043981e7da019f8"
}