Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  utils.sys.mjs   Sprache: unbekannt

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

import { Observers } from "resource://services-common/observers.sys.mjs";

import { CommonUtils } from "resource://services-common/utils.sys.mjs";

const lazy = {};

ChromeUtils.defineLazyGetter(lazy, "textEncoder", function () {
  return new TextEncoder();
});

/**
 * A number of `Legacy` suffixed functions are exposed by CryptoUtils.
 * They work with octet strings, which were used before Javascript
 * got ArrayBuffer and friends.
 */
export var CryptoUtils = {
  xor(a, b) {
    let bytes = [];

    if (a.length != b.length) {
      throw new Error(
        "can't xor unequal length strings: " + a.length + " vs " + b.length
      );
    }

    for (let i = 0; i < a.length; i++) {
      bytes[i] = a.charCodeAt(i) ^ b.charCodeAt(i);
    }

    return String.fromCharCode.apply(String, bytes);
  },

  /**
   * Generate a string of random bytes.
   * @returns {String} Octet string
   */
  generateRandomBytesLegacy(length) {
    let bytes = CryptoUtils.generateRandomBytes(length);
    return CommonUtils.arrayBufferToByteString(bytes);
  },

  generateRandomBytes(length) {
    return crypto.getRandomValues(new Uint8Array(length));
  },

  /**
   * UTF8-encode a message and hash it with the given hasher. Returns a
   * string containing bytes.
   */
  digestUTF8(message, hasher) {
    let data = lazy.textEncoder.encode(message);
    hasher.update(data, data.length);
    let result = hasher.finish(false);
    return result;
  },

  /**
   * Treat the given message as a bytes string (if necessary) and hash it with
   * the given hasher. Returns a string containing bytes.
   */
  digestBytes(bytes, hasher) {
    if (typeof bytes == "string" || bytes instanceof String) {
      bytes = CommonUtils.byteStringToArrayBuffer(bytes);
    }
    return CryptoUtils.digestBytesArray(bytes, hasher);
  },

  digestBytesArray(bytes, hasher) {
    hasher.update(bytes, bytes.length);
    let result = hasher.finish(false);
    return result;
  },

  /**
   * Encode the message into UTF-8 and feed the resulting bytes into the
   * given hasher. Does not return a hash. This can be called multiple times
   * with a single hasher, but eventually you must extract the result
   * yourself.
   */
  updateUTF8(message, hasher) {
    let bytes = lazy.textEncoder.encode(message);
    hasher.update(bytes, bytes.length);
  },

  sha256(message) {
    let hasher = Cc["@mozilla.org/security/hash;1"].createInstance(
      Ci.nsICryptoHash
    );
    hasher.init(hasher.SHA256);
    return CommonUtils.bytesAsHex(CryptoUtils.digestUTF8(message, hasher));
  },

  sha256Base64(message) {
    let data = lazy.textEncoder.encode(message);
    let hasher = Cc["@mozilla.org/security/hash;1"].createInstance(
      Ci.nsICryptoHash
    );
    hasher.init(hasher.SHA256);
    hasher.update(data, data.length);
    return hasher.finish(true);
  },

  /**
   * @param {string} alg Hash algorithm (common values are SHA-1 or SHA-256)
   * @param {string} key Key as an octet string.
   * @param {string} data Data as an octet string.
   */
  async hmacLegacy(alg, key, data) {
    if (!key || !key.length) {
      key = "\0";
    }
    data = CommonUtils.byteStringToArrayBuffer(data);
    key = CommonUtils.byteStringToArrayBuffer(key);
    const result = await CryptoUtils.hmac(alg, key, data);
    return CommonUtils.arrayBufferToByteString(result);
  },

  /**
   * @param {string} ikm IKM as an octet string.
   * @param {string} salt Salt as an Hex string.
   * @param {string} info Info as a regular string.
   * @param {Number} len Desired output length in bytes.
   */
  async hkdfLegacy(ikm, xts, info, len) {
    ikm = CommonUtils.byteStringToArrayBuffer(ikm);
    xts = CommonUtils.byteStringToArrayBuffer(xts);
    info = lazy.textEncoder.encode(info);
    const okm = await CryptoUtils.hkdf(ikm, xts, info, len);
    return CommonUtils.arrayBufferToByteString(okm);
  },

  /**
   * @param {String} alg Hash algorithm (common values are SHA-1 or SHA-256)
   * @param {ArrayBuffer} key
   * @param {ArrayBuffer} data
   * @param {Number} len Desired output length in bytes.
   * @returns {Uint8Array}
   */
  async hmac(alg, key, data) {
    const hmacKey = await crypto.subtle.importKey(
      "raw",
      key,
      { name: "HMAC", hash: alg },
      false,
      ["sign"]
    );
    const result = await crypto.subtle.sign("HMAC", hmacKey, data);
    return new Uint8Array(result);
  },

  /**
   * @param {ArrayBuffer} ikm
   * @param {ArrayBuffer} salt
   * @param {ArrayBuffer} info
   * @param {Number} len Desired output length in bytes.
   * @returns {Uint8Array}
   */
  async hkdf(ikm, salt, info, len) {
    const key = await crypto.subtle.importKey(
      "raw",
      ikm,
      { name: "HKDF" },
      false,
      ["deriveBits"]
    );
    const okm = await crypto.subtle.deriveBits(
      {
        name: "HKDF",
        hash: "SHA-256",
        salt,
        info,
      },
      key,
      len * 8
    );
    return new Uint8Array(okm);
  },

  /**
   * PBKDF2 password stretching with SHA-256 hmac.
   *
   * @param {string} passphrase Passphrase as an octet string.
   * @param {string} salt Salt as an octet string.
   * @param {string} iterations Number of iterations, a positive integer.
   * @param {string} len Desired output length in bytes.
   */
  async pbkdf2Generate(passphrase, salt, iterations, len) {
    passphrase = CommonUtils.byteStringToArrayBuffer(passphrase);
    salt = CommonUtils.byteStringToArrayBuffer(salt);
    const key = await crypto.subtle.importKey(
      "raw",
      passphrase,
      { name: "PBKDF2" },
      false,
      ["deriveBits"]
    );
    const output = await crypto.subtle.deriveBits(
      {
        name: "PBKDF2",
        hash: "SHA-256",
        salt,
        iterations,
      },
      key,
      len * 8
    );
    return CommonUtils.arrayBufferToByteString(new Uint8Array(output));
  },

  /**
   * Compute the HTTP MAC SHA-1 for an HTTP request.
   *
   * @param  identifier
   *         (string) MAC Key Identifier.
   * @param  key
   *         (string) MAC Key.
   * @param  method
   *         (string) HTTP request method.
   * @param  URI
   *         (nsIURI) HTTP request URI.
   * @param  extra
   *         (object) Optional extra parameters. Valid keys are:
   *           nonce_bytes - How many bytes the nonce should be. This defaults
   *             to 8. Note that this many bytes are Base64 encoded, so the
   *             string length of the nonce will be longer than this value.
   *           ts - Timestamp to use. Should only be defined for testing.
   *           nonce - String nonce. Should only be defined for testing as this
   *             function will generate a cryptographically secure random one
   *             if not defined.
   *           ext - Extra string to be included in MAC. Per the HTTP MAC spec,
   *             the format is undefined and thus application specific.
   * @returns
   *         (object) Contains results of operation and input arguments (for
   *           symmetry). The object has the following keys:
   *
   *           identifier - (string) MAC Key Identifier (from arguments).
   *           key - (string) MAC Key (from arguments).
   *           method - (string) HTTP request method (from arguments).
   *           hostname - (string) HTTP hostname used (derived from arguments).
   *           port - (string) HTTP port number used (derived from arguments).
   *           mac - (string) Raw HMAC digest bytes.
   *           getHeader - (function) Call to obtain the string Authorization
   *             header value for this invocation.
   *           nonce - (string) Nonce value used.
   *           ts - (number) Integer seconds since Unix epoch that was used.
   */
  async computeHTTPMACSHA1(identifier, key, method, uri, extra) {
    let ts = extra && extra.ts ? extra.ts : Math.floor(Date.now() / 1000);
    let nonce_bytes = extra && extra.nonce_bytes > 0 ? extra.nonce_bytes : 8;

    // We are allowed to use more than the Base64 alphabet if we want.
    let nonce =
      extra && extra.nonce
        ? extra.nonce
        : btoa(CryptoUtils.generateRandomBytesLegacy(nonce_bytes));

    let host = uri.asciiHost;
    let port;
    let usedMethod = method.toUpperCase();

    if (uri.port != -1) {
      port = uri.port;
    } else if (uri.scheme == "http") {
      port = "80";
    } else if (uri.scheme == "https") {
      port = "443";
    } else {
      throw new Error("Unsupported URI scheme: " + uri.scheme);
    }

    let ext = extra && extra.ext ? extra.ext : "";

    let requestString =
      ts.toString(10) +
      "\n" +
      nonce +
      "\n" +
      usedMethod +
      "\n" +
      uri.pathQueryRef +
      "\n" +
      host +
      "\n" +
      port +
      "\n" +
      ext +
      "\n";

    const mac = await CryptoUtils.hmacLegacy("SHA-1", key, requestString);

    function getHeader() {
      return CryptoUtils.getHTTPMACSHA1Header(
        this.identifier,
        this.ts,
        this.nonce,
        this.mac,
        this.ext
      );
    }

    return {
      identifier,
      key,
      method: usedMethod,
      hostname: host,
      port,
      mac,
      nonce,
      ts,
      ext,
      getHeader,
    };
  },

  /**
   * Obtain the HTTP MAC Authorization header value from fields.
   *
   * @param  identifier
   *         (string) MAC key identifier.
   * @param  ts
   *         (number) Integer seconds since Unix epoch.
   * @param  nonce
   *         (string) Nonce value.
   * @param  mac
   *         (string) Computed HMAC digest (raw bytes).
   * @param  ext
   *         (optional) (string) Extra string content.
   * @returns
   *         (string) Value to put in Authorization header.
   */
  getHTTPMACSHA1Header: function getHTTPMACSHA1Header(
    identifier,
    ts,
    nonce,
    mac,
    ext
  ) {
    let header =
      'MAC id="' +
      identifier +
      '", ' +
      'ts="' +
      ts +
      '", ' +
      'nonce="' +
      nonce +
      '", ' +
      'mac="' +
      btoa(mac) +
      '"';

    if (!ext) {
      return header;
    }

    return (header += ', ext="' + ext + '"');
  },

  /**
   * Given an HTTP header value, strip out any attributes.
   */

  stripHeaderAttributes(value) {
    value = value || "";
    let i = value.indexOf(";");
    return value
      .substring(0, i >= 0 ? i : undefined)
      .trim()
      .toLowerCase();
  },

  /**
   * Compute the HAWK client values (mostly the header) for an HTTP request.
   *
   * @param  URI
   *         (nsIURI) HTTP request URI.
   * @param  method
   *         (string) HTTP request method.
   * @param  options
   *         (object) extra parameters (all but "credentials" are optional):
   *           credentials - (object, mandatory) HAWK credentials object.
   *             All three keys are required:
   *             id - (string) key identifier
   *             key - (string) raw key bytes
   *           ext - (string) application-specific data, included in MAC
   *           localtimeOffsetMsec - (number) local clock offset (vs server)
   *           payload - (string) payload to include in hash, containing the
   *                     HTTP request body. If not provided, the HAWK hash
   *                     will not cover the request body, and the server
   *                     should not check it either. This will be UTF-8
   *                     encoded into bytes before hashing. This function
   *                     cannot handle arbitrary binary data, sorry (the
   *                     UTF-8 encoding process will corrupt any codepoints
   *                     between U+0080 and U+00FF). Callers must be careful
   *                     to use an HTTP client function which encodes the
   *                     payload exactly the same way, otherwise the hash
   *                     will not match.
   *           contentType - (string) payload Content-Type. This is included
   *                         (without any attributes like "charset=") in the
   *                         HAWK hash. It does *not* affect interpretation
   *                         of the "payload" property.
   *           hash - (base64 string) pre-calculated payload hash. If
   *                  provided, "payload" is ignored.
   *           ts - (number) pre-calculated timestamp, secs since epoch
   *           now - (number) current time, ms-since-epoch, for tests
   *           nonce - (string) pre-calculated nonce. Should only be defined
   *                   for testing as this function will generate a
   *                   cryptographically secure random one if not defined.
   * @returns
   *         Promise<Object> Contains results of operation. The object has the
   *         following keys:
   *           field - (string) HAWK header, to use in Authorization: header
   *           artifacts - (object) other generated values:
   *             ts - (number) timestamp, in seconds since epoch
   *             nonce - (string)
   *             method - (string)
   *             resource - (string) path plus querystring
   *             host - (string)
   *             port - (number)
   *             hash - (string) payload hash (base64)
   *             ext - (string) app-specific data
   *             MAC - (string) request MAC (base64)
   */
  async computeHAWK(uri, method, options) {
    let credentials = options.credentials;
    let ts =
      options.ts ||
      Math.floor(
        ((options.now || Date.now()) + (options.localtimeOffsetMsec || 0)) /
          1000
      );
    let port;
    if (uri.port != -1) {
      port = uri.port;
    } else if (uri.scheme == "http") {
      port = 80;
    } else if (uri.scheme == "https") {
      port = 443;
    } else {
      throw new Error("Unsupported URI scheme: " + uri.scheme);
    }

    let artifacts = {
      ts,
      nonce: options.nonce || btoa(CryptoUtils.generateRandomBytesLegacy(8)),
      method: method.toUpperCase(),
      resource: uri.pathQueryRef, // This includes both path and search/queryarg.
      host: uri.asciiHost.toLowerCase(), // This includes punycoding.
      port: port.toString(10),
      hash: options.hash,
      ext: options.ext,
    };

    let contentType = CryptoUtils.stripHeaderAttributes(options.contentType);

    if (
      !artifacts.hash &&
      options.hasOwnProperty("payload") &&
      options.payload
    ) {
      const buffer = lazy.textEncoder.encode(
        `hawk.1.payload\n${contentType}\n${options.payload}\n`
      );
      const hash = await crypto.subtle.digest("SHA-256", buffer);
      // HAWK specifies this .hash to use +/ (not _-) and include the
      // trailing "==" padding.
      artifacts.hash = ChromeUtils.base64URLEncode(hash, { pad: true })
        .replace(/-/g, "+")
        .replace(/_/g, "/");
    }

    let requestString =
      "hawk.1.header\n" +
      artifacts.ts.toString(10) +
      "\n" +
      artifacts.nonce +
      "\n" +
      artifacts.method +
      "\n" +
      artifacts.resource +
      "\n" +
      artifacts.host +
      "\n" +
      artifacts.port +
      "\n" +
      (artifacts.hash || "") +
      "\n";
    if (artifacts.ext) {
      requestString += artifacts.ext.replace("\\", "\\\\").replace("\n", "\\n");
    }
    requestString += "\n";

    const hash = await CryptoUtils.hmacLegacy(
      "SHA-256",
      credentials.key,
      requestString
    );
    artifacts.mac = btoa(hash);
    // The output MAC uses "+" and "/", and padded== .

    function escape(attribute) {
      // This is used for "x=y" attributes inside HTTP headers.
      return attribute.replace(/\\/g, "\\\\").replace(/\"/g, '\\"');
    }
    let header =
      'Hawk id="' +
      credentials.id +
      '", ' +
      'ts="' +
      artifacts.ts +
      '", ' +
      'nonce="' +
      artifacts.nonce +
      '", ' +
      (artifacts.hash ? 'hash="' + artifacts.hash + '", ' : "") +
      (artifacts.ext ? 'ext="' + escape(artifacts.ext) + '", ' : "") +
      'mac="' +
      artifacts.mac +
      '"';
    return {
      artifacts,
      field: header,
    };
  },
};

var Svc = {};

Observers.add("xpcom-shutdown", function unloadServices() {
  Observers.remove("xpcom-shutdown", unloadServices);

  for (let k in Svc) {
    delete Svc[k];
  }
});

[ Dauer der Verarbeitung: 0.27 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge