Best JavaScript code snippet using cypress
webauthn-utils.js
Source:webauthn-utils.js
1const crypto = require('crypto');2const base64url = require('base64url');3const cbor = require('cbor');4const fs = require('fs');5const { Certificate } = require('@fidm/x509');6let userVerificationDefault = "preferred"; //userVerification - can be set to "required", "preferred", "discouraged". More in WebAuthn specification. Default set to "preferred"7/**8 * U2F Presence constant9 */10let U2F_USER_PRESENTED = 0x01;11/**12 * Takes signature, data and PEM public key and tries to verify signature13 * @param {Buffer} signature14 * @param {Buffer} data15 * @param {String} publicKey - PEM encoded public key16 * @return {Boolean}17 */18let verifySignature = (signature, data, publicKey) => {19 return crypto.createVerify('SHA256')20 .update(data)21 .verify(publicKey, signature);22}23/**24 * Returns base64url encoded buffer of the given length25 * @param {Number} len - length of the buffer26 * @return {String} - base64url random buffer27 */28let randomBase64URLBuffer = (len) => {29 len = len || 32;30 let buff = crypto.randomBytes(len);31 return base64url(buff);32}33/**34 * Generates makeCredentials request35 * @param {String} username - username36 * @param {String} displayName - user's personal display name37 * @param {String} id - user's base64url encoded id38 * @return {MakePublicKeyCredentialOptions} - server encoded make credentials request39 */40let generateServerMakeCredRequest = (userVerification, requireResidentKey, id, username, displayName) => {41 if( userVerification == null) userVerification = userVerificationDefault;42 return {43 attestation: 'direct',44 authenticatorSelection : {45 requireResidentKey: requireResidentKey,46 userVerification: userVerification47 },48 challenge: randomBase64URLBuffer(32),49 pubKeyCredParams: [50 {51 type: "public-key", alg: -7 // "ES256" IANA COSE Algorithms registry52 }53 ],54 rp: {55 //id: "fido.demo.gemalto.com",56 name: "Thales FIDO Demo"57 },58 timeout: 90000,59 user: {60 id: id,61 name: username,62 displayName: displayName63 }64 }65}66/**67 * Generates getAssertion request68 * @param {Array} authenticators - list of registered authenticators69 * @return {PublicKeyCredentialRequestOptions} - server encoded get assertion request70 */71let generateServerGetAssertion = (userVerification, authenticators) => {72 if( userVerification == null) userVerification = userVerificationDefault;73 let allowCredentials = [];74 for(let authr of authenticators) {75 allowCredentials.push({76 type: 'public-key',77 id: authr.credID78 //,transports: ['usb', 'nfc', 'ble']79 })80 }81 return {82 challenge: randomBase64URLBuffer(32)83 ,timeout: 6000084 ,allowCredentials: allowCredentials85 ,userVerification: userVerification86 }87}88/**89 * Returns SHA-256 digest of the given data.90 * @param {Buffer} data - data to hash91 * @return {Buffer} - the hash92 */93let hash = (data) => {94 return crypto.createHash('SHA256').update(data).digest();95}96/**97 * Takes COSE encoded public key and converts it to RAW PKCS ECDHA key98 * @param {Buffer} COSEPublicKey - COSE encoded public key99 * @return {Buffer} - RAW PKCS encoded public key100 */101let COSEECDHAtoPKCS = (COSEPublicKey) => {102 /* 103 +------+-------+-------+---------+----------------------------------+104 | name | key | label | type | description |105 | | type | | | |106 +------+-------+-------+---------+----------------------------------+107 | crv | 2 | -1 | int / | EC Curve identifier - Taken from |108 | | | | tstr | the COSE Curves registry |109 | | | | | |110 | x | 2 | -2 | bstr | X Coordinate |111 | | | | | |112 | y | 2 | -3 | bstr / | Y Coordinate |113 | | | | bool | |114 | | | | | |115 | d | 2 | -4 | bstr | Private key |116 +------+-------+-------+---------+----------------------------------+117 */118 let coseStruct = cbor.decodeAllSync(COSEPublicKey)[0];119 let tag = Buffer.from([0x04]);120 let x = coseStruct.get(-2);121 let y = coseStruct.get(-3);122 return Buffer.concat([tag, x, y])123}124/**125 * Convert binary certificate or public key to an OpenSSL-compatible PEM text format.126 * @param {Buffer} buffer - Cert or PubKey buffer127 * @return {String} - PEM128 */129let ASN1toPEM = (pkBuffer) => {130 if (!Buffer.isBuffer(pkBuffer))131 throw new Error("ASN1toPEM: pkBuffer must be Buffer.")132 let type;133 if (pkBuffer.length == 65 && pkBuffer[0] == 0x04) {134 /*135 If needed, we encode rawpublic key to ASN structure, adding metadata:136 SEQUENCE {137 SEQUENCE {138 OBJECTIDENTIFIER 1.2.840.10045.2.1 (ecPublicKey)139 OBJECTIDENTIFIER 1.2.840.10045.3.1.7 (P-256)140 }141 BITSTRING <raw public key>142 }143 Luckily, to do that, we just need to prefix it with constant 26 bytes (metadata is constant).144 */145 146 pkBuffer = Buffer.concat([147 new Buffer.from("3059301306072a8648ce3d020106082a8648ce3d030107034200", "hex"),148 pkBuffer149 ]);150 type = 'PUBLIC KEY';151 } else {152 type = 'CERTIFICATE';153 }154 let b64cert = pkBuffer.toString('base64');155 return formatPEM(b64cert, type);156 /*157 let PEMKey = '';158 for(let i = 0; i < Math.ceil(b64cert.length / 64); i++) {159 let start = 64 * i;160 PEMKey += b64cert.substr(start, 64) + '\n';161 }162 PEMKey = `-----BEGIN ${type}-----\n` + PEMKey + `-----END ${type}-----\n`;163 164 return PEMKey165 */166}167let formatPEM = (b64cert, type = "CERTIFICATE") => {168 let PEMKey = '';169 for(let i = 0; i < Math.ceil(b64cert.length / 64); i++) {170 let start = 64 * i;171 PEMKey += b64cert.substr(start, 64) + '\n';172 }173 PEMKey = `-----BEGIN ${type}-----\n` + PEMKey + `-----END ${type}-----\n`;174 return PEMKey;175}176/**177 * Parses authenticatorData buffer.178 * @param {Buffer} buffer - authenticatorData buffer179 * @return {Object} - parsed authenticatorData struct180 */181let parseMakeCredAuthData = (buffer) => {182 let rpIdHash = buffer.slice(0, 32); buffer = buffer.slice(32);183 let flagsBuf = buffer.slice(0, 1); buffer = buffer.slice(1);184 let flags = flagsBuf[0];185 let counterBuf = buffer.slice(0, 4); buffer = buffer.slice(4);186 let counter = counterBuf.readUInt32BE(0);187 let aaguid = buffer.slice(0, 16); buffer = buffer.slice(16);188 let credIDLenBuf = buffer.slice(0, 2); buffer = buffer.slice(2);189 let credIDLen = credIDLenBuf.readUInt16BE(0);190 let credID = buffer.slice(0, credIDLen); buffer = buffer.slice(credIDLen);191 let COSEPublicKey = buffer;192 return {rpIdHash, flagsBuf, flags, counter, counterBuf, aaguid, credID, COSEPublicKey}193}194let verifyAuthenticatorAttestationResponse = (webAuthnResponse) => {195 console.log("verifyAuthenticatorAttestationResponse");196 let attestationBuffer = base64url.toBuffer(webAuthnResponse.response.attestationObject);197 let attestationObject = cbor.decodeAllSync(attestationBuffer)[0];198 /*199 let details = " type: self";200 let attestationType = "self";201 if( ctapMakeCredResp.attStmt !== undefined && ctapMakeCredResp.attStmt.x5c !== undefined) {202 details += " type: AttCA with X5C length: " + ctapMakeCredResp.attStmt.x5c.length;203 attestationType = "AttCA";204 }205 if( ctapMakeCredResp.attStmt !== undefined && ctapMakeCredResp.attStmt.ecdaaKeyId !== undefined) {206 details += " type: ECDAA";207 attestationType = "ECDAA";208 }209 console.log("Attestation received: " + ctapMakeCredResp.fmt + " " + details);210 */211 console.log("Attestation received: " + attestationObject.fmt);212 let authDataBuffer = attestationObject.authData;213 attestationObject.authData = parseMakeCredAuthData(authDataBuffer);214 let clientDataHash = hash(base64url.toBuffer(webAuthnResponse.response.clientDataJSON))215 let response = {'verified': false, 'fmt': attestationObject.fmt, 'message': "" , 'log' : '', attestationObject: attestationObject };216 if(attestationObject.fmt === 'fido-u2f') {217 response = u2fAttestation(attestationObject, clientDataHash);218 /*219 if(!(attestationObject.authData.flags & U2F_USER_PRESENTED))220 throw new Error('User was NOT presented durring authentication!');221 let reservedByte = Buffer.from([0x00]);222 let publicKey = COSEECDHAtoPKCS(attestationObject.authData.COSEPublicKey)223 let signatureBase = Buffer.concat([reservedByte, attestationObject.authData.rpIdHash, clientDataHash, attestationObject.authData.credID, publicKey]);224 let PEMCertificate = ASN1toPEM(attestationObject.attStmt.x5c[0]);225 let signature = attestationObject.attStmt.sig;226 227 // console.log(PEMCertificate);228 // console.log(ctapMakeCredResp.attStmt.sig.length);229 // console.log(signature.toString("hex"));230 // console.log(hash(signature).toString("hex")); 231 // console.log(ctapMakeCredResp.attStmt.sig.length - 70);232 // let signature = Buffer.allocUnsafe(70);233 // ctapMakeCredResp.attStmt.sig.copy(signature,0, ctapMakeCredResp.attStmt.sig.length - 70)234 235 236 let maxTrailingZero = 0;237 for(let i = 0; i < signature.length ; i++) {238 if( signature.readInt8(i) === 0)239 maxTrailingZero = i + 1;240 else 241 break;242 }243 if( maxTrailingZero > 0) {244 console.log("WARNING - I modifiy the length");245 signature = signature.slice(maxTrailingZero);246 }247 console.log(signature.length);248 console.log(signature);249 250 // console.log(signature);251 // console.log(signatureBase);252 // console.log(base64url.decode(webAuthnResponse.response.clientDataJSON));253 // console.log(hash(signatureBase).toString("hex"));254 // Save cert255 let filename = saveCertificate(PEMCertificate);256 response.verified = verifySignature(signature, signatureBase, PEMCertificate);257 if( !response.verified ) {258 console.log("Invalid Signature");259 }260 261 // Try to detect device262 let cert = Certificate.fromPEM(PEMCertificate);263 let aaguid = '';264 // The certificate should contain the product ID in this OID265 for(let i = 0 ; i < cert.extensions.length ; i ++) {266 if( cert.extensions[i].oid === "1.3.6.1.4.1.45724.1.1.4") {267 aaguid = cert.extensions[i].value.slice(2);268 break;269 } 270 }271 // Try to get the first OID272 if( aaguid.length <= 0 ) {273 for(let i = 0 ; i < cert.extensions.length ; i ++) {274 // Transport OID "1.3.6.1.4.1.45724.2.1.1"275 if( cert.extensions[i].oid.indexOf("1.3.6.1.4.1.") == 0 && cert.extensions[i].oid.indexOf("1.3.6.1.4.1.45724.2.1.1") !== 0 ) {276 aaguid = cert.extensions[i].value;277 break;278 } 279 }280 }281 if(response.verified) {282 console.log("Attestation Verified");283 response.attestationObject.attStmt.sig = response.attestationObject.attStmt.sig.toString('base64');284 response.authrInfo = {285 fmt: 'fido-u2f',286 aaguid: convertAAGUID(aaguid),287 publicKey: base64url.encode(publicKey),288 counter: attestationObject.authData.counter,289 credID: base64url.encode(attestationObject.authData.credID),290 cert: filename291 }292 }293 else294 console.log("Attestation NOT Verified");295 */296 } 297 else if (attestationObject.fmt === 'packed' && attestationObject.attStmt.x5c !== undefined) { 298 response = packedAttestation(attestationObject, clientDataHash, authDataBuffer);299/*300 // https://www.w3.org/TR/webauthn/#packed-attestation301 302 let authrDataStruct = parseMakeCredAuthData(ctapMakeCredResp.authData);303 let clientDataHash = hash(base64url.toBuffer(webAuthnResponse.response.clientDataJSON))304 let publicKey = COSEECDHAtoPKCS(authrDataStruct.COSEPublicKey)305 // Step 1 - Verify that sig is a valid signature306 let signatureBase = Buffer.concat([ctapMakeCredResp.authData, clientDataHash]);307 let PEMCertificate = ASN1toPEM(ctapMakeCredResp.attStmt.x5c[0]);308 let signature = ctapMakeCredResp.attStmt.sig;309 response.verified = verifySignature(signature, signatureBase, PEMCertificate);310 if( !response.verified ) response.message = "Invalid Signature";311 // Save cert312 let filename = "cert/" + crypto.createHash('md5').update(authrDataStruct.credID).digest("hex") + ".crt";313 fs.writeFileSync( "static/" + filename, PEMCertificate);314 // Step 2 - ???315 // Step 3 - if OID 1.3.6.1.4.1.45724.1.1.4 (id-fido-gen-ce-aaguid) 316 // verify that the value of this extension matches the aaguid in authenticatorData.317 318 // DEBUG: PEMCertificate = fs.readFileSync("cert/card.crt");319 //PEMCertificate = fs.readFileSync("cert/ctap_fpvcFk2ZxU8RsZDihqbO23ARBn4sKK_Y0akjna8tAhIt2vfCWgw29_F5KrWFaAb4-PEjYjW_lqPMgccDmwrEVu6CaEQsEaektvPLiig.crt");320 let checkAAGUID = true;321 if( checkAAGUID )322 {323 let cert = Certificate.fromPEM(PEMCertificate);324 for(let i = 0 ; i < cert.extensions.length ; i ++) {325 if( cert.extensions[i].oid === "1.3.6.1.4.1.45724.1.1.4") {326 let certValue = Buffer.from(cert.extensions[i].value, 2);327 response.verified = certValue.equals(authrDataStruct.aaguid);328 if( !response.verified ) { 329 response.message = "Invalid AAGUID";330 response.log = "Invalid AAGUID [" + JSON.stringify(authrDataStruct.aaguid) + "] [" + JSON.stringify(certValue) + "]";331 console.log(response.log);332 }333 break;334 }335 }336 }337 if(response.verified) {338 response.authrInfo = {339 fmt: 'packed',340 aaguid: authrDataStruct.aaguid.toString('hex'),341 publicKey: base64url.encode(publicKey),342 counter: authrDataStruct.counter,343 credID: base64url.encode(authrDataStruct.credID),344 cert: filename345 }346 }347 */348 }349 /*350 else if (ctapMakeCredResp.fmt === "android-key")351 {352 let authrDataStruct = parseMakeCredAuthData(ctapMakeCredResp.authData);353 let clientDataHash = hash(base64url.toBuffer(webAuthnResponse.response.clientDataJSON))354 let publicKey = COSEECDHAtoPKCS(authrDataStruct.COSEPublicKey) 355 // Step 1 - Verify that sig is a valid signature356 let signatureBase = Buffer.concat([ctapMakeCredResp.authData, clientDataHash]);357 let PEMCertificate = ASN1toPEM(ctapMakeCredResp.attStmt.x5c[0]);358 let signature = ctapMakeCredResp.attStmt.sig;359 response.verified = verifySignature(signature, signatureBase, PEMCertificate);360 if( !response.verified ) response.message = "Invalid Signature";361 // Save cert362 let filename = "cert/" + crypto.createHash('md5').update(authrDataStruct.credID).digest("hex") + ".crt";363 fs.writeFileSync( "static/" + filename, PEMCertificate);364 if(response.verified) {365 response.authrInfo = {366 fmt: ctapMakeCredResp.fmt,367 aaguid: authrDataStruct.aaguid.toString('hex'),368 publicKey: base64url.encode(publicKey),369 counter: authrDataStruct.counter,370 credID: base64url.encode(authrDataStruct.credID),371 cert: filename372 }373 }374 }375 */376 else if (ctapMakeCredResp.fmt === "android-safetynet")377 {378 response = androidSafetynetAttestation(attestationObject, clientDataHash);379 /*380 let authrDataStruct = parseMakeCredAuthData(ctapMakeCredResp.authData);381 let clientDataHash = hash(base64url.toBuffer(webAuthnResponse.response.clientDataJSON))382 let publicKey = COSEECDHAtoPKCS(authrDataStruct.COSEPublicKey);383 let ver = ctapMakeCredResp.attStmt.ver;384 let response = ctapMakeCredResp.attStmt.response.toString("utf-8");385 386 console.log(ver);387 let jwsArray = response.split(".");388 if( jwsArray.length )389 let nonce = Buffer.concat([ctapMakeCredResp.authData, clientDataHash]).toString('base64');390 */391 /*392 // Step 1 - Verify that sig is a valid signature393 let signatureBase = Buffer.concat([ctapMakeCredResp.authData, clientDataHash]);394 let PEMCertificate = ASN1toPEM(ctapMakeCredResp.attStmt.x5c[0]);395 let signature = ctapMakeCredResp.attStmt.sig;396 response.verified = verifySignature(signature, signatureBase, PEMCertificate);397 if( !response.verified ) response.message = "Invalid Signature";398 // Save cert399 let filename = "cert/" + crypto.createHash('md5').update(authrDataStruct.credID).digest("hex") + ".crt";400 fs.writeFileSync( "static/" + filename, PEMCertificate);401 if(response.verified) {402 response.authrInfo = {403 fmt: ctapMakeCredResp.fmt,404 aaguid: authrDataStruct.aaguid.toString('hex'),405 publicKey: base64url.encode(publicKey),406 counter: authrDataStruct.counter,407 credID: base64url.encode(authrDataStruct.credID),408 cert: filename409 }410 }411 */412 }413 else414 { 415 response.authrInfo = {416 fmt: attestationObject.fmt, 417 }418 response.message = 'Unsupported attestation [' + attestationObject.fmt + "]"; 419 }420 421 // Format attestationObject for logs422 if( response.verified == true)423 {424 attestationLog = {attStmt: {x5c: []}, authData: {credentialData: {}}};425 // Prepare "attStmt"426 attestationLog.attStmt.sig = attestationObject.attStmt.sig.toString('base64');427 attestationObject.attStmt.x5c.forEach(x5c => attestationLog.attStmt.x5c.push(x5c.toString('base64')));428 // Prepare "credentialData"429 attestationLog.authData.credentialData.aaguid = attestationObject.authData.aaguid.toString('base64');430 attestationLog.authData.credentialData.credentialId = attestationObject.authData.credID.toString('base64');431 attestationLog.authData.credentialData.rpIdHash = attestationObject.authData.rpIdHash.toString('base64');432 attestationLog.authData.credentialData.signatureCounter = attestationObject.authData.signatureCounter;433 // Prepare "fmt"434 attestationLog.fmt = attestationObject.fmt;435 436 // Assign the log object437 response.attestationObject = attestationLog;438 /*439 "credentialData": {440 "aaguid": "AAAAAAAAAAAAAAAAAAAAAA==",441 "credentialId": "aRaHCZ8z63X946K6WFwE5+Naqcc0P3mw46/23s4dHMc5xmjuAmVav4wiAl1LjHlimW2ABKYFl4govGNffdNrktNiU6xr8qNSCh+mqP5MJI6DRq4Z65o5QkABkG1ElcZXsO83ACCFL9JAeRj9X9ufqG4qC31v91Sjk9v2gunfnrcda6fRtNBA9yn9/ONoxbzuXg5LeGV7MRM6NNUwrCREYQ==",442 "publicKey": {443 "1": 2,444 "3": -7,445 "-1": 1,446 "-2": "ia56X+doqE2bb+rmkT5gM6jQZe2zfb6xAHR55uM6Lyc=",447 "-3": "fWnMFdHhu9An/oguPJLHTarRHdFCjGTEiO9lOW8hJmE="448 }449 },450 "flags": {451 "AT": true,452 "ED": false,453 "UP": true,454 "UV": false,455 "value": 65456 },457 "rpIdHash": "xGzvgq0bVGR3WR0Aiwh1nsPm0uy085R0v+ppaZJdA7c=",458 "signatureCounter": 0 },459 "fmt": "fido-u2f"460 */461 }462 return response463}464/**465 * save cert466 * @param {Buffer} data - certificate Data467 * @return {String} - filename468 */469let saveCertificate = (data) => {470 let filename = "cert/" + crypto.createHash('md5').update(data).digest("hex") + ".crt";471 fs.writeFileSync( "static/" + filename, data);472 return filename;473}474function u2fAttestation(attestationObject, clientDataHash) {475 if(!(attestationObject.authData.flags & U2F_USER_PRESENTED)) { 476 console.log("User presented FLAG");477 return {verified: false, message: "User presented FLAG"};478 }479 let reservedByte = Buffer.from([0x00]);480 let publicKey = COSEECDHAtoPKCS(attestationObject.authData.COSEPublicKey)481 let signatureBase = Buffer.concat([reservedByte, attestationObject.authData.rpIdHash, clientDataHash, attestationObject.authData.credID, publicKey]);482 let PEMCertificate = ASN1toPEM(attestationObject.attStmt.x5c[0]);483 let signature = attestationObject.attStmt.sig;484 let maxTrailingZero = 0;485 for(let i = 0; i < signature.length ; i++) {486 if( signature.readInt8(i) === 0)487 maxTrailingZero = i + 1;488 else 489 break;490 }491 if( maxTrailingZero > 0) {492 console.log("WARNING - I modify the length");493 signature = signature.slice(maxTrailingZero);494 }495 // Save cert496 let filename = saveCertificate(PEMCertificate);497 if( !verifySignature(signature, signatureBase, PEMCertificate) ) {498 console.log("Invalid Signature");499 return {verified: false, message: "Invalid Signature"};500 }501 // Try to detect device502 let cert = Certificate.fromPEM(PEMCertificate);503 let aaguid = '';504 // The certificate should contain the product ID in this OID505 for(let i = 0 ; i < cert.extensions.length ; i ++) {506 if( cert.extensions[i].oid === "1.3.6.1.4.1.45724.1.1.4") {507 aaguid = cert.extensions[i].value.slice(2);508 break;509 } 510 }511 // Try to get the first OID512 if( aaguid.length <= 0 ) {513 for(let i = 0 ; i < cert.extensions.length ; i ++) {514 // Transport OID "1.3.6.1.4.1.45724.2.1.1"515 if( cert.extensions[i].oid.indexOf("1.3.6.1.4.1.") == 0 && cert.extensions[i].oid.indexOf("1.3.6.1.4.1.45724.2.1.1") !== 0 ) {516 aaguid = cert.extensions[i].value;517 break;518 } 519 }520 }521 console.log("U2F Attestation Verified");522 let authrInfo = {523 fmt: 'fido-u2f',524 aaguid: convertAAGUID(aaguid),525 publicKey: base64url.encode(publicKey),526 counter: attestationObject.authData.counter,527 credID: base64url.encode(attestationObject.authData.credID),528 cert: filename };529 return {verified: true, authrInfo: authrInfo, message: "OK", attestationObject: attestationObject};530}531/**532 * Validate packed attestation533 * @param {Buffer} attestationObject - ctapMakeCred buffer534 * @param {String} clientDataHash - hash535 * @param {String} authDataBuffer - Original buffer536 * @return {Object} - parsed authenticatorData struct537 */538let packedAttestation = (attestationObject, clientDataHash, authDataBuffer) => {539 // https://www.w3.org/TR/webauthn/#packed-attestation540 541 let publicKey = COSEECDHAtoPKCS(attestationObject.authData.COSEPublicKey);542 // Step 1 - Verify that sig is a valid signature543 let signatureBase = Buffer.concat([authDataBuffer, clientDataHash]);544 let PEMCertificate = ASN1toPEM(attestationObject.attStmt.x5c[0]);545 let signature = attestationObject.attStmt.sig;546 if( !verifySignature(signature, signatureBase, PEMCertificate) ) 547 {548 console.log("invalid Signature");549 return {verified: false, message: "invalid Signature"};550 }551 // Save cert552 let filename = saveCertificate(PEMCertificate);553 // Step 2 - ???554 // Step 3 - if OID 1.3.6.1.4.1.45724.1.1.4 (id-fido-gen-ce-aaguid) 555 // verify that the value of this extension matches the aaguid in authenticatorData.556 557 // DEBUG: PEMCertificate = fs.readFileSync("cert/card.crt");558 //PEMCertificate = fs.readFileSync("cert/ctap_fpvcFk2ZxU8RsZDihqbO23ARBn4sKK_Y0akjna8tAhIt2vfCWgw29_F5KrWFaAb4-PEjYjW_lqPMgccDmwrEVu6CaEQsEaektvPLiig.crt");559 let checkAAGUID = true;560 if( checkAAGUID )561 {562 let cert = Certificate.fromPEM(PEMCertificate);563 for(let i = 0 ; i < cert.extensions.length ; i ++) {564 if( cert.extensions[i].oid === "1.3.6.1.4.1.45724.1.1.4") {565 let certValue = cert.extensions[i].value.slice(2);566 if( !certValue.equals(attestationObject.authData.aaguid)) {567 let log = "Invalid AAGUID [" + JSON.stringify(attestationObject.authData.aaguid) + "] [" + JSON.stringify(certValue) + "]";568 console.log(log);569 return {verified: false, message: "invalid AAGUID", log: log};570 }571 break;572 }573 }574 }575 let authrInfo = {576 fmt: attestationObject.fmt,577 aaguid: convertAAGUID(attestationObject.authData.aaguid),578 publicKey: base64url.encode(publicKey),579 counter: attestationObject.authData.counter,580 credID: base64url.encode(attestationObject.authData.credID),581 cert: filename582 }; 583 584 console.log("Attestation is valid");585 return {verified: true, authrInfo: authrInfo, message: "OK", attestationObject: attestationObject};586}587let convertAAGUID = (byteArray) => {588 if( byteArray.length == 18 )589 byteArray = byteArray.slice(2);590 let aaguid = byteArray.toString('hex').toLowerCase();591 aaguid = aaguid.slice(0,8) + '-' + aaguid.slice(8);592 aaguid = aaguid.slice(0,13) + '-' + aaguid.slice(13);593 aaguid = aaguid.slice(0,18) + '-' + aaguid.slice(18);594 aaguid = aaguid.slice(0,23) + '-' + aaguid.slice(23);595 return aaguid;596}597/**598 * Validate android-safetynet attestation599 * @param {Buffer} attestationObject - ctapMakeCred buffer600 * @param {String} clientDataHash - hash601 * @return {Object} - parsed authenticatorData struct602 */603let androidSafetynetAttestation = (attestationObject, clientDataHash) => {604 //let authrDataStruct = parseMakeCredAuthData(attestationObject.authData);605 let publicKey = COSEECDHAtoPKCS(attestationObject.authData.COSEPublicKey);606 let ver = attestationObject.attStmt.ver;607 let response = attestationObject.attStmt.response.toString("utf-8");608 609 let jwsArray = response.split(".");610 if( jwsArray.length <= 2 )611 return {verified: false, message: "invalid JWS attestation"}612 // STEP 1 - Get certificate613 let attestation = JSON.parse(base64url.decode(jwsArray[0]));614 let PEMCertificate = formatPEM(attestation.x5c[0]);615 // Save cert 616 let filename = saveCertificate(PEMCertificate);617 // Compare hostname618 let cert = Certificate.fromPEM(PEMCertificate);619 let hostnameIsValid = false;620 for(let i = 0 ; i < cert.subject.attributes.length ; i++) {621 if(( cert.subject.attributes[i].shortName === 'CN' ) && (cert.subject.attributes[i].value === 'attest.android.com'))622 hostnameIsValid = true;623 }624 if( !hostnameIsValid ) 625 return {verified: false, message: "invalid Hostname"}626 627 // STEP 2 - Verify nonce628 let jws = JSON.parse(base64url.decode(jwsArray[1]));629 let nonce = crypto.createHash('sha256').update(Buffer.concat([attestationObject.authData, clientDataHash])).digest().toString('base64'); 630 if( nonce != jws.nonce)631 return {verified: false, message: "invalid nonce"}632 // STEP 2 - Verify ctsProfileMatch = true633 if( jws.ctsProfileMatch !== true)634 return {verified: false, message: "invalid ctsProfileMatch"}635 let authrInfo = {636 fmt: attestationObject.fmt,637 aaguid: attestationObject.authData.aaguid.toString('hex'),638 publicKey: base64url.encode(publicKey),639 counter: attestationObject.authData.counter,640 credID: base64url.encode(attestationObject.authData.credID),641 cert: filename642 };643 644 645 console.log("Attestation is valid");646 return {verified: true, authrInfo: authrInfo, message: "OK", attestationObject: attestationObject};647}648/**649 * Takes an array of registered authenticators and find one specified by credID650 * @param {String} credID - base64url encoded credential651 * @param {Array} authenticators - list of authenticators652 * @return {Object} - found authenticator653 */654let findAuthr = (credID, authenticators) => {655 for(let authr of authenticators) {656 if(authr.credID === credID)657 return authr658 }659 throw new Error(`Unknown authenticator with credID ${credID}!`)660}661/**662 * Parses AuthenticatorData from GetAssertion response663 * @param {Buffer} buffer - Auth data buffer664 * @return {Object} - parsed authenticatorData struct665 */666let parseGetAssertAuthData = (buffer) => {667 let rpIdHash = buffer.slice(0, 32); buffer = buffer.slice(32);668 let flagsBuf = buffer.slice(0, 1); buffer = buffer.slice(1);669 let flags = flagsBuf[0];670 let counterBuf = buffer.slice(0, 4); buffer = buffer.slice(4);671 let counter = counterBuf.readUInt32BE(0);672 return {rpIdHash, flagsBuf, flags, counter, counterBuf}673}674let verifyAuthenticatorAssertionResponse = (webAuthnResponse, authenticators) => {675 let authr = findAuthr(webAuthnResponse.id, authenticators);676 let authenticatorData = base64url.toBuffer(webAuthnResponse.response.authenticatorData);677 let response = {'verified': false};678 //*********************************************** */679 // Generic check680 let authrDataStruct = parseGetAssertAuthData(authenticatorData);681 let clientDataHash = hash(base64url.toBuffer(webAuthnResponse.response.clientDataJSON));682 if(!(authrDataStruct.flags & U2F_USER_PRESENTED))683 throw new Error('User was NOT presented durring authentication!');684 let publicKey = ASN1toPEM(base64url.toBuffer(authr.publicKey));685 let signature = base64url.toBuffer(webAuthnResponse.response.signature);686 let signatureBase = Buffer.concat([authrDataStruct.rpIdHash, authrDataStruct.flagsBuf, authrDataStruct.counterBuf, clientDataHash]);687 response.verified = verifySignature(signature, signatureBase, publicKey);688 if( !response.verified ) response.message = "Invalid Signature";689 if(response.verified) {690 if(response.counter <= authr.counter)691 throw new Error('Authr counter did not increase!');692 authr.counter = authrDataStruct.counter693 }694 /*695 if(authr.fmt === 'fido-u2f') {696 let authrDataStruct = parseGetAssertAuthData(authenticatorData);697 if(!(authrDataStruct.flags & U2F_USER_PRESENTED))698 throw new Error('User was NOT presented durring authentication!');699 let clientDataHash = hash(base64url.toBuffer(webAuthnResponse.response.clientDataJSON))700 let signatureBase = Buffer.concat([authrDataStruct.rpIdHash, authrDataStruct.flagsBuf, authrDataStruct.counterBuf, clientDataHash]);701 let publicKey = ASN1toPEM(base64url.toBuffer(authr.publicKey));702 let signature = base64url.toBuffer(webAuthnResponse.response.signature);703 response.verified = verifySignature(signature, signatureBase, publicKey)704 if(response.verified) {705 if(response.counter <= authr.counter)706 throw new Error('Authr counter did not increase!');707 authr.counter = authrDataStruct.counter708 }709 }710 else if(authr.fmt === 'packed') { 711 let authrDataStruct = parseGetAssertAuthData(authenticatorData);712 let clientDataHash = hash(base64url.toBuffer(webAuthnResponse.response.clientDataJSON))713 let publicKey = ASN1toPEM(base64url.toBuffer(authr.publicKey));714 let signature = base64url.toBuffer(webAuthnResponse.response.signature);715 let signatureBase = Buffer.concat([authrDataStruct.rpIdHash, authrDataStruct.flagsBuf, authrDataStruct.counterBuf, clientDataHash]);716 // Step 1 - Verify that sig is a valid signature717 response.verified = verifySignature(signature, signatureBase, publicKey);718 if( !response.verified ) response.message = "Invalid Signature";719 if(response.verified) {720 if(response.counter <= authr.counter)721 throw new Error('Authr counter did not increase!');722 authr.counter = authrDataStruct.counter723 }724 }725 else if(authr.fmt === 'android-safetynet') { 726 console.log("login with: " + authr.fmt); 727 let authrDataStruct = parseGetAssertAuthData(authenticatorData);728 let clientDataHash = hash(base64url.toBuffer(webAuthnResponse.response.clientDataJSON));729 730 let publicKey = ASN1toPEM(base64url.toBuffer(authr.publicKey));731 let signature = base64url.toBuffer(webAuthnResponse.response.signature);732 let signatureBase = Buffer.concat([authrDataStruct.rpIdHash, authrDataStruct.flagsBuf, authrDataStruct.counterBuf, clientDataHash]);733 console.log(publicKey);734 console.log(signature);735 console.log(signatureBase);736 // Step 1 - Verify that sig is a valid signature737 response.verified = verifySignature(signature, signatureBase, publicKey);738 if( !response.verified ) response.message = "Invalid Signature";739 console.log(authr);740 console.log(authrDataStruct);741 console.log(clientDataHash);742 //response.verified = true;743 if(response.verified) {744 if(response.counter <= authr.counter)745 throw new Error('Authr counter did not increase!');746 authr.counter = authrDataStruct.counter747 }748 }*/749 750 return response;751}752module.exports = {753 randomBase64URLBuffer,754 generateServerMakeCredRequest,755 generateServerGetAssertion,756 verifyAuthenticatorAttestationResponse,757 verifyAuthenticatorAssertionResponse...
certificates.js
Source:certificates.js
1const childProcess = require('child_process');2const spawn = require('child_process').spawn;3const S = require('string');4const os = require('os');5const path = require('path');6const pem = require('pem');7const crypto = require('crypto');8const async = require('async');9const _ = require('underscore');10const fs = require('fs');11const request = require('request');12const Utils = require('./utils');13// keep track of root certificates14if(!global.PASSMARKED_ROOT_CERTS) global.PASSMARKED_ROOT_CERTS = [];15module.exports = exports = function(payload) {16 /**17 * Get our payload data18 **/19 var data = payload.getData();20 /**21 * Object to return22 **/23 var SSL = {};24 /**25 * Merge the libraries26 **/27 SSL = _.extend(SSL, Utils(payload));28 /** 29 * Cache of root certificates30 **/ 31 var roots = [];32 /**33 * Returns a presentable form of our internal certificate layout34 **/35 SSL.buildPresentableCertificate = function(cert) {36 // build the output37 var output = {38 pem: cert.pem,39 source: cert.collection,40 type: cert.type,41 commonName: cert.commonName,42 alt: ((cert.san || {}).dns || []),43 title: cert.commonName,44 description: cert.commonName,45 index: cert.index,46 verified: cert.verified === true,47 revoked: cert.revoked === true,48 signature: cert.signature || null,49 bits: cert.strength || null,50 crl: cert.crl || null,51 ocsp: cert.ocsp || null,52 url: cert.url || null53 };54 // returns the output55 return output;56 };57 /**58 * Builds the presentable chain59 **/60 SSL.buildPresentableChain = function(expectedPath, suppliedPath) {61 // the final chain to return 62 var chain = [];63 // sort the chain64 var sortedChain = _.sortBy(expectedPath, 'index');65 // loop the expected certificate66 for(var i = 0; i < sortedChain.length; i++) {67 // use this as the certificate68 var cert = _.find(suppliedPath || [], function(item) {69 return item.commonName === sortedChain[i].commonName;70 });71 // did we find this cert ?72 if(cert) {73 // add our found certificate74 chain.push(SSL.buildPresentableCertificate(cert));75 } else {76 // add the certificate from the chain as it was not present77 chain.push(SSL.buildPresentableCertificate(sortedChain[i]));78 }79 }80 // returns the chain we generated81 return chain;82 };83 /**84 * Returns true if the given certificate fingerprint is present85 **/86 SSL.findByFingerprint = function(certificates, fingerprints, fn) {87 // create a slug we can use to check88 var slug = _.pluck(fingerprints || {}, 'hash').join('-');89 // loop the certificates90 for(var i = 0; i < certificates.length; i++) {91 // loop the fingerprints92 for(var a = 0; a < (certificates[i].fingerprints || []).length; a++) {93 // check if any of the fingerprints match94 if(slug.indexOf(certificates[i].fingerprints[a].hash) !== -1) {95 // return the certificate96 return certificates[i];97 }98 }99 }100 // the default to return is null101 return null;102 };103 /**104 * Returns the possible SSL fingerprints105 **/106 SSL.getFingerPrintsFromPEM = function(cert, fn) {107 // list of hashes to add108 var fingerprints = [];109 // general all the fingerprints110 async.each([111 'md5', 'sha1', 'sha256'112 ], function (algorithm, cb) {113 // set the fingerprint114 pem.getFingerprint(cert, algorithm, function(err, prints) {115 // add to list116 if(prints && 117 S(prints.fingerprint || '').isEmpty() === false)118 fingerprints.push({119 algorithm: algorithm,120 hash: prints.fingerprint121 });122 // done123 cb(err);124 });125 }, function(err) {126 // finish127 fn(err, fingerprints);128 });129 };130 /**131 * Returns the timeout to use132 **/133 SSL.getTimeoutCommand = function() {134 // the path for timeout135 var platform = os.platform();136 var timeoutCommand = 'timeout';137 // fallback to gtimeout on osx138 if(platform === 'linux') {139 // set to gtimeout140 timeoutCommand = 'timeout ';141 } else if(platform === 'darwin') {142 // set to gtimeout143 timeoutCommand = '/usr/local/bin/gtimeout ';144 }145 // done146 return timeoutCommand;147 };148 /**149 * Returns the exec from bin150 **/151 SSL.getExecutable = function(version) {152 // return the full command153 return SSL.getTimeoutCommand() + ' 10 ' + SSL.getSSLExecutable(version);154 };155 /**156 * Execs the OPENSSL and first check the first testing stdout157 **/158 SSL.exec = function(cmd, fn) {159 // load out160 payload.debug('Running command: ' + cmd);161 // check if stdout and stderror was defined162 if(data.testingStdout || data.testingStderr) {163 // return the callback164 return fn(165 null, 166 (data.testingStdout || '').toString(), 167 (data.testingStderr || '').toString()168 );169 }170 // execute the actual process171 childProcess.exec(cmd, {}, function(err, stdout, stderr) {172 // check the error173 if(err) {174 // done175 return fn(err);176 }177 // to string just in case178 stdout = (stdout || '').toString();179 stderr = (stderr || '').toString();180 // done181 fn(err, stdout, stderr);182 });183 };184 /**185 * Returns all the certificates from the peer certificates186 **/187 SSL.getPeerCertificates = function(cert, fn) {188 // get the certificates189 var certs = [];190 var cnames = [];191 // the current item we are looking at192 var currentCertificate = cert;193 // keep count194 var count = 0;195 var run = 0;196 // loop it197 while(currentCertificate !== null) {198 // check if not in list already199 if(cnames.indexOf(currentCertificate.subject.CN) === -1) {200 // add our mod201 cnames.push(currentCertificate.subject.CN);202 // add the certificate203 certs.push(_.extend({}, currentCertificate, {204 index: count205 }));206 // increment207 count++;208 }209 // internal run count210 run++;211 // update the current certificate212 currentCertificate = currentCertificate.issuerCertificate || null;213 // stop it214 if(currentCertificate && 215 (currentCertificate.issuer || {}).CN === currentCertificate.subject.CN) {216 // done217 break;218 }219 // break if more than 30, just in case this is a "forever" loop :\220 if(run > 30) break;221 }222 // done223 fn(null, certs);224 };225 /**226 * Parses the PEM and returns valid information227 **/228 SSL.readCertificateInfo = function(pemCertificate, fn) {229 // get the details from pem230 pem.readCertificateInfo(pemCertificate, function(err, info) {231 // check for a error232 if(err) {233 // report234 payload.error('Problem parsing details out of Pem Format', err);235 // done236 return fn(err);237 }238 // right so we got the info239 info = (info || {});240 // extract more variables241 SSL.extractPEMVariables(pemCertificate, function(err, meta) {242 // set our properties243 info = _.extend(info, meta);244 // done245 fn(null, info);246 });247 });248 };249 /**250 *251 **/252 SSL.verifyChain = function(certs, info, fn) {253 // create the chain254 var userCert = certs[0];255 var middleCerts = certs.slice(1, certs.length).join('\n');256 // get certificates by the same company257 fn(null);258 };259 /**260 * Downloads all the known certificate paths261 **/262 SSL.downloadPath = function(cert, index, fn) {263 // the list for the path264 var paths = [];265 // sanity check266 if(!cert) return fn(null, paths);267 // limit the stack if more than 20 already268 if(index > 20) return fn(null, paths);269 // check if this is a peer certificate270 if(index === 0) {271 // convert the certificate272 return SSL.convertDERtoPEM(cert.raw, function(err, pemCertificate) {273 // check for a error274 if(err) {275 // debug276 payload.error('Something went wrong while converting the DER to PEM', err);277 // done278 return fn(err, paths);279 }280 // get the finger prints of the passed certificates281 SSL.getFingerPrintsFromPEM(pemCertificate, function(err, prints) {282 // handle error283 if(err) {284 // output to stderr285 payload.error('Problem generating fingerprints of PEM file', err);286 // done287 return fn(err, paths);288 }289 // get the details from pem290 SSL.readCertificateInfo(pemCertificate, function(err, info) {291 // handle error292 if(err) {293 // output to stderr294 payload.error('Problem generating certificate info of PEM file', err);295 // done296 return fn(err, paths);297 }298 // add to list299 paths.push(_.extend({}, info, {300 pem: pemCertificate,301 der: cert.raw.toString('base64'),302 fingerprints: prints,303 collection: 'supplied',304 index: index,305 type: 'user'306 }));307 // trigger the next certificate walk308 SSL.downloadPath(info, index + 1, function(err, returnedPaths) {309 // find any other legacy roots310 SSL.verifyChain(_.pluck(returnedPaths, 'pem'), info, function() {311 // done312 fn(err, paths.concat(returnedPaths || []));313 });314 });315 });316 });317 });318 } else if(S(cert.parent || '').isEmpty() === false) {319 // downloads the certificate320 SSL.downloadCertificate(cert.parent, function(err, downloadedCert) {321 // check for a error322 if(err) {323 // output324 payload.error('Problem downloading the certificate from ' + path, err);325 // done326 return fn(err);327 }328 // convert the certificate329 SSL.convertDERtoPEM(downloadedCert, function(err, pemCertificate) {330 // check for a error331 if(err) {332 // debug333 payload.error('Something went wrong while converting the DER to PEM', err);334 // done335 return fn(err);336 }337 // get the finger prints of the passed certificates338 SSL.getFingerPrintsFromPEM(pemCertificate, function(err, prints) {339 // get the details from pem340 SSL.readCertificateInfo(pemCertificate, function(err, info) {341 // check for a error342 if(err) {343 // debug344 payload.error('Something went wrong checking the certificate info', err);345 // done346 return cb(err);347 }348 SSL.getRootCertificateByIssuer(info, function(err, rootCertificate) {349 // get the type350 var certType = 'user';351 if(info.commonName === ((info || {}).issuer || {}).commonName)352 infoType = 'root';353 else354 infoType = 'intermediate';355 // add to list356 paths.push(_.extend({}, info, {357 url: cert.parent,358 pem: pemCertificate.split('\n'),359 der: downloadedCert.toString('base64'),360 fingerprints: prints,361 collection: 'expected',362 index: index,363 type: infoType364 }));365 // if not the root already ?366 // if(info.commonName == (info.issuer || {}).commonName) return fn(null, paths);367 // check the path368 if(S(info.parent || '').isEmpty() === true) {369 SSL.walkTrustedChain(info.issuer || {}, index + 1, function(err, returnedPaths) {370 // done371 fn(err, paths.concat(returnedPaths || []));372 });373 } else {374 // trigger the next certificate walk375 SSL.downloadPath(info, index + 1, function(err, returnedPaths) {376 // done377 fn(err, paths.concat(returnedPaths || []));378 });379 }380 });381 });382 });383 });384 });385 } else {386 // done387 fn(null, paths);388 }389 };390 /**391 * Checks down the chain392 **/393 SSL.walkTrustedChain = function(info, index, fn) {394 // the list of paths to return395 var paths = [];396 // check if not over limit397 if(index > 20) {398 // stop at the limit, 20399 return fn(null, paths);400 }401 // return the info if not defined402 if(!info) {403 // finish returning the path404 return fn(null, paths);405 }406 // check if registed as a trusted certificate407 SSL.getRootCertificateByIssuer(info, function(err, cert) {408 // sanity check409 if(!cert) return fn(null, paths);410 // create fingerprints411 SSL.getFingerPrintsFromPEM(cert.pem, function(err, prints) {412 // get the type413 var certType = 'user';414 if(cert.index === 0)415 certType = 'user';416 else if(cert.commonName === ((cert || {}).issuer || {}).commonName)417 certType = 'root';418 else419 certType = 'intermediate';420 // add to list421 paths.push(_.extend({}, info, {422 pem: cert.pem.split('\n'),423 der: cert.der,424 fingerprints: prints,425 collection: 'expected',426 index: index,427 type: certType428 }));429 // check if not the root430 if(cert.issuer && 431 cert.issuer.commonName !== cert.commonName) {432 // start to walk the actual tree433 return SSL.walkTrustedChain(cert.issuer, index + 1, function(err, returnedCert) {434 // return the paths435 fn(null, paths.concat(returnedCert || []));436 });437 }438 // return the paths439 fn(null, paths);440 });441 });442 };443 /**444 * Returns all the certificates from the peer certificates445 **/446 SSL.parseCertificates = function(peers, fn) {447 // get the certificates448 var certs = [];449 // loop the certificates450 async.each(peers, function(cert, cb) {451 // do te request and get all the certificates452 SSL.convertDERtoPEM(cert.raw, function(err, pemCertificate) {453 // check for a error454 if(err) {455 // debug456 payload.error('Something went wrong while converting the DER to PEM', err);457 // done458 return cb(err);459 }460 // get the details from pem461 SSL.readCertificateInfo(pemCertificate, function(err, info) {462 // check for a error463 if(err) {464 // debug465 payload.error('Something went wrong checking the certificate info', err);466 // done467 return cb(err);468 }469 // create fingerprints470 SSL.getFingerPrintsFromPEM(pemCertificate, function(err, prints) {471 SSL.getRootCertificateByIssuer(info, function(err, rootCertificate) {472 // get the type473 var certType = 'user';474 if(cert.index === 0)475 certType = 'user';476 else if(rootCertificate && 477 cert.commonName === ((cert || {}).issuer || {}).commonName)478 certType = 'root';479 else480 certType = 'intermediate';481 // add to list482 certs.push(_.extend({}, info, {483 pem: pemCertificate.split('\n'),484 der: cert.raw.toString('base64'),485 fingerprints: prints,486 collection: 'supplied',487 index: cert.index,488 type: certType489 }));490 // done491 cb(null);492 });493 });494 });495 });496 }, function() {497 // done498 fn(null, certs);499 });500 };501 /**502 * Returns the CRT for the path given, but first check the cache503 **/504 SSL.downloadCertificate = function(path, fn) {505 // create the sha1 hash506 var shasum = crypto.createHash('sha1');507 shasum.update(path);508 var hash = shasum.digest('hex');509 // build the cache key510 var cacheKey = [511 'passmarked',512 'certificates',513 'der',514 hash515 ].join(':');516 // first check the cache for the certificate517 payload.get(cacheKey, function(err, cachedBody) {518 // did we get a error ?519 if(!err && cachedBody) {520 // return the cached version521 return fn(null, new Buffer(cachedBody, 'base64'));522 }523 // do the actual request and processing524 request({525 url: path,526 timeout: 10 * 1000,527 encoding: null528 }, function(err, response, body) {529 // check if we have a error530 if(err) {531 // output to stderr532 payload.error('Problem downloading the given certificate from ' + path, err);533 // done534 return fn(err);535 }536 // check if we got the certificate537 if((response || {}).statusCode === 200) {538 // all good set in the cache and return539 return payload.set(cacheKey, body.toString('base64'), function(err) {540 // just output the error setting this in the cache541 if(err) payload.error('Problem settings the certificate from ' + path + ' in the cache', err);542 // just move on543 fn(null, body);544 });545 }546 // nope was not able to get the certificate547 fn(null);548 });549 });550 };551 // return object instance552 return SSL;...
webauthn.js
Source:webauthn.js
1const crypto = require('crypto')2const cbor = require('cbor')3import base64url from 'base64url'4import { db } from 'src/lib/db'5import { Webauthn } from 'src/lib/webauthn/Webauthn'6import {7 PublicKeyCredentialType,8 COSEAlgorithmIdentifier,9 AuthDataFlags,10 AttestationConveyancePreference,11 UserVerificationRequirement,12} from 'src/lib/webauthn/Dictionaries'13function verifyAuthenticatorAttestationResponse(14 attestationObject,15 clientDataJSON16) {17 const attestationBuffer = base64url.toBuffer(attestationObject)18 const ctapCredResp = cbor.decodeFirstSync(attestationBuffer)19 console.log('ctapCredResp', ctapCredResp)20 const authrDataStruct = Webauthn.parseCredAuthData(ctapCredResp.authData)21 console.log('authrDataStruct', authrDataStruct)22 if (!(authrDataStruct.flags & AuthDataFlags.USER_PRESENT)) {23 throw new Error('User was NOT presented durring authentication!')24 }25 const parsedCoseKey = Webauthn.parseCOSEKey(authrDataStruct.COSEPublicKey)26 console.log('parsedCoseKey', parsedCoseKey)27 const response = { verified: false }28 if (ctapCredResp.fmt === 'none') {29 response.verified = true30 } else if (ctapCredResp.fmt === 'fido-u2f') {31 const clientDataHash = Webauthn.hash(32 'sha256',33 base64url.toBuffer(clientDataJSON)34 )35 const publicKey = Webauthn.COSEECDHAtoPKCS(parsedCoseKey)36 const reservedByte = Buffer.from([0x00])37 const signatureBase = Buffer.concat([38 reservedByte,39 authrDataStruct.rpIdHash,40 clientDataHash,41 authrDataStruct.credID,42 publicKey,43 ])44 const PEMCertificate = Webauthn.ASN1toPEM(ctapCredResp.attStmt.x5c[0])45 const signature = ctapCredResp.attStmt.sig46 response.verified = Webauthn.verifySignature(47 signature,48 signatureBase,49 PEMCertificate50 )51 /*} else if (52 ctapCredResp.fmt === 'packed' &&53 ctapCredResp.attStmt.hasOwnProperty('x5c')54 ) {55 const clientDataHash = Webauthn.hash('sha256',56 base64url.toBuffer(clientDataJSON)57 )58 const signatureBase = Buffer.concat([59 ctapCredResp.authData,60 clientDataHash,61 ])62 const PEMCertificate = Webauthn.ASN1toPEM(ctapCredResp.attStmt.x5c[0])63 const signature = ctapCredResp.attStmt.sig64 const pem = Certificate.fromPEM(PEMCertificate)65 // Getting requirements from https://www.w3.org/TR/webauthn/#packed-attestation66 const aaguid_ext = pem.getExtension('1.3.6.1.4.1.45724.1.1.4')67 response.verified = // Verify that sig is a valid signature over the concatenation of authenticatorData68 // and clientDataHash using the attestation public key in attestnCert with the algorithm specified in alg.69 Webauthn.verifySignature(signature, signatureBase, PEMCertificate) &&70 // version must be 3 (which is indicated by an ASN.1 INTEGER with value 2)71 pem.version == 3 &&72 // ISO 3166 valid country73 // TODO: Re-add this74 // typeof iso_3166_1.whereAlpha2(pem.subject.countryName) !== 'undefined' &&75 // Legal name of the Authenticator vendor (UTF8String)76 pem.subject.organizationName &&77 // Literal string âAuthenticator Attestationâ (UTF8String)78 pem.subject.organizationalUnitName === 'Authenticator Attestation' &&79 // A UTF8String of the vendorâs choosing80 pem.subject.commonName &&81 // The Basic Constraints extension MUST have the CA component set to false82 !pem.extensions.isCA &&83 // If attestnCert contains an extension with OID 1.3.6.1.4.1.45724.1.1.4 (id-fido-gen-ce-aaguid)84 // verify that the value of this extension matches the aaguid in authenticatorData.85 // The extension MUST NOT be marked as critical.86 (aaguid_ext != null87 ? authrDataStruct.hasOwnProperty('aaguid')88 ? !aaguid_ext.critical &&89 aaguid_ext.value.slice(2).equals(authrDataStruct.aaguid)90 : false91 : true)92*/93 // Self signed94 } else if (ctapCredResp.fmt === 'packed') {95 const clientDataHash = Webauthn.hash(96 'sha256',97 base64url.toBuffer(clientDataJSON)98 )99 const signatureBase = Buffer.concat([ctapCredResp.authData, clientDataHash])100 const publicKey = Webauthn.COSEECDHAtoPKCS(parsedCoseKey)101 const PEMCertificate = Webauthn.ASN1toPEM(publicKey)102 const {103 attStmt: { sig: signature, alg },104 } = ctapCredResp105 response.verified = // Verify that sig is a valid signature over the concatenation of authenticatorData106 // and clientDataHash using the attestation public key in attestnCert with the algorithm specified in alg.107 Webauthn.verifySignature(signature, signatureBase, PEMCertificate) &&108 alg === COSEAlgorithmIdentifier.ECDSA_w_SHA256109 /*} else if (ctapCredResp.fmt === 'android-safetynet') {110 let [111 header,112 payload,113 signature,114 ] = ctapCredResp.attStmt.response.toString('utf8').split('.')115 const signatureBase = Buffer.from([header, payload].join('.'))116 header = JSON.parse(base64url.decode(header))117 payload = JSON.parse(base64url.decode(payload))118 signature = base64url.toBuffer(signature)119 const PEMCertificate = Webauthn.ASN1toPEM(120 Buffer.from(header.x5c[0], 'base64')121 )122 const pem = Certificate.fromPEM(PEMCertificate)123 response.verified = // Verify that sig is a valid signature over the concatenation of authenticatorData124 // and clientDataHash using the attestation public key in attestnCert with the algorithm specified in alg.125 Webauthn.verifySignature(signature, signatureBase, PEMCertificate) &&126 // version must be 3 (which is indicated by an ASN.1 INTEGER with value 2)127 pem.version == 3 &&128 pem.subject.commonName === 'attest.android.com'*/129 } else {130 throw new Error(`Unsupported attestation format: ${ctapCredResp.fmt}`)131 }132 if (response.verified) {133 response.authrInfo = {134 fmt: 'fido-u2f', // TODO: Always? Really?135 publicKey: parsedCoseKey,136 counter: authrDataStruct.counter,137 credID: base64url.encode(authrDataStruct.credID),138 }139 }140 console.log('verify response', response)141 return response142}143export const prepCredentialCreation = async ({ userName, displayName }) => {144 const challenge = base64url.encode(crypto.randomBytes(26))145 const userIdBuf = crypto.randomBytes(16)146 // On a production system you don't want to do this. I just do it here to147 // make it easier to find these keys, where this becomes part of the system148 // key name149 userIdBuf[0] = 0xba150 userIdBuf[1] = 0xda151 userIdBuf[2] = 0x55152 const userId = base64url.encode(userIdBuf)153 let dbUser = await db.user.findOne({ where: { userName } })154 if (dbUser?.registered) {155 return {156 ok: false,157 message: `Username ${userName} already exists`,158 }159 }160 dbUser = await db.user.upsert({161 where: { userName },162 create: {163 userName,164 name: displayName,165 registered: false,166 id: userId,167 authenticators: [],168 challenge,169 },170 update: {171 userName,172 name: displayName,173 registered: false,174 id: userId,175 authenticators: [],176 challenge,177 },178 })179 const opts = {180 challenge,181 rp: {182 name: 'Tobbe Inc',183 id: process.env.RP_HOST,184 icon: 'https://rwsbwebauthn.netlify.app/favicon.png',185 },186 user: {187 id: userId,188 name: userName,189 displayName,190 },191 pubKeyCredParams: [192 {193 type: PublicKeyCredentialType.PUBLIC_KEY,194 alg: COSEAlgorithmIdentifier.ECDSA_w_SHA256,195 },196 {197 // Windows Hello198 type: PublicKeyCredentialType.PUBLIC_KEY,199 alg: COSEAlgorithmIdentifier.RSASSA_PKCS1_v1_5_using_SHA_256,200 },201 ],202 attestation: AttestationConveyancePreference.NONE,203 }204 console.log('opts', opts)205 return {206 ok: true,207 opts,208 }209}210export const verifyAndRegister = async ({211 userName,212 clientDataJSON,213 attestationObject,214 id,215}) => {216 let dbUser = await db.user.findOne({ where: { userName } })217 if (!dbUser || !dbUser.challenge) {218 return {219 ok: false,220 message: `No ongoing creation for user ${userName}`,221 }222 }223 let clientData224 try {225 const bufferarray = base64url.toBuffer(clientDataJSON)226 const json = String.fromCharCode.apply(null, new Uint8Array(bufferarray))227 console.log('json', json)228 clientData = JSON.parse(json)229 } catch (err) {230 console.log('err', err)231 return {232 ok: false,233 message: 'failed to decode client data',234 }235 }236 const { challenge, origin, type } = clientData237 console.log('challenge ', challenge)238 console.log('saved challenge', dbUser.challenge)239 console.log('id', id)240 if (dbUser.authenticators && dbUser.authenticators[0]) {241 console.log('saved id', dbUser.authenticators[0].credID)242 }243 if (!challenge || challenge !== dbUser.challenge) {244 return {245 ok: false,246 message: 'challenge missmatch',247 }248 }249 if (origin !== 'http://localhost:8910' && origin !== 'https://rwsbwebauthn.netlify.app') {250 return {251 ok: false,252 message: 'origin missmatch',253 }254 }255 if (!type || type !== 'webauthn.create') {256 return {257 ok: false,258 message: 'wrong type',259 }260 }261 let result262 if (attestationObject !== undefined) {263 result = verifyAuthenticatorAttestationResponse(264 attestationObject,265 clientDataJSON266 )267 if (result.verified) {268 const authenticators = dbUser.authenticators269 authenticators.push(result.authrInfo)270 await db.user.update({271 data: {272 ...dbUser,273 registered: true,274 authenticators,275 },276 where: { userName },277 })278 }279 }280 console.log('===================================')281 console.log('== Registration done ==')282 console.log('===================================')283 return {284 ok: true,285 }286}287export const prepCredentialGet = async ({ userName }) => {288 const challenge = base64url.encode(crypto.randomBytes(26))289 let dbUser = await db.user.findOne({ where: { userName } })290 if (!dbUser || !dbUser.registered) {291 return {292 ok: false,293 message: 'user not registered',294 }295 }296 console.log('get-challenge', challenge)297 await db.user.update({298 data: { ...dbUser, challenge },299 where: { userName },300 })301 return {302 ok: true,303 opts: {304 challenge,305 // allowCredentials: [{306 // type: PublicKeyCredentialType.PUBLIC_KEY,307 // }]308 userVerification: UserVerificationRequirement.DISCOURAGED,309 },310 }311}312export const verifyAndLogin = async ({313 userName,314 authenticatorData,315 clientDataJSON,316 signature,317}) => {318 let dbUser = await db.user.findOne({ where: { userName } })319 if (!dbUser?.registered) {320 return {321 ok: false,322 message: 'user not registered',323 }324 }325 console.log('dbUser', dbUser)326 let clientData327 let clientDataJSONStr328 try {329 const bufferarray = base64url.toBuffer(clientDataJSON)330 const json = String.fromCharCode.apply(null, new Uint8Array(bufferarray))331 clientDataJSONStr = json332 console.log('json', json)333 clientData = JSON.parse(json)334 } catch (err) {335 console.log('err', err)336 return {337 ok: false,338 message: 'failed to decode client data',339 }340 }341 console.log('clientData', clientData)342 console.log('authenticatorData', authenticatorData)343 const authDataBuffer = base64url.toBuffer(authenticatorData)344 const authrDataStruct = Webauthn.parseCredAuthData(authDataBuffer)345 console.log('authDataBuffer', authenticatorData)346 console.log('authDataB64', Buffer.from(authenticatorData, 'base64'))347 console.log('authrData', authrDataStruct)348 console.log('rpId', base64url.encode(authrDataStruct.rpIdHash))349 // TODO: Check proper counter350 if (authrDataStruct.counter <= /*dbUser...*/ 0) {351 return {352 ok: false,353 message: 'possibly cloned authenticator',354 }355 }356 const sigBuffer = base64url.toBuffer(signature)357 console.log('sigBuffer', sigBuffer)358 // TODO: Support more keys by looping over all of them359 const publicKey = dbUser.authenticators[0].publicKey360 //Step 11: Verify that the rpIdHash in authData is the SHA-256 hash of the361 //RP ID expected by the Relying Party.362 if (!authrDataStruct.rpIdHash.equals(Webauthn.hash('sha256', process.env.RP_HOST))) {363 throw new Error(364 'RPID hash does not match expected value: sha256(' + hostname + ')'365 )366 }367 const clientDataHash = Webauthn.hash(368 'sha256',369 Buffer.from(clientDataJSON, 'base64')370 // base64url.toBuffer(clientDataJSON)371 )372 // TODO: Try to just use authenticatorData373 const signatureBase = Buffer.concat([374 // authrDataStruct.rpIdHash,375 // authrDataStruct.flagsBuf,376 // authrDataStruct.counterBuf,377 // Buffer.from(authenticatorData, 'base64'),378 authDataBuffer,379 clientDataHash,380 ])381 const verified = Webauthn.verifySignature(sigBuffer, signatureBase, publicKey)382 // `signature` is authenticatorData concatinated with clientDataHash and383 // then encrypted with the private key.384 console.log('verified', verified)385 const pemPublicKey = Webauthn.RSAPublicKeyToPEM(publicKey)386 console.log(387 'inline verify',388 crypto389 .createVerify('RSA-SHA256')390 .update(authDataBuffer)391 .update(crypto.createHash('sha256').update(clientDataJSONStr).digest())392 .verify(pemPublicKey, sigBuffer)393 )394 if (!verified) {395 return {396 ok: false,397 message: 'Could not verify signature',398 }399 }400 return {401 ok: true,402 }...
utils.js
Source:utils.js
1const childProcess = require('child_process');2const spawn = require('child_process').spawn;3const S = require('string');4const os = require('os');5const path = require('path');6const pem = require('pem');7const crypto = require('crypto');8const async = require('async');9const _ = require('underscore');10const fs = require('fs');11const request = require('request');12// keep track of root certificates13if(!global.PASSMARKED_ROOT_CERTS) global.PASSMARKED_ROOT_CERTS = [];14module.exports = exports = function(logger) {15 /**16 * Object to return17 **/18 var SSL = {};19 /** 20 * Cache of root certificates21 **/ 22 var roots = [];23 /**24 * Returns the exec from bin25 **/26 SSL.getSSLExecutable = function(version) {27 return '/usr/local/ssl/bin/openssl';28 };29 /**30 * Matches a regex against a string returns the result else null31 **/32 SSL.extractVariable = function(text, pattern) {33 // set the matches34 var matches = new RegExp(pattern).exec(text || '');35 // did we find any ?36 if(matches && matches.length > 1) {37 return matches[1];38 }39 // default40 return null;41 };42 /**43 * Executes a investigation via OPENSSL on CRT properties44 **/45 SSL.getPEMInformation = function(pemFormat, fn) {46 // the singleton of the callback to call47 var callback = _.once(fn);48 // handle the util49 var proc = spawn('openssl', [50 'x509',51 '-noout',52 '-text'53 ]);54 // chunks we got55 var chunks = [];56 var err = null;57 var timer = null;58 // add any chunks we got59 proc.stdout.on('data', function(data) { 60 chunks.push(data);61 });62 // handle any errors we found63 proc.stderr.on('data', function(data) {64 // set as the error65 err = new Error(data);66 });67 // handle on close68 proc.on('close', function(code) {69 // clear the timeout70 if(timer) clearTimeout(timer);71 // done72 callback(err, Buffer.concat(chunks || []).toString());73 });74 // write to the stream75 proc.stdin.write(pemFormat);76 proc.stdin.end();77 // timeout if any78 timer = setTimeout(function() {79 // done80 callback(new Error('Timeout'));81 }, 1000 * 5);82 };83 /**84 * Converts a certificate in DER format to PEM85 **/86 SSL.convertDERtoPEM = function(derFormat, fn) {87 // check if not already in PEM format88 if(derFormat && derFormat.toString().toLowerCase().indexOf('-----begin') === 0) {89 // just return it90 return fn(null, derFormat.toString());91 }92 // the singleton of the callback to call93 var callback = _.once(fn);94 // handle the util95 var proc = spawn(SSL.getSSLExecutable(), [96 'x509',97 '-inform',98 'der'99 ]);100 // chunks we got101 var chunks = [];102 var err = null;103 var timer = null;104 // add any chunks we got105 proc.stdout.on('data', function(data) { 106 chunks.push(data);107 });108 // handle any errors we found109 proc.stderr.on('data', function(data) {110 // set as the error111 err = new Error(data);112 });113 // handle on close114 proc.on('close', function(code) {115 // clear the timeout116 if(timer) clearTimeout(timer);117 // done118 callback(err, Buffer.concat(chunks || []).toString());119 });120 // write to the stream121 proc.stdin.write(derFormat);122 proc.stdin.end();123 // timeout if any124 timer = setTimeout(function() {125 // done126 callback(new Error('Timeout'));127 }, 1000 * 5);128 };129 /**130 * Extracts known information from the PEM format of the certificate131 **/132 SSL.extractPEMVariables = function(pem, fn) {133 // get the details134 SSL.getPEMInformation(pem, function(err, parsedInfo) {135 // return the results returned from the PEM136 fn(null, {137 parent: SSL.extractVariable(parsedInfo, /ca\s+issuer[s].*\-.*uri\:(.*)/gim),138 ocsp: SSL.extractVariable(parsedInfo, /ocsp\s+\-\s+uri\:(.*)/gim),139 crl: SSL.extractVariable(parsedInfo, /uri\:.*(http.*\.crl)/gim),140 signature: SSL.extractVariable(parsedInfo, /Signature[\s+]Algorithm.*\:[\s+](.*)/gim),141 strengh: SSL.extractVariable(parsedInfo, /Public\s+Key.*\:.*\((.*)\s+bit\)/gim)142 });143 });144 };145 /**146 * Parses the PEM and returns valid information147 **/148 SSL.readCertificateInfo = function(pemCertificate, fn) {149 // get the details from pem150 pem.readCertificateInfo(pemCertificate, function(err, info) {151 // check for a error152 if(err) {153 // report154 logger.error('Problem parsing details out of Pem Format', err);155 // done156 return fn(err);157 }158 // right so we got the info159 info = (info || {});160 // extract more variables161 SSL.extractPEMVariables(pemCertificate, function(err, meta) {162 // set our properties163 info = _.extend(info, meta);164 // done165 fn(null, info);166 });167 });168 };169 /**170 * Returns a root certificate that matches the issuer passed171 **/172 SSL.getRootCertificateByIssuer = function(issuer, fn) {173 // sanity checj174 if(!issuer) return fn(null);175 // get the certificates176 SSL.getInstalledRootCertificates(function(err, roots) {177 if(err) {178 return fn(err);179 }180 for(var i = 0; i < (roots || []).length; i++) {181 182 if(roots[i].commonName == issuer.commonName) {183 // done184 return fn(null, roots[i]);185 }186 }187 // done188 fn(null);189 });190 };191 /**192 * Extracts the installed certificates193 **/194 SSL.getInstalledRootCertificates = function(fn) {195 // return it196 if(global.PASSMARKED_ROOT_CERTS && 197 global.PASSMARKED_ROOT_CERTS.length > 0) {198 // done199 return fn(null, global.PASSMARKED_ROOT_CERTS);200 }201 // awk -v cmd='openssl x509 -noout -subject' ' /BEGIN/{close(cmd)};{print | cmd}' < /etc/ssl/certs/ca-certificates.crt202 // the singleton of the callback to call203 var callback = _.once(fn);204 // handle the delim205 var DELIM = '--------------------';206 // run the command207 childProcess.exec(208 "awk -v cmd='echo \"" + DELIM + "\";openssl x509' ' /BEGIN/{close(cmd)};{print | cmd}' < " + (process.env.CA_CERTS || '/etc/ssl/certs/ca-certificates.crt'), 209 {210 maxBuffer: 1024 * 1024 * 100211 },212 function(err, stdout, stderr) {213 // clear the timeout214 if(timer) clearTimeout(timer);215 // get the lines216 var sections = (stdout || '').toString().split(DELIM);217 // the lines218 var subjects = [];219 async.eachLimit(sections || [], 10, function(pemCertificate, cb) {220 // read in the cert221 SSL.readCertificateInfo(pemCertificate, function(err, cert) {222 // sanity check223 if(!cert) return cb(null);224 // add it225 subjects.push(_.extend({}, cert, {226 pem: pemCertificate227 }));228 // output progress229 if(subjects.length % 15 == 0)230 logger.info('getInstalledRootCertificates', 'Loading certificates ' + subjects.length + '/' + sections.length);231 // done232 cb(null);233 234 });235 }, function(err) {236 // set the certs237 if(subjects.length > 0)238 global.PASSMARKED_ROOT_CERTS = subjects;239 // done240 callback(err, subjects);241 });242 });243 // timeout if any244 timer = setTimeout(function() {245 // done246 callback(new Error('Timeout'));247 }, 1000 * 15);248 };249 // return object instance250 return SSL;...
safari-ssl-e2e-specs.js
Source:safari-ssl-e2e-specs.js
1import chai from 'chai';2import chaiAsPromised from 'chai-as-promised';3import _ from 'lodash';4import B from 'bluebird';5import { MOCHA_TIMEOUT, initSession, deleteSession } from '../helpers/session';6import { doesIncludeCookie, doesNotIncludeCookie,7 newCookie, oldCookie1 } from './safari-basic-e2e-specs';8import { SAFARI_CAPS } from '../desired';9import https from 'https';10const pem = B.promisifyAll(require('pem'));11chai.should();12chai.use(chaiAsPromised);13const HTTPS_PORT = 9762;14const LOCAL_HTTPS_URL = `https://localhost:${HTTPS_PORT}/`;15const caps = _.defaults({16 safariInitialUrl: LOCAL_HTTPS_URL,17 noReset: true,18}, SAFARI_CAPS);19let pemCertificate;20if (!process.env.REAL_DEVICE && !process.env.CLOUD) {21 describe.skip('Safari SSL', function () {22 this.timeout(MOCHA_TIMEOUT);23 let sslServer, driver;24 before(async function () {25 // Create a random pem certificate26 const privateKey = await pem.createPrivateKeyAsync();27 const keys = await pem.createCertificateAsync({28 days: 1,29 selfSigned: true,30 serviceKey: privateKey.key,31 altNames: ['localhost'],32 });33 pemCertificate = keys.certificate;34 // Host an SSL server that uses that certificate35 const serverOpts = {key: keys.serviceKey, cert: pemCertificate};36 sslServer = https.createServer(serverOpts, (req, res) => {37 res.end('Arbitrary text');38 }).listen(HTTPS_PORT);39 caps.customSSLCert = pemCertificate;40 });41 after(async function () {42 await deleteSession();43 if (sslServer) {44 await sslServer.close();45 }46 });47 it('should open pages with untrusted certs if the cert was provided in desired capabilities', async function () {48 try {49 driver = await initSession(caps);50 await driver.source().should.eventually.include('Arbitrary text');51 await driver.quit();52 await B.delay(1000);53 // Now do another session using the same cert to verify that it still works54 // (Don't do it on CLOUD. Restarting is too slow)55 if (!process.env.CLOUD) {56 await driver.init(caps);57 await driver.get(LOCAL_HTTPS_URL);58 await driver.source().should.eventually.include('Arbitrary text');59 }60 } finally {61 await deleteSession();62 }63 });64 describe('cookies', function () {65 const secureCookie = Object.assign({}, newCookie, {66 secure: true,67 name: 'securecookie',68 value: 'this is a secure cookie',69 });70 before(async function () {71 driver = await initSession(caps);72 });73 beforeEach(async function () {74 await driver.get(LOCAL_HTTPS_URL);75 await driver.setCookie(oldCookie1);76 await driver.deleteCookie(secureCookie.name);77 });78 it('should be able to set a secure cookie', async function () {79 let cookies = await driver.allCookies();80 doesNotIncludeCookie(cookies, secureCookie);81 await driver.setCookie(secureCookie);82 cookies = await driver.allCookies();83 doesIncludeCookie(cookies, secureCookie);84 // should not clobber old cookie85 doesIncludeCookie(cookies, oldCookie1);86 });87 it('should be able to set a secure cookie', async function () {88 await driver.setCookie(secureCookie);89 let cookies = await driver.allCookies();90 doesIncludeCookie(cookies, secureCookie);91 // should not clobber old cookie92 doesIncludeCookie(cookies, oldCookie1);93 await driver.deleteCookie(secureCookie.name);94 cookies = await driver.allCookies();95 doesNotIncludeCookie(cookies, secureCookie);96 });97 });98 });...
p12.js
Source:p12.js
1import * as forge from 'node-forge';2export function ConvertToPem(p12base64, password) {3 const p12Asn1 = forge.asn1.fromDer(p12base64);4 const p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, false, password);5 const pemKey = getKeyFromP12(p12, password);6 const pemCertificate = getCertificateFromP12(p12);7 return { pemKey, pemCertificate };8}9function getKeyFromP12(p12, password) {10 let keyData = p12.getBags({ bagType: forge.pki.oids.pkcs8ShroudedKeyBag }, password);11 let pkcs8Key = keyData[forge.pki.oids.pkcs8ShroudedKeyBag][0];12 if (typeof pkcs8Key === 'undefined') {13 keyData = p12.getBags({ bagType: forge.pki.oids.keyBag }, password);14 pkcs8Key = keyData[forge.pki.oids.keyBag][0];15 }16 if (typeof pkcs8Key === 'undefined') {17 throw new Error('Unable to get private key');18 }19 // convert a Forge private key to an ASN.1 RSAPrivateKey20 const rsaPrivateKey = forge.pki.privateKeyToAsn1(pkcs8Key.key);21 // wrap an RSAPrivateKey ASN.1 object in a PKCS#8 ASN.1 PrivateKeyInfo22 const privateKeyInfo = forge.pki.wrapRsaPrivateKey(rsaPrivateKey);23 // convert a PKCS#8 ASN.1 PrivateKeyInfo to PEM24 const pemKey = forge.pki.privateKeyInfoToPem(privateKeyInfo);25 return pemKey;26}27function getCertificateFromP12(p12) {28 const certData = p12.getBags({ bagType: forge.pki.oids.certBag });29 const certificateList = certData[forge.pki.oids.certBag];30 let pemCertificate = '';31 certificateList.forEach(certificate => { pemCertificate += forge.pki.certificateToPem(certificate.cert) });32 return pemCertificate;33}34function ConvertCertificateToP12(privateKeyTxt, certificateChainTxt, password) {35 const certChainBeginTxt = "-----BEGIN CERTIFICATE-----";36 const certChainEndTxt = "-----END CERTIFICATE-----";37 const certChain = certificateChainTxt.split(certChainEndTxt);38 const certificateChain = [];39 certChain.forEach((certTxt) => {40 if (certTxt.indexOf(certChainBeginTxt) < 0) {41 return;42 }43 certificateChain.push(forge.pki.certificateFromPem(certTxt + certChainEndTxt));44 });45 const privateKey = forge.pki.privateKeyFromPem(privateKeyTxt);46 const p12Asn1 = forge.pkcs12.toPkcs12Asn1(47 privateKey, certificateChain, password, { generateLocalKeyId: true, algorithm: '3des' });48 return forge.asn1.toDer(p12Asn1).getBytes();49}50const p12 = {51 ConvertToPem,52 ConvertCertificateToP1253}...
getCertificateInfo.js
Source:getCertificateInfo.js
1"use strict";2require("core-js/modules/es.string.split");3require("core-js/modules/web.dom-collections.iterator");4Object.defineProperty(exports, "__esModule", {5 value: true6});7const jsrsasign_1 = require("jsrsasign");8/**9 * Extract PEM certificate info10 *11 * @param pemCertificate Result from call to `convertASN1toPEM(x5c[0])`12 */13function getCertificateInfo(pemCertificate) {14 var _a;15 const subjectCert = new jsrsasign_1.X509();16 subjectCert.readCertPEM(pemCertificate); // Break apart the Issuer17 const issuerString = subjectCert.getIssuerString();18 const issuerParts = issuerString.slice(1).split('/');19 const issuer = {};20 issuerParts.forEach(field => {21 const [key, val] = field.split('=');22 issuer[key] = val;23 }); // Break apart the Subject24 let subjectRaw = '/';25 try {26 subjectRaw = subjectCert.getSubjectString();27 } catch (err) {28 // Don't throw on an error that indicates an empty subject29 if (err !== 'malformed RDN') {30 throw err;31 }32 }33 const subjectParts = subjectRaw.slice(1).split('/');34 const subject = {};35 subjectParts.forEach(field => {36 if (field) {37 const [key, val] = field.split('=');38 subject[key] = val;39 }40 });41 const {42 version43 } = subjectCert;44 const basicConstraintsCA = !!((_a = subjectCert.getExtBasicConstraints()) === null || _a === void 0 ? void 0 : _a.cA);45 return {46 issuer,47 subject,48 version,49 basicConstraintsCA,50 notBefore: jsrsasign_1.zulutodate(subjectCert.getNotBefore()),51 notAfter: jsrsasign_1.zulutodate(subjectCert.getNotAfter())52 };53}...
ssl-specs.js
Source:ssl-specs.js
1import desired from './desired';2import B from 'bluebird';3import https from 'https';4import setup from '../safari-setup';5import { MOCHA_SAFARI_TIMEOUT } from '../../helpers/session';6const pem = B.promisifyAll(require('pem'));7describe('When accessing an HTTPS encrypted site in Safari', function () {8 this.timeout(MOCHA_SAFARI_TIMEOUT);9 let sslServer;10 let caps = Object.assign({}, desired);11 before(async function () {12 // Create an HTTPS server with a random pem certificate13 let privateKey = await pem.createPrivateKeyAsync();14 let keys = await pem.createCertificateAsync({days: 1, selfSigned: true, serviceKey: privateKey.key});15 let pemCertificate = keys.certificate;16 sslServer = https.createServer({key: keys.serviceKey, cert: pemCertificate}, function (req, res) {17 res.end('Arbitrary text');18 }).listen(9758);19 caps.customSSLCert = pemCertificate;20 caps.fullReset = false;21 caps.noReset = true;22 });23 const driver = setup(this, caps).driver;24 after(async function () {25 if (sslServer) {26 await sslServer.close();27 }28 });29 it('should be able to access it as long the PEM certificate is provided as a capability', async function () {30 await B.delay(500);31 await driver.setUrl('https://localhost:9758');32 (await driver.getPageSource()).should.include('Arbitrary text');33 });...
Using AI Code Generation
1describe('My First Test', function() {2 it('Visits the Kitchen Sink', function() {3 cy.contains('type').click()4 cy.url().should('include', '/commands/actions')5 cy.get('.action-email')6 .type('
Using AI Code Generation
1describe('My First Test', () => {2 it('Visits the Kitchen Sink', () => {3 cy.contains('type').click()4 cy.url().should('include', '/commands/actions')5 cy.get('.action-email')6 .type('
Using AI Code Generation
1Cypress.Commands.add('pemCertificate', (url) => {2 cy.request(url).then((response) => {3 cy.writeFile('certificate.pem', response.body);4 });5});6Cypress.Commands.add('pemCertificate', (url) => {7 cy.request(url).then((response) => {8 cy.writeFile('certificate.pem', response.body);9 });10});11Cypress.Commands.add('pemCertificate', (url) => {12 cy.request(url).then((response) => {13 cy.writeFile('certificate.pem', response.body);14 });15});16Cypress.Commands.add('pemCertificate', (url) => {17 cy.request(url).then((response) => {18 cy.writeFile('certificate.pem', response.body);19 });20});21Cypress.Commands.add('pemCertificate', (url) => {22 cy.request(url).then((response) => {23 cy.writeFile('certificate.pem', response.body);24 });25});26Cypress.Commands.add('pemCertificate', (url) => {27 cy.request(url).then((response) => {28 cy.writeFile('certificate.pem', response.body);29 });30});31Cypress.Commands.add('pemCertificate', (url) => {32 cy.request(url).then((response) => {33 cy.writeFile('certificate.pem', response.body);34 });35});36Cypress.Commands.add('pemCertificate', (url) => {37 cy.request(url).then((response) => {38 cy.writeFile('certificate.pem', response.body);39 });40});41Cypress.Commands.add('pemCertificate', (url) => {42 cy.request(url).then((response) => {43 cy.writeFile('certificate.pem', response.body);44 });45});46Cypress.Commands.add('pemCertificate', (url) => {47 cy.request(url).then((response) => {48 cy.writeFile('certificate.pem', response.body);49 });50});51Cypress.Commands.add('pemCertificate', (url) => {
Using AI Code Generation
1const fs = require('fs');2const cypress = require('cypress');3const https = require('https');4const pem = require('pem');5const path = require('path');6const os = require('os');7const { spawn } = require('child_process');8const { promisify } = require('util');9const { exec } = require('child_process');10const execAsync = promisify(exec);11const certDir = path.join(os.tmpdir(), 'certs');12const certPath = path.join(certDir, 'cert.pem');13const keyPath = path.join(certDir, 'key.pem');14const certPath2 = path.join(certDir, 'cert2.pem');15const keyPath2 = path.join(certDir, 'key2.pem');16const certPath3 = path.join(certDir, 'cert3.pem');17const keyPath3 = path.join(certDir, 'key3.pem');18const certPath4 = path.join(certDir, 'cert4.pem');19const keyPath4 = path.join(certDir, 'key4.pem');20const certPath5 = path.join(certDir, 'cert5.pem');21const keyPath5 = path.join(certDir, 'key5.pem');22const certPath6 = path.join(certDir, 'cert6.pem');23const keyPath6 = path.join(certDir, 'key6.pem');24const certPath7 = path.join(certDir, 'cert7.pem');25const keyPath7 = path.join(certDir, 'key7.pem');26const certPath8 = path.join(certDir, 'cert8.pem');27const keyPath8 = path.join(certDir, 'key8.pem');28const certPath9 = path.join(certDir, 'cert9.pem');29const keyPath9 = path.join(certDir, 'key9.pem');30const certPath10 = path.join(certDir, 'cert10.pem');31const keyPath10 = path.join(certDir, 'key10.pem');32const certPath11 = path.join(certDir, 'cert11.pem');33const keyPath11 = path.join(certDir, 'key11.pem');34const certPath12 = path.join(certDir, 'cert12.pem');35const keyPath12 = path.join(certDir, 'key12.pem');36const certPath13 = path.join(certDir, 'cert13.pem');37const keyPath13 = path.join(certDir, 'key13.pem');38const certPath14 = path.join(certDir, '
Using AI Code Generation
1cy.task('pemCertificate').then((cert) => {2 cy.request({3 })4})5const pemCertificate = require('cypress-pem-certificate')6module.exports = (on, config) => {7 on('task', {8 })9}10import 'cypress-pem-certificate'11{12}13const pemCertificate = require('cypress-pem-certificate')14module.exports = (on, config) => {15 on('task', {16 })17}18import 'cypress-pem-certificate'19{20}21const pemCertificate = require('cypress-pem-certificate')22module.exports = (on, config) => {23 on('task', {24 })25}26import 'cypress-pem-certificate'27{28}
Using AI Code Generation
1const fs = require('fs');2const https = require('https');3const pem = require('pem');4const request = require('request');5describe('test', function() {6 it('test', function() {7 pem.createCertificate({ days: 1, selfSigned: true }, function(err, keys) {8 const server = https.createServer(9 {10 },11 (req, res) => {12 res.writeHead(200);13 res.end('hello world\n');14 }15 );16 server.listen(0, 'localhost', function() {17 const port = server.address().port;18 request(url, function(error, response, body) {19 expect(body).to.equal('hello world\n');20 server.close();21 });22 });23 });24 });25});
Cypress is a renowned Javascript-based open-source, easy-to-use end-to-end testing framework primarily used for testing web applications. Cypress is a relatively new player in the automation testing space and has been gaining much traction lately, as evidenced by the number of Forks (2.7K) and Stars (42.1K) for the project. LambdaTest’s Cypress Tutorial covers step-by-step guides that will help you learn from the basics till you run automation tests on LambdaTest.
You can elevate your expertise with end-to-end testing using the Cypress automation framework and stay one step ahead in your career by earning a Cypress certification. Check out our Cypress 101 Certification.
Watch this 3 hours of complete tutorial to learn the basics of Cypress and various Cypress commands with the Cypress testing at LambdaTest.
Get 100 minutes of automation test minutes FREE!!