PBKDF2Params.ts (6603B)
1 import * as asn1js from "asn1js"; 2 import * as pvutils from "pvutils"; 3 import { AlgorithmIdentifier, AlgorithmIdentifierJson, AlgorithmIdentifierSchema } from "./AlgorithmIdentifier"; 4 import { EMPTY_STRING } from "./constants"; 5 import { AsnError } from "./errors"; 6 import { PkiObject, PkiObjectParameters } from "./PkiObject"; 7 import * as Schema from "./Schema"; 8 9 const SALT = "salt"; 10 const ITERATION_COUNT = "iterationCount"; 11 const KEY_LENGTH = "keyLength"; 12 const PRF = "prf"; 13 const CLEAR_PROPS = [ 14 SALT, 15 ITERATION_COUNT, 16 KEY_LENGTH, 17 PRF 18 ]; 19 20 export interface IPBKDF2Params { 21 salt: any; 22 iterationCount: number; 23 keyLength?: number; 24 prf?: AlgorithmIdentifier; 25 } 26 27 export interface PBKDF2ParamsJson { 28 salt: any; 29 iterationCount: number; 30 keyLength?: number; 31 prf?: AlgorithmIdentifierJson; 32 } 33 34 export type PBKDF2ParamsParameters = PkiObjectParameters & Partial<IPBKDF2Params>; 35 36 /** 37 * Represents the PBKDF2Params structure described in [RFC2898](https://www.ietf.org/rfc/rfc2898.txt) 38 */ 39 export class PBKDF2Params extends PkiObject implements IPBKDF2Params { 40 41 public static override CLASS_NAME = "PBKDF2Params"; 42 43 public salt: any; 44 public iterationCount!: number; 45 public keyLength?: number; 46 public prf?: AlgorithmIdentifier; 47 48 /** 49 * Initializes a new instance of the {@link PBKDF2Params} class 50 * @param parameters Initialization parameters 51 */ 52 constructor(parameters: PBKDF2ParamsParameters = {}) { 53 super(); 54 55 this.salt = pvutils.getParametersValue(parameters, SALT, PBKDF2Params.defaultValues(SALT)); 56 this.iterationCount = pvutils.getParametersValue(parameters, ITERATION_COUNT, PBKDF2Params.defaultValues(ITERATION_COUNT)); 57 if (KEY_LENGTH in parameters) { 58 this.keyLength = pvutils.getParametersValue(parameters, KEY_LENGTH, PBKDF2Params.defaultValues(KEY_LENGTH)); 59 } 60 if (PRF in parameters) { 61 this.prf = pvutils.getParametersValue(parameters, PRF, PBKDF2Params.defaultValues(PRF)); 62 } 63 64 if (parameters.schema) { 65 this.fromSchema(parameters.schema); 66 } 67 } 68 69 /** 70 * Returns default values for all class members 71 * @param memberName String name for a class member 72 * @returns Default value 73 */ 74 public static override defaultValues(memberName: typeof SALT): any; 75 public static override defaultValues(memberName: typeof ITERATION_COUNT): number; 76 public static override defaultValues(memberName: typeof KEY_LENGTH): number; 77 public static override defaultValues(memberName: typeof PRF): AlgorithmIdentifier; 78 public static override defaultValues(memberName: string): any { 79 switch (memberName) { 80 case SALT: 81 return {}; 82 case ITERATION_COUNT: 83 return (-1); 84 case KEY_LENGTH: 85 return 0; 86 case PRF: 87 return new AlgorithmIdentifier({ 88 algorithmId: "1.3.14.3.2.26", // SHA-1 89 algorithmParams: new asn1js.Null() 90 }); 91 default: 92 return super.defaultValues(memberName); 93 } 94 } 95 96 /** 97 * @inheritdoc 98 * @asn ASN.1 schema 99 * ```asn 100 * PBKDF2-params ::= SEQUENCE { 101 * salt CHOICE { 102 * specified OCTET STRING, 103 * otherSource AlgorithmIdentifier }, 104 * iterationCount INTEGER (1..MAX), 105 * keyLength INTEGER (1..MAX) OPTIONAL, 106 * prf AlgorithmIdentifier 107 * DEFAULT { algorithm hMAC-SHA1, parameters NULL } } 108 *``` 109 */ 110 public static override schema(parameters: Schema.SchemaParameters<{ 111 saltPrimitive?: string; 112 saltConstructed?: AlgorithmIdentifierSchema; 113 iterationCount?: string; 114 keyLength?: string; 115 prf?: AlgorithmIdentifierSchema; 116 }> = {}): Schema.SchemaType { 117 const names = pvutils.getParametersValue<NonNullable<typeof parameters.names>>(parameters, "names", {}); 118 119 return (new asn1js.Sequence({ 120 name: (names.blockName || EMPTY_STRING), 121 value: [ 122 new asn1js.Choice({ 123 value: [ 124 new asn1js.OctetString({ name: (names.saltPrimitive || EMPTY_STRING) }), 125 AlgorithmIdentifier.schema(names.saltConstructed || {}) 126 ] 127 }), 128 new asn1js.Integer({ name: (names.iterationCount || EMPTY_STRING) }), 129 new asn1js.Integer({ 130 name: (names.keyLength || EMPTY_STRING), 131 optional: true 132 }), 133 AlgorithmIdentifier.schema(names.prf || { 134 names: { 135 optional: true 136 } 137 }) 138 ] 139 })); 140 } 141 142 public fromSchema(schema: Schema.SchemaType): void { 143 // Clear input data first 144 pvutils.clearProps(schema, CLEAR_PROPS); 145 146 // Check the schema is valid 147 const asn1 = asn1js.compareSchema(schema, 148 schema, 149 PBKDF2Params.schema({ 150 names: { 151 saltPrimitive: SALT, 152 saltConstructed: { 153 names: { 154 blockName: SALT 155 } 156 }, 157 iterationCount: ITERATION_COUNT, 158 keyLength: KEY_LENGTH, 159 prf: { 160 names: { 161 blockName: PRF, 162 optional: true 163 } 164 } 165 } 166 }) 167 ); 168 AsnError.assertSchema(asn1, this.className); 169 170 // Get internal properties from parsed schema 171 this.salt = asn1.result.salt; 172 this.iterationCount = asn1.result.iterationCount.valueBlock.valueDec; 173 if (KEY_LENGTH in asn1.result) 174 this.keyLength = asn1.result.keyLength.valueBlock.valueDec; 175 if (PRF in asn1.result) 176 this.prf = new AlgorithmIdentifier({ schema: asn1.result.prf }); 177 } 178 179 public toSchema(): asn1js.Sequence { 180 //#region Create array for output sequence 181 const outputArray = []; 182 183 outputArray.push(this.salt); 184 outputArray.push(new asn1js.Integer({ value: this.iterationCount })); 185 186 if (KEY_LENGTH in this) { 187 if (PBKDF2Params.defaultValues(KEY_LENGTH) !== this.keyLength) 188 outputArray.push(new asn1js.Integer({ value: this.keyLength })); 189 } 190 191 if (this.prf) { 192 if (PBKDF2Params.defaultValues(PRF).isEqual(this.prf) === false) 193 outputArray.push(this.prf.toSchema()); 194 } 195 //#endregion 196 197 //#region Construct and return new ASN.1 schema for this object 198 return (new asn1js.Sequence({ 199 value: outputArray 200 })); 201 //#endregion 202 } 203 204 public toJSON(): PBKDF2ParamsJson { 205 const res: PBKDF2ParamsJson = { 206 salt: this.salt.toJSON(), 207 iterationCount: this.iterationCount 208 }; 209 210 if (KEY_LENGTH in this) { 211 if (PBKDF2Params.defaultValues(KEY_LENGTH) !== this.keyLength) 212 res.keyLength = this.keyLength; 213 } 214 215 if (this.prf) { 216 if (PBKDF2Params.defaultValues(PRF).isEqual(this.prf) === false) 217 res.prf = this.prf.toJSON(); 218 } 219 220 return res; 221 } 222 223 }