UI: Change maxlength key validation LWm2M security in PSK and x509 mode

This commit is contained in:
Vladyslav_Prykhodko 2021-08-09 11:48:08 +03:00 committed by Andrew Shvayka
parent 3bf48f0f5e
commit 972f1516d0
10 changed files with 36 additions and 75 deletions

View File

@ -28,27 +28,18 @@
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label>{{ 'device.lwm2m-security-config.client-publicKey-or-id' | translate }}</mat-label> <mat-label>{{ 'device.lwm2m-security-config.client-publicKey-or-id' | translate }}</mat-label>
<textarea matInput <textarea matInput
#clientPublicKeyOrId
[maxlength]="lenMaxClientPublicKeyOrId"
cdkTextareaAutosize cdkTextareaAutosize
cdkAutosizeMinRows="1" cdkAutosizeMinRows="1"
cols="1" cols="1"
formControlName="clientPublicKeyOrId" formControlName="clientPublicKeyOrId"
required> required>
</textarea> </textarea>
<mat-hint align="end">{{clientPublicKeyOrId.value?.length || 0}}/{{lenMaxClientPublicKeyOrId}}</mat-hint>
<mat-error *ngIf="serverFormGroup.get('clientPublicKeyOrId').hasError('required')"> <mat-error *ngIf="serverFormGroup.get('clientPublicKeyOrId').hasError('required')">
{{ 'device.lwm2m-security-config.client-publicKey-or-id-required' | translate }} {{ 'device.lwm2m-security-config.client-publicKey-or-id-required' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="serverFormGroup.get('clientPublicKeyOrId').hasError('pattern')"> <mat-error *ngIf="serverFormGroup.get('clientPublicKeyOrId').hasError('pattern')">
{{ 'device.lwm2m-security-config.client-publicKey-or-id-pattern' | translate }} {{ 'device.lwm2m-security-config.client-publicKey-or-id-pattern' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="(serverFormGroup.get('clientPublicKeyOrId').hasError('maxlength') ||
serverFormGroup.get('clientPublicKeyOrId').hasError('minlength'))">
{{ 'device.lwm2m-security-config.client-publicKey-or-id-length' | translate: {
count: lenMaxClientPublicKeyOrId
} }}
</mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label>{{ 'device.lwm2m-security-config.client-secret-key' | translate }}</mat-label> <mat-label>{{ 'device.lwm2m-security-config.client-secret-key' | translate }}</mat-label>
@ -61,17 +52,18 @@
formControlName="clientSecretKey" formControlName="clientSecretKey"
required> required>
</textarea> </textarea>
<mat-hint align="end">{{clientSecretKey.value?.length || 0}}/{{lengthClientSecretKey}}</mat-hint> <mat-hint [fxShow]="serverFormGroup.get('securityMode').value === securityConfigLwM2MType.PSK" align="end">
{{clientSecretKey.value?.length || 0}}/{{lengthClientSecretKey}}
</mat-hint>
<mat-error *ngIf="serverFormGroup.get('clientSecretKey').hasError('required')"> <mat-error *ngIf="serverFormGroup.get('clientSecretKey').hasError('required')">
{{ 'device.lwm2m-security-config.client-secret-key-required' | translate }} {{ 'device.lwm2m-security-config.client-secret-key-required' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="serverFormGroup.get('clientSecretKey').hasError('pattern')"> <mat-error *ngIf="serverFormGroup.get('clientSecretKey').hasError('pattern')">
{{ 'device.lwm2m-security-config.client-secret-key-pattern' | translate }} {{ 'device.lwm2m-security-config.client-secret-key-pattern' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="(serverFormGroup.get('clientSecretKey').hasError('maxlength') || <mat-error *ngIf="serverFormGroup.get('clientSecretKey').hasError('length')">
serverFormGroup.get('clientSecretKey').hasError('minlength'))">
{{ 'device.lwm2m-security-config.client-secret-key-length' | translate: { {{ 'device.lwm2m-security-config.client-secret-key-length' | translate: {
count: lengthClientSecretKey count: allowLengthKey.join(', ')
} }} } }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>

View File

@ -16,6 +16,7 @@
import { Component, forwardRef, OnDestroy } from '@angular/core'; import { Component, forwardRef, OnDestroy } from '@angular/core';
import { import {
AbstractControl,
ControlValueAccessor, ControlValueAccessor,
FormBuilder, FormBuilder,
FormGroup, FormGroup,
@ -29,8 +30,6 @@ import {
KEY_REGEXP_HEX_DEC, KEY_REGEXP_HEX_DEC,
LEN_MAX_PRIVATE_KEY, LEN_MAX_PRIVATE_KEY,
LEN_MAX_PSK, LEN_MAX_PSK,
LEN_MAX_PUBLIC_KEY_RPK,
LEN_MAX_PUBLIC_KEY_X509,
Lwm2mSecurityType, Lwm2mSecurityType,
Lwm2mSecurityTypeTranslationMap, Lwm2mSecurityTypeTranslationMap,
ServerSecurityConfig ServerSecurityConfig
@ -62,9 +61,8 @@ export class DeviceCredentialsLwm2mServerComponent implements OnDestroy, Control
securityConfigLwM2MType = Lwm2mSecurityType; securityConfigLwM2MType = Lwm2mSecurityType;
securityConfigLwM2MTypes = Object.values(Lwm2mSecurityType); securityConfigLwM2MTypes = Object.values(Lwm2mSecurityType);
lwm2mSecurityTypeTranslationMap = Lwm2mSecurityTypeTranslationMap; lwm2mSecurityTypeTranslationMap = Lwm2mSecurityTypeTranslationMap;
lenMinClientPublicKeyOrId = 0;
lenMaxClientPublicKeyOrId = LEN_MAX_PUBLIC_KEY_RPK;
lengthClientSecretKey = LEN_MAX_PRIVATE_KEY; lengthClientSecretKey = LEN_MAX_PRIVATE_KEY;
allowLengthKey = [32, 64, LEN_MAX_PSK];
private destroy$ = new Subject(); private destroy$ = new Subject();
private propagateChange = (v: any) => {}; private propagateChange = (v: any) => {};
@ -134,21 +132,15 @@ export class DeviceCredentialsLwm2mServerComponent implements OnDestroy, Control
this.serverFormGroup.get('clientSecretKey').disable(); this.serverFormGroup.get('clientSecretKey').disable();
break; break;
case Lwm2mSecurityType.PSK: case Lwm2mSecurityType.PSK:
this.lenMinClientPublicKeyOrId = 0;
this.lenMaxClientPublicKeyOrId = LEN_MAX_PUBLIC_KEY_RPK;
this.lengthClientSecretKey = LEN_MAX_PSK; this.lengthClientSecretKey = LEN_MAX_PSK;
this.setValidatorsSecurity(securityMode); this.setValidatorsSecurity(securityMode);
break; break;
case Lwm2mSecurityType.RPK: case Lwm2mSecurityType.RPK:
this.lenMinClientPublicKeyOrId = LEN_MAX_PUBLIC_KEY_RPK; this.lengthClientSecretKey = null;
this.lenMaxClientPublicKeyOrId = LEN_MAX_PUBLIC_KEY_RPK;
this.lengthClientSecretKey = LEN_MAX_PRIVATE_KEY;
this.setValidatorsSecurity(securityMode); this.setValidatorsSecurity(securityMode);
break; break;
case Lwm2mSecurityType.X509: case Lwm2mSecurityType.X509:
this.lenMinClientPublicKeyOrId = 0; this.lengthClientSecretKey = null;
this.lenMaxClientPublicKeyOrId = LEN_MAX_PUBLIC_KEY_X509;
this.lengthClientSecretKey = LEN_MAX_PRIVATE_KEY;
this.setValidatorsSecurity(securityMode); this.setValidatorsSecurity(securityMode);
break; break;
} }
@ -157,25 +149,28 @@ export class DeviceCredentialsLwm2mServerComponent implements OnDestroy, Control
} }
private setValidatorsSecurity = (securityMode: Lwm2mSecurityType): void => { private setValidatorsSecurity = (securityMode: Lwm2mSecurityType): void => {
const clientSecretKeyValidators = [Validators.required, Validators.pattern(KEY_REGEXP_HEX_DEC)];
const clientPublicKeyOrIdValidators = [Validators.required];
if (securityMode === Lwm2mSecurityType.PSK) { if (securityMode === Lwm2mSecurityType.PSK) {
this.serverFormGroup.get('clientPublicKeyOrId').setValidators([Validators.required]); clientSecretKeyValidators.push(this.maxLength(this.allowLengthKey));
} else { } else {
this.serverFormGroup.get('clientPublicKeyOrId').setValidators([ clientPublicKeyOrIdValidators.push(Validators.pattern(KEY_REGEXP_HEX_DEC));
Validators.required,
Validators.pattern(KEY_REGEXP_HEX_DEC),
Validators.minLength(this.lenMinClientPublicKeyOrId),
Validators.maxLength(this.lenMaxClientPublicKeyOrId)
]);
} }
this.serverFormGroup.get('clientSecretKey').setValidators([ this.serverFormGroup.get('clientPublicKeyOrId').setValidators(clientPublicKeyOrIdValidators);
Validators.required, this.serverFormGroup.get('clientSecretKey').setValidators(clientSecretKeyValidators);
Validators.pattern(KEY_REGEXP_HEX_DEC),
Validators.minLength(this.lengthClientSecretKey),
Validators.maxLength(this.lengthClientSecretKey)
]);
this.serverFormGroup.get('clientPublicKeyOrId').enable({emitEvent: false}); this.serverFormGroup.get('clientPublicKeyOrId').enable({emitEvent: false});
this.serverFormGroup.get('clientSecretKey').enable(); this.serverFormGroup.get('clientSecretKey').enable();
} }
private maxLength(keyLengths: number[]) {
return (control: AbstractControl): ValidationErrors | null => {
const value = control.value;
if (keyLengths.some(len => value.length === len)) {
return null;
}
return {length: true};
};
}
} }

View File

@ -53,7 +53,8 @@
formControlName="key" formControlName="key"
required> required>
</textarea> </textarea>
<mat-hint align="end">{{key.value?.length || 0}}/{{lenMaxKeyClient}}</mat-hint> <mat-hint *ngIf="lwm2mConfigFormGroup.get('client.securityConfigClientMode').value === securityConfigLwM2MType.PSK"
align="end">{{key.value?.length || 0}}/{{lenMaxKeyClient}}</mat-hint>
<mat-error *ngIf="lwm2mConfigFormGroup.get('client.key').hasError('required')"> <mat-error *ngIf="lwm2mConfigFormGroup.get('client.key').hasError('required')">
{{ 'device.lwm2m-security-config.client-key-required' | translate }} {{ 'device.lwm2m-security-config.client-key-required' | translate }}
</mat-error> </mat-error>

View File

@ -31,7 +31,6 @@ import {
getDefaultServerSecurityConfig, getDefaultServerSecurityConfig,
KEY_REGEXP_HEX_DEC, KEY_REGEXP_HEX_DEC,
LEN_MAX_PSK, LEN_MAX_PSK,
LEN_MAX_PUBLIC_KEY_RPK,
Lwm2mSecurityConfigModels, Lwm2mSecurityConfigModels,
Lwm2mSecurityType, Lwm2mSecurityType,
Lwm2mSecurityTypeTranslationMap Lwm2mSecurityTypeTranslationMap
@ -65,7 +64,7 @@ export class DeviceCredentialsLwm2mComponent implements ControlValueAccessor, Va
securityConfigLwM2MTypes = Object.keys(Lwm2mSecurityType); securityConfigLwM2MTypes = Object.keys(Lwm2mSecurityType);
credentialTypeLwM2MNamesMap = Lwm2mSecurityTypeTranslationMap; credentialTypeLwM2MNamesMap = Lwm2mSecurityTypeTranslationMap;
lenMaxKeyClient = LEN_MAX_PSK; lenMaxKeyClient = LEN_MAX_PSK;
allowLengthKey: number[]; allowLengthKey = [32, 64, LEN_MAX_PSK];
private destroy$ = new Subject(); private destroy$ = new Subject();
private propagateChange = (v: any) => {}; private propagateChange = (v: any) => {};
@ -137,13 +136,11 @@ export class DeviceCredentialsLwm2mComponent implements ControlValueAccessor, Va
break; break;
case Lwm2mSecurityType.PSK: case Lwm2mSecurityType.PSK:
this.lenMaxKeyClient = LEN_MAX_PSK; this.lenMaxKeyClient = LEN_MAX_PSK;
this.allowLengthKey = [32, 64, LEN_MAX_PSK];
this.setValidatorsPskRpk(mode); this.setValidatorsPskRpk(mode);
this.lwm2mConfigFormGroup.get('client.identity').enable({emitEvent: false}); this.lwm2mConfigFormGroup.get('client.identity').enable({emitEvent: false});
break; break;
case Lwm2mSecurityType.RPK: case Lwm2mSecurityType.RPK:
this.lenMaxKeyClient = LEN_MAX_PUBLIC_KEY_RPK; this.lenMaxKeyClient = null;
this.allowLengthKey = [LEN_MAX_PUBLIC_KEY_RPK];
this.setValidatorsPskRpk(mode); this.setValidatorsPskRpk(mode);
this.lwm2mConfigFormGroup.get('client.identity').disable({emitEvent: false}); this.lwm2mConfigFormGroup.get('client.identity').disable({emitEvent: false});
break; break;
@ -160,16 +157,14 @@ export class DeviceCredentialsLwm2mComponent implements ControlValueAccessor, Va
} }
private setValidatorsPskRpk = (mode: Lwm2mSecurityType): void => { private setValidatorsPskRpk = (mode: Lwm2mSecurityType): void => {
const keyValidators = [Validators.required, Validators.pattern(KEY_REGEXP_HEX_DEC)];
if (mode === Lwm2mSecurityType.PSK) { if (mode === Lwm2mSecurityType.PSK) {
this.lwm2mConfigFormGroup.get('client.identity').setValidators([Validators.required]); this.lwm2mConfigFormGroup.get('client.identity').setValidators([Validators.required]);
keyValidators.push(this.maxLength(this.allowLengthKey));
} else { } else {
this.lwm2mConfigFormGroup.get('client.identity').clearValidators(); this.lwm2mConfigFormGroup.get('client.identity').clearValidators();
} }
this.lwm2mConfigFormGroup.get('client.key').setValidators([ this.lwm2mConfigFormGroup.get('client.key').setValidators(keyValidators);
Validators.required,
Validators.pattern(KEY_REGEXP_HEX_DEC),
this.maxLength(this.allowLengthKey)
]);
this.lwm2mConfigFormGroup.get('client.key').enable({emitEvent: false}); this.lwm2mConfigFormGroup.get('client.key').enable({emitEvent: false});
this.lwm2mConfigFormGroup.get('client.cert').disable({emitEvent: false}); this.lwm2mConfigFormGroup.get('client.cert').disable({emitEvent: false});
} }

View File

@ -95,24 +95,17 @@
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label>{{ 'device-profile.lwm2m.server-public-key' | translate }}</mat-label> <mat-label>{{ 'device-profile.lwm2m.server-public-key' | translate }}</mat-label>
<textarea matInput <textarea matInput
#serverPublicKey
maxlength="{{maxLengthPublicKey}}"
cdkTextareaAutosize cdkTextareaAutosize
cdkAutosizeMinRows="1" cdkAutosizeMinRows="1"
cols="1" required cols="1" required
formControlName="serverPublicKey" formControlName="serverPublicKey"
></textarea> ></textarea>
<mat-hint align="end">{{serverPublicKey.value?.length || 0}}/{{maxLengthPublicKey}}</mat-hint>
<mat-error *ngIf="serverFormGroup.get('serverPublicKey').hasError('required')"> <mat-error *ngIf="serverFormGroup.get('serverPublicKey').hasError('required')">
{{ 'device-profile.lwm2m.server-public-key-required' | translate }} {{ 'device-profile.lwm2m.server-public-key-required' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="serverFormGroup.get('serverPublicKey').hasError('pattern')"> <mat-error *ngIf="serverFormGroup.get('serverPublicKey').hasError('pattern')">
{{ 'device-profile.lwm2m.server-public-key-pattern' | translate }} {{ 'device-profile.lwm2m.server-public-key-pattern' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="serverFormGroup.get('serverPublicKey').hasError('maxlength') ||
serverFormGroup.get('serverPublicKey').hasError('minlength')">
{{ 'device-profile.lwm2m.server-public-key-length' | translate: {count: maxLengthPublicKey } }}
</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
</section> </section>

View File

@ -29,8 +29,6 @@ import {
DEFAULT_PORT_BOOTSTRAP_NO_SEC, DEFAULT_PORT_BOOTSTRAP_NO_SEC,
DEFAULT_PORT_SERVER_NO_SEC, DEFAULT_PORT_SERVER_NO_SEC,
KEY_REGEXP_HEX_DEC, KEY_REGEXP_HEX_DEC,
LEN_MAX_PUBLIC_KEY_RPK,
LEN_MAX_PUBLIC_KEY_X509,
securityConfigMode, securityConfigMode,
securityConfigModeNames, securityConfigModeNames,
ServerSecurityConfig ServerSecurityConfig
@ -68,7 +66,6 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc
securityConfigLwM2MType = securityConfigMode; securityConfigLwM2MType = securityConfigMode;
securityConfigLwM2MTypes = Object.keys(securityConfigMode); securityConfigLwM2MTypes = Object.keys(securityConfigMode);
credentialTypeLwM2MNamesMap = securityConfigModeNames; credentialTypeLwM2MNamesMap = securityConfigModeNames;
maxLengthPublicKey = LEN_MAX_PUBLIC_KEY_RPK;
currentSecurityMode = null; currentSecurityMode = null;
@Input() @Input()
@ -147,12 +144,10 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc
this.clearValidators(); this.clearValidators();
break; break;
case securityConfigMode.RPK: case securityConfigMode.RPK:
this.maxLengthPublicKey = LEN_MAX_PUBLIC_KEY_RPK; this.setValidators();
this.setValidators(LEN_MAX_PUBLIC_KEY_RPK);
break; break;
case securityConfigMode.X509: case securityConfigMode.X509:
this.maxLengthPublicKey = LEN_MAX_PUBLIC_KEY_X509; this.setValidators();
this.setValidators(0);
break; break;
} }
this.serverFormGroup.get('serverPublicKey').updateValueAndValidity({emitEvent: false}); this.serverFormGroup.get('serverPublicKey').updateValueAndValidity({emitEvent: false});
@ -162,12 +157,10 @@ export class Lwm2mDeviceConfigServerComponent implements OnInit, ControlValueAcc
this.serverFormGroup.get('serverPublicKey').clearValidators(); this.serverFormGroup.get('serverPublicKey').clearValidators();
} }
private setValidators(minLengthKey: number): void { private setValidators(): void {
this.serverFormGroup.get('serverPublicKey').setValidators([ this.serverFormGroup.get('serverPublicKey').setValidators([
Validators.required, Validators.required,
Validators.pattern(KEY_REGEXP_HEX_DEC), Validators.pattern(KEY_REGEXP_HEX_DEC)
Validators.minLength(minLengthKey),
Validators.maxLength(this.maxLengthPublicKey)
]); ]);
} }

View File

@ -34,8 +34,6 @@ export const DEFAULT_MIN_PERIOD = 1;
export const DEFAULT_NOTIF_IF_DESIBLED = true; export const DEFAULT_NOTIF_IF_DESIBLED = true;
export const DEFAULT_BINDING = 'UQ'; export const DEFAULT_BINDING = 'UQ';
export const DEFAULT_BOOTSTRAP_SERVER_ACCOUNT_TIME_OUT = 0; export const DEFAULT_BOOTSTRAP_SERVER_ACCOUNT_TIME_OUT = 0;
export const LEN_MAX_PUBLIC_KEY_RPK = 182;
export const LEN_MAX_PUBLIC_KEY_X509 = 3000;
export const KEY_REGEXP_HEX_DEC = /^[-+]?[0-9A-Fa-f]+\.?[0-9A-Fa-f]*?$/; export const KEY_REGEXP_HEX_DEC = /^[-+]?[0-9A-Fa-f]+\.?[0-9A-Fa-f]*?$/;
export const INSTANCES_ID_VALUE_MIN = 0; export const INSTANCES_ID_VALUE_MIN = 0;
export const INSTANCES_ID_VALUE_MAX = 65535; export const INSTANCES_ID_VALUE_MAX = 65535;

View File

@ -16,8 +16,6 @@
export const LEN_MAX_PSK = 128; export const LEN_MAX_PSK = 128;
export const LEN_MAX_PRIVATE_KEY = 134; export const LEN_MAX_PRIVATE_KEY = 134;
export const LEN_MAX_PUBLIC_KEY_RPK = 182;
export const LEN_MAX_PUBLIC_KEY_X509 = 3000;
export const KEY_REGEXP_HEX_DEC = /^[-+]?[0-9A-Fa-f]+\.?[0-9A-Fa-f]*?$/; export const KEY_REGEXP_HEX_DEC = /^[-+]?[0-9A-Fa-f]+\.?[0-9A-Fa-f]*?$/;
export enum Lwm2mSecurityType { export enum Lwm2mSecurityType {

View File

@ -974,7 +974,6 @@
"client-publicKey-or-id": "Veřejný klíč nebo Id klienta", "client-publicKey-or-id": "Veřejný klíč nebo Id klienta",
"client-publicKey-or-id-required": "Veřejný klíč nebo Id klienta jsou povinné.", "client-publicKey-or-id-required": "Veřejný klíč nebo Id klienta jsou povinné.",
"client-publicKey-or-id-pattern": "Veřejný klíč klienta nebo Id musí být v hexadecimálním formátu.", "client-publicKey-or-id-pattern": "Veřejný klíč klienta nebo Id musí být v hexadecimálním formátu.",
"client-publicKey-or-id-length": "Veřejný klíč klienta nebo Id musí mít {{ count }} znaků.",
"client-secret-key": "Tajný klíč klienta", "client-secret-key": "Tajný klíč klienta",
"client-secret-key-required": "Tajný klíč klienta je povinný.", "client-secret-key-required": "Tajný klíč klienta je povinný.",
"client-secret-key-pattern": "Tajný klíč klienta musí být v hexadecimálním formátu.", "client-secret-key-pattern": "Tajný klíč klienta musí být v hexadecimálním formátu.",
@ -1301,7 +1300,6 @@
"server-public-key": "Veřejný klíč serveru", "server-public-key": "Veřejný klíč serveru",
"server-public-key-required": "Veřejný klíč serveru je povinný.", "server-public-key-required": "Veřejný klíč serveru je povinný.",
"server-public-key-pattern": "Veřejný klíč serveru musí být v hexadecimálním formátu.", "server-public-key-pattern": "Veřejný klíč serveru musí být v hexadecimálním formátu.",
"server-public-key-length": "Veřejný klíč serveru musí být {{ count }} znaků.",
"client-hold-off-time": "Čas odložení", "client-hold-off-time": "Čas odložení",
"client-hold-off-time-required": "Čas odloženíje povinný.", "client-hold-off-time-required": "Čas odloženíje povinný.",
"client-hold-off-time-pattern": "Čas odložení musí být kladné celé číslo.", "client-hold-off-time-pattern": "Čas odložení musí být kladné celé číslo.",

View File

@ -978,7 +978,6 @@
"client-publicKey-or-id": "Client Public Key or Id", "client-publicKey-or-id": "Client Public Key or Id",
"client-publicKey-or-id-required": "Client Public Key or Id is required.", "client-publicKey-or-id-required": "Client Public Key or Id is required.",
"client-publicKey-or-id-pattern": "Client Public Key or Id must be hexadecimal format.", "client-publicKey-or-id-pattern": "Client Public Key or Id must be hexadecimal format.",
"client-publicKey-or-id-length": "Client Public Key or Id must be {{ count }} characters.",
"client-secret-key": "Client Secret Key", "client-secret-key": "Client Secret Key",
"client-secret-key-required": "Client Secret Key is required.", "client-secret-key-required": "Client Secret Key is required.",
"client-secret-key-pattern": "Client Secret Key must be hexadecimal format.", "client-secret-key-pattern": "Client Secret Key must be hexadecimal format.",
@ -1315,7 +1314,6 @@
"server-public-key": "Server Public Key", "server-public-key": "Server Public Key",
"server-public-key-required": "Server Public Key is required.", "server-public-key-required": "Server Public Key is required.",
"server-public-key-pattern": "Server Public Key must be hex decimal format.", "server-public-key-pattern": "Server Public Key must be hex decimal format.",
"server-public-key-length": "Server Public Key must be {{ count }} characters.",
"client-hold-off-time": "Hold Off Time", "client-hold-off-time": "Hold Off Time",
"client-hold-off-time-required": "Hold Off Time is required.", "client-hold-off-time-required": "Hold Off Time is required.",
"client-hold-off-time-pattern": "Hold Off Time must be a positive integer.", "client-hold-off-time-pattern": "Hold Off Time must be a positive integer.",