1'use strict'; 2 3const { 4 ObjectDefineProperty, 5 Symbol, 6} = primordials; 7 8const { 9 KeyObjectHandle, 10 createNativeKeyObjectClass, 11 kKeyTypeSecret, 12 kKeyTypePublic, 13 kKeyTypePrivate, 14 kKeyFormatPEM, 15 kKeyFormatDER, 16 kKeyEncodingPKCS1, 17 kKeyEncodingPKCS8, 18 kKeyEncodingSPKI, 19 kKeyEncodingSEC1 20} = internalBinding('crypto'); 21const { 22 ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS, 23 ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE, 24 ERR_INVALID_ARG_TYPE, 25 ERR_INVALID_ARG_VALUE, 26 ERR_INVALID_OPT_VALUE, 27 ERR_OUT_OF_RANGE 28} = require('internal/errors').codes; 29const { kHandle } = require('internal/crypto/util'); 30 31const { isArrayBufferView } = require('internal/util/types'); 32 33const kKeyType = Symbol('kKeyType'); 34 35// Key input contexts. 36const kConsumePublic = 0; 37const kConsumePrivate = 1; 38const kCreatePublic = 2; 39const kCreatePrivate = 3; 40 41const encodingNames = []; 42for (const m of [[kKeyEncodingPKCS1, 'pkcs1'], [kKeyEncodingPKCS8, 'pkcs8'], 43 [kKeyEncodingSPKI, 'spki'], [kKeyEncodingSEC1, 'sec1']]) 44 encodingNames[m[0]] = m[1]; 45 46// Creating the KeyObject class is a little complicated due to inheritance 47// and that fact that KeyObjects should be transferrable between threads, 48// which requires the KeyObject base class to be implemented in C++. 49// The creation requires a callback to make sure that the NativeKeyObject 50// base class cannot exist without the other KeyObject implementations. 51const { 52 0: KeyObject, 53 1: SecretKeyObject, 54 2: PublicKeyObject, 55 3: PrivateKeyObject, 56} = createNativeKeyObjectClass((NativeKeyObject) => { 57 // Publicly visible KeyObject class. 58 class KeyObject extends NativeKeyObject { 59 constructor(type, handle) { 60 if (type !== 'secret' && type !== 'public' && type !== 'private') 61 throw new ERR_INVALID_ARG_VALUE('type', type); 62 if (typeof handle !== 'object' || !(handle instanceof KeyObjectHandle)) 63 throw new ERR_INVALID_ARG_TYPE('handle', 'object', handle); 64 65 super(handle); 66 67 this[kKeyType] = type; 68 69 ObjectDefineProperty(this, kHandle, { 70 value: handle, 71 enumerable: false, 72 configurable: false, 73 writable: false 74 }); 75 } 76 77 get type() { 78 return this[kKeyType]; 79 } 80 } 81 82 class SecretKeyObject extends KeyObject { 83 constructor(handle) { 84 super('secret', handle); 85 } 86 87 get symmetricKeySize() { 88 return this[kHandle].getSymmetricKeySize(); 89 } 90 91 export() { 92 return this[kHandle].export(); 93 } 94 } 95 96 const kAsymmetricKeyType = Symbol('kAsymmetricKeyType'); 97 98 class AsymmetricKeyObject extends KeyObject { 99 get asymmetricKeyType() { 100 return this[kAsymmetricKeyType] || 101 (this[kAsymmetricKeyType] = this[kHandle].getAsymmetricKeyType()); 102 } 103 } 104 105 class PublicKeyObject extends AsymmetricKeyObject { 106 constructor(handle) { 107 super('public', handle); 108 } 109 110 export(encoding) { 111 const { 112 format, 113 type 114 } = parsePublicKeyEncoding(encoding, this.asymmetricKeyType); 115 return this[kHandle].export(format, type); 116 } 117 } 118 119 class PrivateKeyObject extends AsymmetricKeyObject { 120 constructor(handle) { 121 super('private', handle); 122 } 123 124 export(encoding) { 125 const { 126 format, 127 type, 128 cipher, 129 passphrase 130 } = parsePrivateKeyEncoding(encoding, this.asymmetricKeyType); 131 return this[kHandle].export(format, type, cipher, passphrase); 132 } 133 } 134 135 return [KeyObject, SecretKeyObject, PublicKeyObject, PrivateKeyObject]; 136}); 137 138function parseKeyFormat(formatStr, defaultFormat, optionName) { 139 if (formatStr === undefined && defaultFormat !== undefined) 140 return defaultFormat; 141 else if (formatStr === 'pem') 142 return kKeyFormatPEM; 143 else if (formatStr === 'der') 144 return kKeyFormatDER; 145 throw new ERR_INVALID_OPT_VALUE(optionName, formatStr); 146} 147 148function parseKeyType(typeStr, required, keyType, isPublic, optionName) { 149 if (typeStr === undefined && !required) { 150 return undefined; 151 } else if (typeStr === 'pkcs1') { 152 if (keyType !== undefined && keyType !== 'rsa') { 153 throw new ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS( 154 typeStr, 'can only be used for RSA keys'); 155 } 156 return kKeyEncodingPKCS1; 157 } else if (typeStr === 'spki' && isPublic !== false) { 158 return kKeyEncodingSPKI; 159 } else if (typeStr === 'pkcs8' && isPublic !== true) { 160 return kKeyEncodingPKCS8; 161 } else if (typeStr === 'sec1' && isPublic !== true) { 162 if (keyType !== undefined && keyType !== 'ec') { 163 throw new ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS( 164 typeStr, 'can only be used for EC keys'); 165 } 166 return kKeyEncodingSEC1; 167 } 168 169 throw new ERR_INVALID_OPT_VALUE(optionName, typeStr); 170} 171 172function option(name, objName) { 173 return objName === undefined ? name : `${objName}.${name}`; 174} 175 176function parseKeyFormatAndType(enc, keyType, isPublic, objName) { 177 const { format: formatStr, type: typeStr } = enc; 178 179 const isInput = keyType === undefined; 180 const format = parseKeyFormat(formatStr, 181 isInput ? kKeyFormatPEM : undefined, 182 option('format', objName)); 183 184 const type = parseKeyType(typeStr, 185 !isInput || format === kKeyFormatDER, 186 keyType, 187 isPublic, 188 option('type', objName)); 189 190 return { format, type }; 191} 192 193function isStringOrBuffer(val) { 194 return typeof val === 'string' || isArrayBufferView(val); 195} 196 197function parseKeyEncoding(enc, keyType, isPublic, objName) { 198 if (enc === null || typeof enc !== 'object') 199 throw new ERR_INVALID_ARG_TYPE('options', 'object', enc); 200 201 const isInput = keyType === undefined; 202 203 const { 204 format, 205 type 206 } = parseKeyFormatAndType(enc, keyType, isPublic, objName); 207 208 let cipher, passphrase; 209 if (isPublic !== true) { 210 ({ cipher, passphrase } = enc); 211 212 if (!isInput) { 213 if (cipher != null) { 214 if (typeof cipher !== 'string') 215 throw new ERR_INVALID_OPT_VALUE(option('cipher', objName), cipher); 216 if (format === kKeyFormatDER && 217 (type === kKeyEncodingPKCS1 || 218 type === kKeyEncodingSEC1)) { 219 throw new ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS( 220 encodingNames[type], 'does not support encryption'); 221 } 222 } else if (passphrase !== undefined) { 223 throw new ERR_INVALID_OPT_VALUE(option('cipher', objName), cipher); 224 } 225 } 226 227 if ((isInput && passphrase !== undefined && 228 !isStringOrBuffer(passphrase)) || 229 (!isInput && cipher != null && !isStringOrBuffer(passphrase))) { 230 throw new ERR_INVALID_OPT_VALUE(option('passphrase', objName), 231 passphrase); 232 } 233 } 234 235 return { format, type, cipher, passphrase }; 236} 237 238// Parses the public key encoding based on an object. keyType must be undefined 239// when this is used to parse an input encoding and must be a valid key type if 240// used to parse an output encoding. 241function parsePublicKeyEncoding(enc, keyType, objName) { 242 return parseKeyEncoding(enc, keyType, keyType ? true : undefined, objName); 243} 244 245// Parses the private key encoding based on an object. keyType must be undefined 246// when this is used to parse an input encoding and must be a valid key type if 247// used to parse an output encoding. 248function parsePrivateKeyEncoding(enc, keyType, objName) { 249 return parseKeyEncoding(enc, keyType, false, objName); 250} 251 252function getKeyObjectHandle(key, ctx) { 253 if (ctx === kCreatePrivate) { 254 throw new ERR_INVALID_ARG_TYPE( 255 'key', 256 ['string', 'Buffer', 'TypedArray', 'DataView'], 257 key 258 ); 259 } 260 261 if (key.type !== 'private') { 262 if (ctx === kConsumePrivate || ctx === kCreatePublic) 263 throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(key.type, 'private'); 264 if (key.type !== 'public') { 265 throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(key.type, 266 'private or public'); 267 } 268 } 269 270 return key[kHandle]; 271} 272 273function prepareAsymmetricKey(key, ctx) { 274 if (isKeyObject(key)) { 275 // Best case: A key object, as simple as that. 276 return { data: getKeyObjectHandle(key, ctx) }; 277 } else if (typeof key === 'string' || isArrayBufferView(key)) { 278 // Expect PEM by default, mostly for backward compatibility. 279 return { format: kKeyFormatPEM, data: key }; 280 } else if (typeof key === 'object') { 281 const data = key.key; 282 // The 'key' property can be a KeyObject as well to allow specifying 283 // additional options such as padding along with the key. 284 if (isKeyObject(data)) 285 return { data: getKeyObjectHandle(data, ctx) }; 286 // Either PEM or DER using PKCS#1 or SPKI. 287 if (!isStringOrBuffer(data)) { 288 throw new ERR_INVALID_ARG_TYPE( 289 'key.key', 290 ['string', 'Buffer', 'TypedArray', 'DataView', 291 ...(ctx !== kCreatePrivate ? ['KeyObject'] : [])], 292 data); 293 } 294 295 const isPublic = 296 (ctx === kConsumePrivate || ctx === kCreatePrivate) ? false : undefined; 297 return { data, ...parseKeyEncoding(key, undefined, isPublic) }; 298 } 299 throw new ERR_INVALID_ARG_TYPE( 300 'key', 301 ['string', 'Buffer', 'TypedArray', 'DataView', 302 ...(ctx !== kCreatePrivate ? ['KeyObject'] : [])], 303 key 304 ); 305} 306 307function preparePrivateKey(key) { 308 return prepareAsymmetricKey(key, kConsumePrivate); 309} 310 311function preparePublicOrPrivateKey(key) { 312 return prepareAsymmetricKey(key, kConsumePublic); 313} 314 315function prepareSecretKey(key, bufferOnly = false) { 316 if (!isArrayBufferView(key) && (bufferOnly || typeof key !== 'string')) { 317 if (isKeyObject(key) && !bufferOnly) { 318 if (key.type !== 'secret') 319 throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(key.type, 'secret'); 320 return key[kHandle]; 321 } 322 throw new ERR_INVALID_ARG_TYPE( 323 'key', 324 ['Buffer', 'TypedArray', 'DataView', 325 ...(bufferOnly ? [] : ['string', 'KeyObject'])], 326 key); 327 } 328 return key; 329} 330 331function createSecretKey(key) { 332 key = prepareSecretKey(key, true); 333 if (key.byteLength === 0) 334 throw new ERR_OUT_OF_RANGE('key.byteLength', '> 0', key.byteLength); 335 const handle = new KeyObjectHandle(); 336 handle.init(kKeyTypeSecret, key); 337 return new SecretKeyObject(handle); 338} 339 340function createPublicKey(key) { 341 const { format, type, data } = prepareAsymmetricKey(key, kCreatePublic); 342 const handle = new KeyObjectHandle(); 343 handle.init(kKeyTypePublic, data, format, type); 344 return new PublicKeyObject(handle); 345} 346 347function createPrivateKey(key) { 348 const { format, type, data, passphrase } = 349 prepareAsymmetricKey(key, kCreatePrivate); 350 const handle = new KeyObjectHandle(); 351 handle.init(kKeyTypePrivate, data, format, type, passphrase); 352 return new PrivateKeyObject(handle); 353} 354 355function isKeyObject(key) { 356 return key instanceof KeyObject; 357} 358 359module.exports = { 360 // Public API. 361 createSecretKey, 362 createPublicKey, 363 createPrivateKey, 364 KeyObject, 365 366 // These are designed for internal use only and should not be exposed. 367 parsePublicKeyEncoding, 368 parsePrivateKeyEncoding, 369 preparePrivateKey, 370 preparePublicOrPrivateKey, 371 prepareSecretKey, 372 SecretKeyObject, 373 PublicKeyObject, 374 PrivateKeyObject, 375 isKeyObject 376}; 377