Skip to content
Snippets Groups Projects
signingUtilities.js 46.9 KiB
Newer Older
Alexey Lunin's avatar
Alexey Lunin committed
import {
  canTryPincode,
  failPincodeAttempt,
  getTimeLeftInLocalStorage,
  makeid
} from "./appUtility";
import {
  bufferToHexCodes,
  stringToArrayBuffer,
  isEqualBuffer
} from "pvutils";
Alexey Lunin's avatar
Alexey Lunin committed
import { fromBER } from "asn1js";
import { ContentInfo, SignedData, Certificate } from "pkijs";
Alexey Lunin's avatar
Alexey Lunin committed
import { algomap, rdnmap } from "../constants/certificates";
Damyan Mitev's avatar
Damyan Mitev committed
import {
  fixNewLines,
  getAttachment,
  getAttachments,
  getGlobalHeaderValue,
  getHeaderValue,
  parseMIME
} from "../helpers/mailparser";
import dataUriToBlob from "data-uri-to-blob";
import {
  extractHtmlBodyFromString,
  getFilenameFromHeaders,
  SIGNATURE_CONTENT_TYPE
} from "./emailUtilities";
Damyan Mitev's avatar
Damyan Mitev committed
import {
  stringToUtf8ByteArray,
  utf8ByteArrayToString,
  stringToUtf8Base64,
  utf8Base64ToString,
  base64ToByteArray,
  byteArrayToBase64
} from "../utilities/stringUtilities";
Alexey Lunin's avatar
Alexey Lunin committed
const libmime = require("libmime");
const pkijs = require("pkijs");
const asn1js = require("asn1js");
const pvutils = require("pvutils");

//*********************************************************************************

const CERTIFIATE_Version_1 = 0;
const CERTIFIATE_Version_3 = 2;

//these bit fields are reversed, WTF!
Alexey Lunin's avatar
Alexey Lunin committed
const KEY_USAGE_DigitalSignature = 0x80; //01;
const KEY_USAGE_NonRepudiation = 0x40; //02;
const KEY_USAGE_KeyEncipherment = 0x20; //04;
const KEY_USAGE_DataEncipherment = 0x10; //08;
const KEY_USAGE_KeyAgreement = 0x08; //10;
const KEY_USAGE_KeyCertSign = 0x04; //20;
const KEY_USAGE_CRLSign = 0x02; //40;
//const KEY_USAGE_EncipherOnly		= 0x01;//80; // Not used for now. Must be used together with KEY_USAGE_KeyAgreement (maybe should be ORed as a constant directly?)
//const KEY_USAGE_DecipherOnly		= 0x80;//0100; // If used, modify "KeyUsage" extension array buffer size and appropriate bit operators to accomodate for extra byte

Alexey Lunin's avatar
Alexey Lunin committed
const KEY_USAGE_LeafCertificate =
  KEY_USAGE_DigitalSignature |
  KEY_USAGE_NonRepudiation |
  KEY_USAGE_KeyEncipherment |
  KEY_USAGE_DataEncipherment;
const KEY_USAGE_CertificateAuthority =
  KEY_USAGE_DigitalSignature |
  KEY_USAGE_KeyCertSign |
  KEY_USAGE_CRLSign;
Alexey Lunin's avatar
Alexey Lunin committed

const OID_EXT_KEY_USAGE_Any = "2.5.29.37.0";
const OID_ID_PKIX_ServerAuth = "1.3.6.1.5.5.7.3.1";
const OID_ID_PKIX_ClientAuth = "1.3.6.1.5.5.7.3.2";
const OID_ID_PKIX_CodeSigning = "1.3.6.1.5.5.7.3.3";
const OID_ID_PKIX_EmailProtection = "1.3.6.1.5.5.7.3.4";
const OID_ID_PKIX_TimeStamping = "1.3.6.1.5.5.7.3.8";
const OID_ID_PKIX_OCSPSigning = "1.3.6.1.5.5.7.3.9";
// const OID_EXT_KEY_USAGE_MS...	= "1.3.6.1.4.1.311.10.3.1"; // Microsoft Certificate Trust List signing
// const OID_EXT_KEY_USAGE_MS...	= "1.3.6.1.4.1.311.10.3.4";  // Microsoft Encrypted File System
Alexey Lunin's avatar
Alexey Lunin committed
const OID_PKCS7_Data = "1.2.840.113549.1.7.1";
const OID_PKCS7_SignedData = "1.2.840.113549.1.7.2";
const OID_PKCS7_EnvelopedData = "1.2.840.113549.1.7.3";
const OID_PKCS9_EmailAddress = "1.2.840.113549.1.9.1";
const OID_PKCS9_ContentType = "1.2.840.113549.1.9.3";
const OID_PKCS9_MessageDigest = "1.2.840.113549.1.9.4";
const OID_PKCS9_SigningTime = "1.2.840.113549.1.9.5";

const defaultAlgorithms = {
  hashAlg: "SHA-256",
  signAlg: "RSASSA-PKCS1-v1_5",
  keyLength: 2048
};

Alexey Lunin's avatar
Alexey Lunin committed
const AES_encryptionVariant_Password = 2;
const encryptionAlgorithm = {
  name: "AES-CBC",
  length: 128
};

// Convert a hex string to a byte array
function hexStringToBytes(hex) {
Damyan Mitev's avatar
Damyan Mitev committed
  let bytes, c;
  if (hex.length % 2 === 1) {
    hex = "0" + hex;
  }
  for (bytes = [], c = 0; c < hex.length; c += 2) {
    bytes.push(parseInt(hex.substr(c, 2), 16));
  }
  return bytes;
}

export class CertificateData {
  //**********************************************************************************
  /**
   * Constructor for SignedData class
   * @param {pkijs.Certificate} [certificate]
   * @param {Object} [parameters]
   */
  constructor(parameters = {}) {
    this.serialNumber = null; //string || ArrayBuffer || Uint8Array

Damyan Mitev's avatar
Damyan Mitev committed
    this.keyPair = null; // write only; {publicKey, privateKey}
Damyan Mitev's avatar
Damyan Mitev committed
    this.signatureAlgorithm = null; //read-only, initialized form pkijs.Certificate object

    this.algorithms = null; // write only; {hashAlg: "SHA-256", signAlg: "RSASSA-PKCS1-v1_5", keyLength: 2048};

    this.issuer = null; //same as subject

    this.subject = {
      commonName: null, //string
      country: null, //string
      locality: null, //string
      state: null, //string
      organization: null, //string
      organizationUnit: null, //string
      email: null, //string
      url: null //string
    };

    this.validity = {
      notBefore: null, //new Date()
      notAfter: null, //new Date()
Damyan Mitev's avatar
Damyan Mitev committed
      validYears: null //int
    };

    this.isCA = false;

    if (parameters) {
      if (parameters instanceof Certificate) {
        this.fromCertificate(parameters);
      } else {
        this.fromParameters(parameters);
      }
    }
  }

  fromCertificate(certificate) {
    this.serialNumber = bufferToHexCodes(
      certificate.serialNumber.valueBlock.valueHex
    );

    let signatureAlgorithm =
      algomap[certificate.signatureAlgorithm.algorithmId];
    if (typeof signatureAlgorithm === "undefined") {
      signatureAlgorithm = certificate.signatureAlgorithm.algorithmId;
    } else {
      signatureAlgorithm = `${signatureAlgorithm}`;
    }
    this.signatureAlgorithm = signatureAlgorithm;

    this.issuer = {};
    const issuer = certificate.issuer.typesAndValues;
    for (const typeAndValue of issuer) {
      let typeVal = rdnmap[typeAndValue.type];
      if (typeof typeVal === "undefined") {
        typeVal = typeAndValue.type;
      }
      const subjVal = typeAndValue.value.valueBlock.value;
      this.issuer[typeVal] = subjVal;
    }

    const subject = certificate.subject.typesAndValues;
    for (const typeAndValue of subject) {
      let typeVal = rdnmap[typeAndValue.type];
      if (typeof typeVal === "undefined") {
        typeVal = typeAndValue.type;
      }
      const subjVal = typeAndValue.value.valueBlock.value;
      this.subject[typeVal] = subjVal;
    }

    this.validity.notBefore = certificate.notBefore.value;
    this.validity.notAfter = certificate.notAfter.value;

    this.isCA = certificate.issuer.isEqual(certificate.subject);
  }

  fromParameters(parameters) {
    if ("serialNumber" in parameters) {
      this.serialNumber = parameters.serialNumber;
    }

Loading
Loading full blame...