Skip to content

Commit 086f3df

Browse files
fix: Improve Error Handling for VerificationMethodType in utils [DEV-4853] (#430)
* fix: Improve Error Handling for VerificationMethodType in utils * Do an early fast check on whether base64 or Hex * Update package-lock.json * Fix markdown issue * Bump conventional commits --------- Co-authored-by: Ankur Banerjee <[email protected]>
1 parent bbb4421 commit 086f3df

File tree

5 files changed

+125
-19
lines changed

5 files changed

+125
-19
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Changelog
22

3-
## <small>5.1.4-develop.1 (2025-03-21)</small>
3+
## 5.1.4-develop.1 (2025-03-21)
44

55
* fix: Deserialize JSON unescaped assertionMethod [DEV-4850] (#431) ([e9c4a9c](https://github.com/cheqd/sdk/commit/e9c4a9c)), closes [#431](https://github.com/cheqd/sdk/issues/431)
66
* chore: Tidy changelog ([41c6c71](https://github.com/cheqd/sdk/commit/41c6c71))

cjs/src/utils.ts

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,21 @@ export type TImportableEd25519Key = {
4343
type: 'Ed25519';
4444
};
4545

46+
export const TImportableEd25519Key = {
47+
isValid(key: any): key is TImportableEd25519Key {
48+
return (
49+
typeof key === 'object' &&
50+
key !== null &&
51+
typeof key.publicKeyHex === 'string' &&
52+
isHex(key.publicKeyHex) &&
53+
typeof key.privateKeyHex === 'string' &&
54+
isHex(key.privateKeyHex) &&
55+
typeof key.kid === 'string' &&
56+
key.type === 'Ed25519'
57+
);
58+
},
59+
};
60+
4661
const MULTICODEC_ED25519_HEADER = new Uint8Array([0xed, 0x01]);
4762

4863
export function isEqualKeyValuePair(kv1: IKeyValuePair[], kv2: IKeyValuePair[]): boolean {
@@ -57,12 +72,13 @@ export function createSignInputsFromImportableEd25519Key(
5772
key: TImportableEd25519Key,
5873
verificationMethod: VerificationMethod[]
5974
): ISignInputs {
60-
if (verificationMethod?.length === 0) throw new Error('No verification methods provided');
75+
if (!TImportableEd25519Key.isValid(key))
76+
throw new Error(`Key validation failed. Expected ${Object.values(TImportableEd25519Key).join(', ')}`);
6177

6278
const publicKey = fromString(key.publicKeyHex, 'hex');
6379

6480
for (const method of verificationMethod) {
65-
switch (method?.type) {
81+
switch (method.type) {
6682
case VerificationMethods.Ed255192020:
6783
const publicKeyMultibase = toMultibaseRaw(publicKey);
6884
if (method.publicKeyMultibase === publicKeyMultibase) {
@@ -92,9 +108,13 @@ export function createSignInputsFromImportableEd25519Key(
92108
};
93109
}
94110
}
111+
throw new Error(
112+
`Unsupported verification method type: ${method.type}. Expected one of: ${Object.values(VerificationMethods).join(', ')}`
113+
);
95114
}
96-
97-
throw new Error('No verification method type provided');
115+
throw new Error(
116+
`No verification method type provided. Expected one of: ${Object.values(VerificationMethods).join(', ')}`
117+
);
98118
}
99119

100120
export function createKeyPairRaw(seed?: string): KeyPair {
@@ -125,7 +145,12 @@ export function createVerificationKeys(
125145
methodSpecificId?: TMethodSpecificId,
126146
didUrl?: string
127147
): IVerificationKeys {
128-
publicKey = publicKey.length == 43 ? publicKey : toString(fromString(publicKey, 'hex'), 'base64');
148+
if (isHex(publicKey)) {
149+
publicKey = toString(fromString(publicKey, 'hex'), 'base64');
150+
} else if (!isBase64(publicKey)) {
151+
throw new Error('publicKey validation failed. PublicKey should be in base64 or hex format');
152+
}
153+
129154
switch (algo) {
130155
case MethodSpecificIdAlgo.Base58:
131156
methodSpecificId ||= bases['base58btc'].encode(base64ToBytes(publicKey));
@@ -369,3 +394,31 @@ export async function retry<T>(fn: () => Promise<T>, options?: BackoffOptions):
369394

370395
return result;
371396
}
397+
398+
function isBase64(str: string): boolean {
399+
// Quick pattern check to filter obvious non-base64 strings
400+
const base64Pattern = /^[A-Za-z0-9+/]*={0,3}$/;
401+
if (!base64Pattern.test(str)) {
402+
return false;
403+
}
404+
405+
try {
406+
return toString(fromString(str, 'base64'), 'base64') === str;
407+
} catch (e) {
408+
return false;
409+
}
410+
}
411+
412+
function isHex(str: string): boolean {
413+
// Quick pattern check to filter obvious non-hex strings
414+
const hexPattern = /^[0-9a-fA-F]*$/;
415+
if (!hexPattern.test(str)) {
416+
return false;
417+
}
418+
419+
try {
420+
return toString(fromString(str, 'hex'), 'hex') === str;
421+
} catch {
422+
return false;
423+
}
424+
}

esm/src/utils.ts

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,21 @@ export type TImportableEd25519Key = {
4343
type: 'Ed25519';
4444
};
4545

46+
export const TImportableEd25519Key = {
47+
isValid(key: any): key is TImportableEd25519Key {
48+
return (
49+
typeof key === 'object' &&
50+
key !== null &&
51+
typeof key.publicKeyHex === 'string' &&
52+
isHex(key.publicKeyHex) &&
53+
typeof key.privateKeyHex === 'string' &&
54+
isHex(key.privateKeyHex) &&
55+
typeof key.kid === 'string' &&
56+
key.type === 'Ed25519'
57+
);
58+
},
59+
};
60+
4661
const MULTICODEC_ED25519_HEADER = new Uint8Array([0xed, 0x01]);
4762

4863
export function isEqualKeyValuePair(kv1: IKeyValuePair[], kv2: IKeyValuePair[]): boolean {
@@ -57,12 +72,13 @@ export function createSignInputsFromImportableEd25519Key(
5772
key: TImportableEd25519Key,
5873
verificationMethod: VerificationMethod[]
5974
): ISignInputs {
60-
if (verificationMethod?.length === 0) throw new Error('No verification methods provided');
75+
if (!TImportableEd25519Key.isValid(key))
76+
throw new Error(`Key validation failed. Expected ${Object.values(TImportableEd25519Key).join(', ')}`);
6177

6278
const publicKey = fromString(key.publicKeyHex, 'hex');
6379

6480
for (const method of verificationMethod) {
65-
switch (method?.type) {
81+
switch (method.type) {
6682
case VerificationMethods.Ed255192020:
6783
const publicKeyMultibase = toMultibaseRaw(publicKey);
6884
if (method.publicKeyMultibase === publicKeyMultibase) {
@@ -92,9 +108,13 @@ export function createSignInputsFromImportableEd25519Key(
92108
};
93109
}
94110
}
111+
throw new Error(
112+
`Unsupported verification method type: ${method.type}. Expected one of: ${Object.values(VerificationMethods).join(', ')}`
113+
);
95114
}
96-
97-
throw new Error('No verification method type provided');
115+
throw new Error(
116+
`No verification method type provided. Expected one of: ${Object.values(VerificationMethods).join(', ')}`
117+
);
98118
}
99119

100120
export function createKeyPairRaw(seed?: string): KeyPair {
@@ -125,7 +145,12 @@ export function createVerificationKeys(
125145
methodSpecificId?: TMethodSpecificId,
126146
didUrl?: string
127147
): IVerificationKeys {
128-
publicKey = publicKey.length == 43 ? publicKey : toString(fromString(publicKey, 'hex'), 'base64');
148+
if (isHex(publicKey)) {
149+
publicKey = toString(fromString(publicKey, 'hex'), 'base64');
150+
} else if (!isBase64(publicKey)) {
151+
throw new Error('publicKey validation failed. PublicKey should be in base64 or hex format');
152+
}
153+
129154
switch (algo) {
130155
case MethodSpecificIdAlgo.Base58:
131156
methodSpecificId ||= bases['base58btc'].encode(base64ToBytes(publicKey));
@@ -363,3 +388,31 @@ export async function retry<T>(fn: () => Promise<T>, options?: BackoffOptions):
363388

364389
return result;
365390
}
391+
392+
function isBase64(str: string): boolean {
393+
// Quick pattern check to filter obvious non-base64 strings
394+
const base64Pattern = /^[A-Za-z0-9+/]*={0,3}$/;
395+
if (!base64Pattern.test(str)) {
396+
return false;
397+
}
398+
399+
try {
400+
return toString(fromString(str, 'base64'), 'base64') === str;
401+
} catch (e) {
402+
return false;
403+
}
404+
}
405+
406+
function isHex(str: string): boolean {
407+
// Quick pattern check to filter obvious non-hex strings
408+
const hexPattern = /^[0-9a-fA-F]*$/;
409+
if (!hexPattern.test(str)) {
410+
return false;
411+
}
412+
413+
try {
414+
return toString(fromString(str, 'hex'), 'hex') === str;
415+
} catch {
416+
return false;
417+
}
418+
}

package-lock.json

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@
113113
"@types/node-cjs": "npm:@types/node@^18.19.47",
114114
"@types/uuid": "^10.0.0",
115115
"@types/uuid-cjs": "npm:@types/uuid@^10.0.0",
116-
"conventional-changelog-conventionalcommits": "^7.0.2",
116+
"conventional-changelog-conventionalcommits": "^8.0.0",
117117
"cross-env": "^7.0.3",
118118
"husky": "^9.1.7",
119119
"jest": "^29.7.0",

0 commit comments

Comments
 (0)