diff --git a/packages/core-types/src/SharedPasswordProtection.ts b/packages/core-types/src/SharedPasswordProtection.ts index 289968f5b..2e1dfcc23 100644 --- a/packages/core-types/src/SharedPasswordProtection.ts +++ b/packages/core-types/src/SharedPasswordProtection.ts @@ -6,7 +6,6 @@ export interface ISharedPasswordProtection extends ISerializable { passwordType: "pw" | `pin${number}`; salt: ICoreBuffer; passwordLocationIndicator?: number; - password?: string; } export class SharedPasswordProtection extends Serializable implements ISharedPasswordProtection { @@ -22,10 +21,6 @@ export class SharedPasswordProtection extends Serializable implements ISharedPas @serialize({ any: true }) public passwordLocationIndicator?: number; - @validate({ nullable: true }) - @serialize() - public password?: string; - public static from(value: ISharedPasswordProtection): SharedPasswordProtection { return this.fromAny(value); } @@ -34,8 +29,8 @@ export class SharedPasswordProtection extends Serializable implements ISharedPas if (value === undefined || value === "") return undefined; const splittedPasswordParts = value.split("&"); - if (![2, 3, 4].includes(splittedPasswordParts.length)) { - throw new CoreError("error.core-types.invalidTruncatedReference", "The password part of a TruncatedReference must consist of 2, 3 or 4 components."); + if (![2, 3].includes(splittedPasswordParts.length)) { + throw new CoreError("error.core-types.invalidTruncatedReference", "The password part of a TruncatedReference must consist of 2 or 3 components."); } const passwordType = splittedPasswordParts[0] as "pw" | `pin${number}`; @@ -43,9 +38,7 @@ export class SharedPasswordProtection extends Serializable implements ISharedPas const salt = this.parseSalt(splittedPasswordParts[1]); - const password = splittedPasswordParts.length > 3 && splittedPasswordParts[3] ? splittedPasswordParts[3] : undefined; - - return SharedPasswordProtection.from({ passwordType, salt, passwordLocationIndicator, password }); + return SharedPasswordProtection.from({ passwordType, salt, passwordLocationIndicator }); } private static parseSalt(value: string): CoreBuffer { @@ -60,8 +53,8 @@ export class SharedPasswordProtection extends Serializable implements ISharedPas public truncate(): string { const base = `${this.passwordType}&${this.salt.toBase64()}`; - if (this.passwordLocationIndicator === undefined && this.password === undefined) return base; + if (this.passwordLocationIndicator === undefined) return base; - return `${base}&${this.passwordLocationIndicator ?? ""}${this.password ? `&${this.password}` : ""}`; + return `${base}&${this.passwordLocationIndicator ?? ""}`; } } diff --git a/packages/core-types/test/references/Reference.test.ts b/packages/core-types/test/references/Reference.test.ts index d806209c1..bf55ac2e8 100644 --- a/packages/core-types/test/references/Reference.test.ts +++ b/packages/core-types/test/references/Reference.test.ts @@ -50,27 +50,7 @@ describe("Reference", () => { ); }); - test("toUrl with a cleartext password in the passwordProtection reference fields used", () => { - const reference = Reference.from({ - id: CoreId.from("ANID1234"), - backboneBaseUrl: "https://backbone.example.com", - key: CryptoSecretKey.from({ - secretKey: CoreBuffer.from("lerJyX8ydJDEXowq2PMMntRXXA27wgHJYA_BjnFx55Y"), - algorithm: CryptoEncryptionAlgorithm.XCHACHA20_POLY1305 - }), - passwordProtection: SharedPasswordProtection.from({ - passwordType: "pw", - salt: CoreBuffer.fromUtf8("a16byteslongsalt"), - password: "aPassword" - }) - }); - - expect(reference.toUrl()).toBe( - "https://backbone.example.com/r/ANID1234#M3xsZXJKeVg4eWRKREVYb3dxMlBNTW50UlhYQTI3d2dISllBX0JqbkZ4NTVZfHxwdyZZVEUyWW5sMFpYTnNiMjVuYzJGc2RBPT0mJmFQYXNzd29yZA" - ); - }); - - test("toUrl without a cleartext password but with a passwordLocationIndicator in the passwordProtection reference fields used", () => { + test("toUrl with a passwordLocationIndicator in the passwordProtection reference fields used", () => { const reference = Reference.from({ id: CoreId.from("ANID1234"), backboneBaseUrl: "https://backbone.example.com", diff --git a/packages/runtime-types/src/dtos/transport/EmptyTokenDTO.ts b/packages/runtime-types/src/dtos/transport/EmptyTokenDTO.ts index 162646e86..660f0dd35 100644 --- a/packages/runtime-types/src/dtos/transport/EmptyTokenDTO.ts +++ b/packages/runtime-types/src/dtos/transport/EmptyTokenDTO.ts @@ -1,9 +1,6 @@ -import { PasswordProtectionDTO } from "./PasswordProtectionDTO"; - export interface EmptyTokenDTO { id: string; expiresAt: string; - passwordProtection: PasswordProtectionDTO; reference: { truncated: string; url: string; diff --git a/packages/runtime/src/useCases/transport/devices/FillDeviceOnboardingTokenWithNewDevice.ts b/packages/runtime/src/useCases/transport/devices/FillDeviceOnboardingTokenWithNewDevice.ts index 4ef390dac..240a3ced6 100644 --- a/packages/runtime/src/useCases/transport/devices/FillDeviceOnboardingTokenWithNewDevice.ts +++ b/packages/runtime/src/useCases/transport/devices/FillDeviceOnboardingTokenWithNewDevice.ts @@ -30,9 +30,6 @@ export class FillDeviceOnboardingTokenWithNewDeviceUseCase extends UseCase> { const reference = TokenReference.from(request.reference); - const passwordProtection = reference.passwordProtection; - if (!passwordProtection?.password) throw RuntimeErrors.devices.referenceNotPointingToAnEmptyToken(); - const isEmptyToken = await this.tokenController.isEmptyToken(reference); if (!isEmptyToken) throw RuntimeErrors.devices.referenceNotPointingToAnEmptyToken(); @@ -45,8 +42,7 @@ export class FillDeviceOnboardingTokenWithNewDeviceUseCase extends UseCase { test("should create an empty token", async () => { const result = await noLoginRuntime.anonymousServices.tokens.createEmptyToken(); expect(result).toBeSuccessful(); - - const token = result.value; - expect(token.passwordProtection.password).toBeDefined(); - expect(token.passwordProtection.passwordIsPin).toBeUndefined(); - expect(token.passwordProtection.passwordLocationIndicator).toBeUndefined(); }); test("should get a proper error when trying to load an empty token", async () => { diff --git a/packages/transport/src/core/types/PasswordProtection.ts b/packages/transport/src/core/types/PasswordProtection.ts index d9c29224d..290409cbd 100644 --- a/packages/transport/src/core/types/PasswordProtection.ts +++ b/packages/transport/src/core/types/PasswordProtection.ts @@ -31,12 +31,11 @@ export class PasswordProtection extends Serializable implements IPasswordProtect return this.fromAny(value); } - public toSharedPasswordProtection(includeCleartextPassword?: boolean): SharedPasswordProtection { + public toSharedPasswordProtection(): SharedPasswordProtection { return SharedPasswordProtection.from({ passwordType: this.passwordType, salt: this.salt, - passwordLocationIndicator: this.passwordLocationIndicator, - password: includeCleartextPassword ? this.password : undefined + passwordLocationIndicator: this.passwordLocationIndicator }); } diff --git a/packages/transport/src/modules/tokens/AnonymousTokenController.ts b/packages/transport/src/modules/tokens/AnonymousTokenController.ts index fd439b6dd..c03bd8465 100644 --- a/packages/transport/src/modules/tokens/AnonymousTokenController.ts +++ b/packages/transport/src/modules/tokens/AnonymousTokenController.ts @@ -1,5 +1,5 @@ import { Serializable } from "@js-soft/ts-serval"; -import { CoreAddress, CoreDate, CoreId, Random, RandomCharacterRange } from "@nmshd/core-types"; +import { CoreAddress, CoreDate, CoreId } from "@nmshd/core-types"; import { CryptoCipher, CryptoSecretKey } from "@nmshd/crypto"; import { CoreCrypto, IConfig, ICorrelator, TransportCoreErrors } from "../../core"; import { PasswordProtection } from "../../core/types/PasswordProtection"; @@ -16,27 +16,21 @@ export class AnonymousTokenController { public async createEmptyToken(): Promise { const secretKey = await CoreCrypto.generateSecretKey(); - const password = await Random.string(16, RandomCharacterRange.Alphanumeric + RandomCharacterRange.SpecialCharacters); - - const salt = await CoreCrypto.random(16); - const passwordProtection = PasswordProtection.from({ password, passwordType: "pw", salt }); - const expiresAt = CoreDate.utc().add({ minutes: 2 }); - const hashedPassword = (await CoreCrypto.deriveHashOutOfPassword(password, salt)).toBase64(); - const response = (await this.client.createToken({ password: hashedPassword, expiresAt: expiresAt.toISOString() })).value; + const response = (await this.client.createToken({ expiresAt: expiresAt.toISOString() })).value; - return EmptyToken.from({ id: CoreId.from(response.id), secretKey: secretKey, expiresAt, passwordProtection }); + return EmptyToken.from({ id: CoreId.from(response.id), secretKey: secretKey, expiresAt }); } public async loadPeerTokenByReference(reference: TokenReference, password?: string): Promise { - if (reference.passwordProtection && !reference.passwordProtection.password && !password) throw TransportCoreErrors.general.noPasswordProvided(); + if (reference.passwordProtection && !password) throw TransportCoreErrors.general.noPasswordProvided(); const passwordProtection = reference.passwordProtection ? PasswordProtection.from({ salt: reference.passwordProtection.salt, passwordType: reference.passwordProtection.passwordType, - password: (password ?? reference.passwordProtection.password)!, + password: password!, passwordLocationIndicator: reference.passwordProtection.passwordLocationIndicator }) : undefined; diff --git a/packages/transport/src/modules/tokens/TokenController.ts b/packages/transport/src/modules/tokens/TokenController.ts index 042fa6963..fa273f2c4 100644 --- a/packages/transport/src/modules/tokens/TokenController.ts +++ b/packages/transport/src/modules/tokens/TokenController.ts @@ -1,6 +1,6 @@ import { ISerializable, Serializable } from "@js-soft/ts-serval"; import { log } from "@js-soft/ts-utils"; -import { CoreAddress, CoreDate, CoreId, Random, RandomCharacterRange } from "@nmshd/core-types"; +import { CoreAddress, CoreDate, CoreId } from "@nmshd/core-types"; import { CoreBuffer, CryptoCipher, CryptoSecretKey } from "@nmshd/crypto"; import { CoreCrypto, TransportCoreErrors } from "../../core"; import { DbCollectionName } from "../../core/DbCollectionName"; @@ -93,18 +93,12 @@ export class TokenController extends TransportController { const input = SendEmptyTokenParameters.from(parameters); const secretKey = await CoreCrypto.generateSecretKey(); - const password = await Random.string(16, RandomCharacterRange.Alphanumeric + RandomCharacterRange.SpecialCharacters); - const salt = await CoreCrypto.random(16); - const hashedPassword = (await CoreCrypto.deriveHashOutOfPassword(password, salt)).toBase64(); - const passwordProtection = PasswordProtection.from({ password, passwordType: "pw", salt }); - - const response = (await this.client.createEmptyToken({ password: hashedPassword, expiresAt: input.expiresAt.toISOString() })).value; + const response = (await this.client.createEmptyToken({ expiresAt: input.expiresAt.toISOString() })).value; const token = EmptyToken.from({ id: CoreId.from(response.id), secretKey: secretKey, - expiresAt: input.expiresAt, - passwordProtection + expiresAt: input.expiresAt }); if (!input.ephemeral) { @@ -135,12 +129,12 @@ export class TokenController extends TransportController { } public async loadPeerTokenByReference(reference: TokenReference, ephemeral: boolean, password?: string): Promise { - if (reference.passwordProtection && !reference.passwordProtection.password && !password) throw TransportCoreErrors.general.noPasswordProvided(); + if (reference.passwordProtection && !password) throw TransportCoreErrors.general.noPasswordProvided(); const passwordProtection = reference.passwordProtection ? PasswordProtection.from({ salt: reference.passwordProtection.salt, passwordType: reference.passwordProtection.passwordType, - password: (password ?? reference.passwordProtection.password)!, + password: password!, passwordLocationIndicator: reference.passwordProtection.passwordLocationIndicator }) : undefined; @@ -233,25 +227,12 @@ export class TokenController extends TransportController { const cipher = await CoreCrypto.encrypt(serializedTokenBuffer, input.secretKey); - const password = parameters.passwordProtection.password; - if (!password) throw TransportCoreErrors.general.noPasswordProvided(); - - const hashedPassword = (await CoreCrypto.deriveHashOutOfPassword(password, input.passwordProtection.salt)).toBase64(); - - const response = (await this.client.updateTokenContent({ id: parameters.id.toString(), newContent: cipher.toBase64(), password: hashedPassword })).value; - - const passwordProtection = PasswordProtection.from({ - password, - passwordType: parameters.passwordProtection.passwordType, - salt: parameters.passwordProtection.salt, - passwordLocationIndicator: parameters.passwordProtection.passwordLocationIndicator - }); + const response = (await this.client.updateTokenContent({ id: parameters.id.toString(), newContent: cipher.toBase64() })).value; const token = Token.from({ id: CoreId.from(response.id), secretKey: input.secretKey, isOwn: true, - passwordProtection, createdAt: CoreDate.from(response.createdAt), expiresAt: CoreDate.from(response.expiresAt), createdBy: this.parent.identity.address, @@ -263,10 +244,9 @@ export class TokenController extends TransportController { } public async isEmptyToken(reference: TokenReference): Promise { - if (!reference.passwordProtection?.password) throw TransportCoreErrors.general.noPasswordProvided(); + if (reference.passwordProtection) return false; - const hashedPassword = (await CoreCrypto.deriveHashOutOfPassword(reference.passwordProtection.password, reference.passwordProtection.salt)).toBase64(); - const response = (await this.client.getToken(reference.id.toString(), hashedPassword)).value; + const response = (await this.client.getToken(reference.id.toString())).value; return !response.content; } diff --git a/packages/transport/src/modules/tokens/local/EmptyToken.ts b/packages/transport/src/modules/tokens/local/EmptyToken.ts index 0a3616da5..2ce588f88 100644 --- a/packages/transport/src/modules/tokens/local/EmptyToken.ts +++ b/packages/transport/src/modules/tokens/local/EmptyToken.ts @@ -3,19 +3,16 @@ import { CoreDate, ICoreDate } from "@nmshd/core-types"; import { CryptoSecretKey, ICryptoSecretKey } from "@nmshd/crypto"; import { nameof } from "ts-simple-nameof"; import { CoreSynchronizable, ICoreSynchronizable } from "../../../core"; -import { IPasswordProtection, PasswordProtection } from "../../../core/types/PasswordProtection"; import { TokenReference } from "../transmission/TokenReference"; export interface IEmptyToken extends ICoreSynchronizable { secretKey: ICryptoSecretKey; expiresAt: ICoreDate; - passwordProtection: IPasswordProtection; } @type("EmptyToken") export class EmptyToken extends CoreSynchronizable implements IEmptyToken { public override readonly technicalProperties = ["@type", "@context", nameof((r) => r.secretKey), nameof((r) => r.expiresAt)]; - public override readonly userdataProperties = [nameof((r) => r.passwordProtection)]; @validate() @serialize() @@ -25,10 +22,6 @@ export class EmptyToken extends CoreSynchronizable implements IEmptyToken { @serialize() public expiresAt: CoreDate; - @validate() - @serialize() - public passwordProtection: PasswordProtection; - public static from(value: IEmptyToken): EmptyToken { return this.fromAny(value); } @@ -37,8 +30,7 @@ export class EmptyToken extends CoreSynchronizable implements IEmptyToken { return TokenReference.from({ id: this.id, backboneBaseUrl, - key: this.secretKey, - passwordProtection: this.passwordProtection.toSharedPasswordProtection(true) + key: this.secretKey }); } } diff --git a/packages/transport/src/modules/tokens/local/UpdateTokenContentParameters.ts b/packages/transport/src/modules/tokens/local/UpdateTokenContentParameters.ts index 8a4551501..62330ef03 100644 --- a/packages/transport/src/modules/tokens/local/UpdateTokenContentParameters.ts +++ b/packages/transport/src/modules/tokens/local/UpdateTokenContentParameters.ts @@ -1,12 +1,11 @@ import { ISerializable, Serializable, serialize, type, validate } from "@js-soft/ts-serval"; -import { CoreId, ICoreId, ISharedPasswordProtection, SharedPasswordProtection } from "@nmshd/core-types"; +import { CoreId, ICoreId } from "@nmshd/core-types"; import { CryptoSecretKey, ICryptoSecretKey } from "@nmshd/crypto"; export interface IUpdateTokenContentParameters extends ISerializable { id: ICoreId; secretKey: ICryptoSecretKey; content: ISerializable; - passwordProtection: ISharedPasswordProtection; } @type("UpdateTokenContentParameters") @@ -23,10 +22,6 @@ export class UpdateTokenContentParameters extends Serializable implements IUpdat @serialize() public content: Serializable; - @validate({ nullable: true }) - @serialize() - public passwordProtection: SharedPasswordProtection; - public static from(value: IUpdateTokenContentParameters): UpdateTokenContentParameters { return this.fromAny(value); } diff --git a/packages/transport/test/modules/tokens/AnonymousTokenController.test.ts b/packages/transport/test/modules/tokens/AnonymousTokenController.test.ts index c3712f04f..87a7be7d4 100644 --- a/packages/transport/test/modules/tokens/AnonymousTokenController.test.ts +++ b/packages/transport/test/modules/tokens/AnonymousTokenController.test.ts @@ -133,8 +133,7 @@ describe("AnonymousTokenController", function () { test("should create an empty token", async () => { const token = await anonymousTokenController.createEmptyToken(); - expect(token.passwordProtection.password).toBeDefined(); - expect(token.passwordProtection.passwordLocationIndicator).toBeUndefined(); + expect(token).toBeDefined(); }); test("should get a proper error when trying to load an empty token", async () => { diff --git a/packages/transport/test/modules/tokens/TokenController.test.ts b/packages/transport/test/modules/tokens/TokenController.test.ts index 5eb0ed425..7b93f0b83 100644 --- a/packages/transport/test/modules/tokens/TokenController.test.ts +++ b/packages/transport/test/modules/tokens/TokenController.test.ts @@ -389,7 +389,6 @@ describe("TokenController", function () { const updatedSentToken = await sender.tokens.updateTokenContent({ content: content, id: reference.id, - passwordProtection: reference.passwordProtection!, secretKey: reference.key });