Skip to content

Commit 1b9c105

Browse files
authored
Merge pull request #303 from mgtmdccix-oss/feat/wallet-key-version
feat(wallets): add keyVersion field to Wallet
2 parents e0c3979 + e4b69d4 commit 1b9c105

10 files changed

Lines changed: 36 additions & 1 deletion

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
-- Migration: add keyVersion field to Wallet
2+
--
3+
-- keyVersion tracks the key algorithm/derivation scheme version on a wallet.
4+
-- It is distinct from:
5+
-- encryptionVersion – the envelope/KMS format used to encrypt the secret material
6+
-- secretVersion – a monotonic counter incremented on every key rotation
7+
--
8+
-- Default value of 1 is applied to all existing rows so no data migration is needed.
9+
10+
ALTER TABLE "Wallet" ADD COLUMN "keyVersion" INTEGER NOT NULL DEFAULT 1;

prisma/schema.prisma

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,11 @@ model Wallet {
156156
/// Supports rotation by incrementing secret material while preserving history.
157157
secretVersion Int @default(1)
158158
159+
/// Key algorithm/derivation scheme version (e.g. 1 = Stellar Ed25519 via stellar-sdk).
160+
/// Increment when the key algorithm or derivation path changes so consumers can detect
161+
/// stale material and trigger re-encryption or re-issuance.
162+
keyVersion Int @default(1)
163+
159164
network WalletNetwork
160165
status WalletStatus @default(PROVISIONING)
161166

src/key-management/domain/key-types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ export interface GeneratedKeyPair {
3030
export interface EncryptedKeyMaterial {
3131
encryptedData: string;
3232
encryptionVersion: number;
33+
/** Key algorithm/derivation scheme version. Matches Wallet.keyVersion. */
34+
keyVersion: number;
3335
keyType: KeyType;
3436
publicKey: string;
3537
}

src/key-management/key-management.controller.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export class KeyManagementController {
4545
publicKey: result.publicKey,
4646
encryptedData: result.encryptedData,
4747
encryptionVersion: result.encryptionVersion,
48+
keyVersion: result.keyVersion,
4849
keyType: result.keyType,
4950
// Note: No private key is ever returned
5051
};

src/key-management/key-management.service.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ describe('KeyManagementService', () => {
7171
expect(result).toHaveProperty('publicKey');
7272
expect(result).toHaveProperty('keyType', KeyType.STELLAR_ED25519);
7373
expect(result).toHaveProperty('encryptionVersion');
74+
expect(result).toHaveProperty('keyVersion', 1);
7475

7576
// Critical: Should NOT contain plaintext private key
7677
expect(result).not.toHaveProperty('privateKey');

src/key-management/key-management.service.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ export class KeyManagementService {
119119
return {
120120
encryptedData,
121121
encryptionVersion: 1,
122+
keyVersion: 1,
122123
keyType: request.keyType,
123124
publicKey: keyPair.publicKey,
124125
};
@@ -269,7 +270,8 @@ export class KeyManagementService {
269270

270271
return {
271272
encryptedData: newEncryptedData,
272-
encryptionVersion: 2, // Increment version
273+
encryptionVersion: 2, // Increment encryption envelope version
274+
keyVersion: currentKeyVersion, // Key algorithm version is unchanged on re-encryption
273275
keyType,
274276
publicKey: '', // Would derive from private key in production
275277
};

src/wallets/domain/wallet.model.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ export interface Wallet {
3737
/** Supports rotation by incrementing secret material while preserving history. */
3838
secretVersion: number;
3939

40+
/**
41+
* Key algorithm/derivation scheme version (e.g. 1 = Stellar Ed25519 via stellar-sdk).
42+
* Increment when the key algorithm or derivation path changes so consumers can detect
43+
* stale material and trigger re-encryption or re-issuance.
44+
*/
45+
keyVersion: number;
46+
4047
/** Mainnet/testnet separation. */
4148
network: WalletNetwork;
4249

src/wallets/wallet-creation-orchestrator.service.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ export class WalletCreationOrchestrator {
465465
status: 'PROVISIONING', // Start in PROVISIONING (Issue #188)
466466
encryptionVersion: 1,
467467
secretVersion: 1,
468+
keyVersion: 1,
468469
},
469470
});
470471
phaseTimings['wallet-persist'] = Date.now() - t;
@@ -624,6 +625,7 @@ export class WalletCreationOrchestrator {
624625
encryptedSecret: prismaWallet.encryptedSecret,
625626
encryptionVersion: prismaWallet.encryptionVersion,
626627
secretVersion: prismaWallet.secretVersion,
628+
keyVersion: prismaWallet.keyVersion ?? 1,
627629
network: prismaWallet.network as WalletNetwork,
628630
status: prismaWallet.status as WalletStatus,
629631
statusReason: prismaWallet.statusReason,

src/wallets/wallets.service.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ describe('WalletsService', () => {
107107
status: 'ACTIVE',
108108
encryptionVersion: 1,
109109
secretVersion: 1,
110+
keyVersion: 1,
110111
statusReason: null,
111112
statusChangedAt: new Date(),
112113
rotatedFromId: null,
@@ -289,6 +290,7 @@ describe('WalletsService', () => {
289290
publicKey: 'old-public-key',
290291
encryptedSecret: 'old-encrypted-secret',
291292
secretVersion: 1,
293+
keyVersion: 1,
292294
};
293295

294296
const updatedWallet = {
@@ -297,6 +299,7 @@ describe('WalletsService', () => {
297299
publicKey: 'new-public-key',
298300
encryptedSecret: 'new-encrypted-secret',
299301
secretVersion: 2,
302+
keyVersion: 2,
300303
network: WalletNetwork.TESTNET,
301304
status: 'ACTIVE',
302305
encryptionVersion: 1,

src/wallets/wallets.service.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ export class WalletsService {
9191
status: 'ACTIVE',
9292
encryptionVersion: encryptedKeyMaterial.encryptionVersion,
9393
secretVersion: 1,
94+
keyVersion: 1,
9495
},
9596
});
9697

@@ -330,6 +331,7 @@ export class WalletsService {
330331
encryptedSecret: prismaWallet.encryptedSecret,
331332
encryptionVersion: prismaWallet.encryptionVersion,
332333
secretVersion: prismaWallet.secretVersion,
334+
keyVersion: prismaWallet.keyVersion ?? 1,
333335
network: prismaWallet.network as WalletNetwork,
334336
status: prismaWallet.status as WalletStatus,
335337
statusReason: prismaWallet.statusReason,

0 commit comments

Comments
 (0)