/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { Base64 } from "../codec/Base64Codec.js";
import { DerCodec, DerType, ObjectId } from "../codec/DerCodec.js";
import { MatterError, NotImplementedError } from "../MatterError.js";
import { Bytes } from "../util/Bytes.js";
import { ec } from "./Crypto.js";
import { KeyInputError } from "./CryptoError.js";
import { Pem } from "./Pem.js";
const {
  p256: { getPublicKey, Point, getSharedSecret }
} = ec;
const JWK_KEYS = [
  "crv",
  "d",
  "dp",
  "dq",
  "e",
  "ext",
  "k",
  "key_ops",
  "kty",
  "n",
  "oth",
  "p",
  "q",
  "qi",
  "use",
  "x",
  "y"
];
var KeyType = /* @__PURE__ */ ((KeyType2) => {
  KeyType2["EC"] = "EC";
  KeyType2["RSA"] = "RSA";
  KeyType2["oct"] = "oct";
  return KeyType2;
})(KeyType || {});
var CurveType = /* @__PURE__ */ ((CurveType2) => {
  CurveType2["p256"] = "P-256";
  CurveType2["p384"] = "P-384";
  CurveType2["p521"] = "P-521";
  return CurveType2;
})(CurveType || {});
var Asn1ObjectID = /* @__PURE__ */ ((Asn1ObjectID2) => {
  Asn1ObjectID2["ecPublicKey"] = "2a8648ce3d0201";
  Asn1ObjectID2["prime256r1"] = "2a8648ce3d030107";
  Asn1ObjectID2["prime384r1"] = "0103840022";
  Asn1ObjectID2["prime521r1"] = "0103840023";
  return Asn1ObjectID2;
})(Asn1ObjectID || {});
const CurveLookup = {
  ["2a8648ce3d030107" /* prime256r1 */]: "P-256" /* p256 */,
  ["0103840022" /* prime384r1 */]: "P-384" /* p384 */,
  ["0103840023" /* prime521r1 */]: "P-521" /* p521 */
};
const CurveOidLookup = {
  ["P-256" /* p256 */]: "2a8648ce3d030107" /* prime256r1 */,
  ["P-384" /* p384 */]: "0103840022" /* prime384r1 */,
  ["P-521" /* p521 */]: "0103840023" /* prime521r1 */
};
function checkDerVersion(type, node, version) {
  const derVersion = node && node._tag === DerType.Integer && node._bytes && node._bytes.byteLength === 1 && Bytes.of(node._bytes)[0];
  if (derVersion !== version) {
    throw new KeyInputError(`${type} key version mismatch`);
  }
}
function getDerObjectID(type, node) {
  const id = node && node._tag === DerType.ObjectIdentifier && node._bytes?.byteLength > 1 && node._bytes;
  if (id) return id;
  throw new KeyInputError(`Missing object in ${type} key`);
}
function getDerCurve(type, node) {
  const oid = getDerObjectID(type, node);
  const curve = CurveLookup[Bytes.toHex(oid)];
  if (curve) return curve;
  throw new KeyInputError(`Unsupported ${type} EC curve`);
}
function getDerKey(type, node, derType = DerType.OctetString) {
  const key = node && node._tag === derType && node._bytes?.byteLength > 1 && node._bytes;
  if (key) return key;
  throw new MatterError(`Missing ${type} key node`);
}
var Translators;
((Translators2) => {
  Translators2.sec1 = {
    set: function(input) {
      const decoded = DerCodec.decode(input);
      const versionNode = decoded?._elements?.[0];
      checkDerVersion("SEC 1", versionNode, 1);
      const curveNode = decoded?._elements?.[2]?._elements?.[0];
      const curve = getDerCurve("SEC 1", curveNode);
      const keyNode = decoded?._elements?.[1];
      const key = getDerKey("SEC 1", keyNode);
      this.type = "EC" /* EC */;
      this.curve = curve;
      this.privateBits = key;
    },
    get: function() {
      throw new NotImplementedError("SEC1 export not implemented");
    }
  };
  Translators2.pkcs8 = {
    set: function(input) {
      const outer = Pem.decode(input);
      const version = outer?._elements?.[0];
      checkDerVersion("PKCS #8", version, 0);
      const algorithmElements = outer?._elements?.[1]?._elements;
      const algorithm = getDerObjectID("PKCS #8", algorithmElements?.[0]);
      if (Bytes.toHex(algorithm) !== "2a8648ce3d0201" /* ecPublicKey */) {
        throw new KeyInputError("Unsupported PKCS #8 decryption algorithm");
      }
      const curve = getDerCurve("PKCS #8", algorithmElements?.[1]);
      const innerBytes = outer?._elements?.[2]._bytes;
      if (innerBytes === void 0 || innerBytes === null) {
        throw new KeyInputError("Invalid PKCS #8 key");
      }
      const inner = DerCodec.decode(innerBytes);
      const key = getDerKey("PKCS #8", inner?._elements?.[1]);
      this.type = "EC" /* EC */;
      this.curve = curve;
      this.privateBits = key;
    },
    get: function() {
      if (this.type !== "EC" /* EC */ || !this.d || !this.crv) {
        return void 0;
      }
      const curveOid = CurveOidLookup[this.crv];
      if (!curveOid) {
        throw new KeyInputError(`Unsupported curve for PKCS #8 export: ${this.crv}`);
      }
      const privateBits = this.privateBits;
      if (privateBits === void 0) {
        return void 0;
      }
      const innerSequence = DerCodec.encode({
        version: 1,
        privateKey: privateBits
      });
      return DerCodec.encode({
        version: 0,
        algorithm: {
          algorithmId: ObjectId("2a8648ce3d0201" /* ecPublicKey */),
          curve: ObjectId(curveOid)
        },
        privateKey: { _tag: DerType.OctetString, _bytes: innerSequence }
      });
    }
  };
  Translators2.pem = {
    set: function(input) {
      this.pkcs8 = Pem.asDer(input);
    },
    get: function() {
      const pkcs82 = this.pkcs8;
      if (pkcs82) {
        return Pem.encode(pkcs82, "PRIVATE KEY");
      }
    }
  };
  Translators2.spki = {
    set: function(input) {
      const decoded = DerCodec.decode(input);
      const algorithmElements = decoded?._elements?.[0]?._elements;
      const algorithm = getDerObjectID("SPKI", algorithmElements?.[0]);
      if (Bytes.toHex(algorithm) !== "2a8648ce3d0201" /* ecPublicKey */) {
        throw new KeyInputError("Unsupported SPKI decryption algorithm");
      }
      const curve = getDerCurve("SPKI", algorithmElements?.[1]);
      const key = getDerKey("SPKI", decoded?._elements?.[1], DerType.BitString);
      this.type = "EC" /* EC */;
      this.curve = curve;
      this.publicBits = key;
    },
    get: function() {
      throw new NotImplementedError("SPKI export not implemented");
    }
  };
  Translators2.publicBits = {
    set: function(input) {
      const data = Bytes.of(input);
      if (!(data.length % 2)) {
        throw new KeyInputError("Invalid public key encoding");
      }
      switch (data[0]) {
        case 2:
        case 3:
          throw new KeyInputError("Unsupported public key compression");
        case 4:
          break;
        case 5:
          throw new KeyInputError("Illegal public key format specifier");
      }
      const coordinateLength = (data.length - 1) / 2;
      inferCurve(this, coordinateLength);
      this.type = "EC" /* EC */;
      this.xBits = data.slice(1, coordinateLength + 1);
      this.yBits = data.slice(coordinateLength + 1);
    },
    get: function() {
      if (this.xBits === void 0 || this.yBits === void 0) {
        return void 0;
      }
      return Bytes.concat(new Uint8Array([4]), this.xBits, this.yBits);
    }
  };
  Translators2.keyPairBits = {
    set: function(keyPair) {
      this.publicBits = keyPair.publicKey;
      this.privateBits = keyPair.privateKey;
    },
    get: function() {
      const publicBits2 = this.publicBits;
      const privateBits = this.privateBits;
      if (publicBits2 === void 0 || privateBits === void 0) {
        return;
      }
      return {
        publicKey: Bytes.of(publicBits2),
        privateKey: Bytes.of(privateBits)
      };
    }
  };
})(Translators || (Translators = {}));
var Aliases = /* @__PURE__ */ ((Aliases2) => {
  Aliases2["algorithm"] = "alg";
  Aliases2["curve"] = "crv";
  Aliases2["type"] = "kty";
  Aliases2["operations"] = "key_ops";
  Aliases2["private"] = "d";
  Aliases2["extractable"] = "ext";
  return Aliases2;
})(Aliases || {});
var Base64Codecs = /* @__PURE__ */ ((Base64Codecs2) => {
  Base64Codecs2["privateBits"] = "d";
  Base64Codecs2["xBits"] = "x";
  Base64Codecs2["yBits"] = "y";
  return Base64Codecs2;
})(Base64Codecs || {});
var AssertedAliases = /* @__PURE__ */ ((AssertedAliases2) => {
  AssertedAliases2["publicKey"] = "publicBits";
  AssertedAliases2["privateKey"] = "privateBits";
  AssertedAliases2["keyPair"] = "keyPairBits";
  return AssertedAliases2;
})(AssertedAliases || {});
function inferCurve(key, bytes) {
  if (!key.curve) {
    switch (bytes) {
      case 66:
        key.curve = "P-521" /* p521 */;
        break;
      case 48:
        key.curve = "P-384" /* p384 */;
        break;
      case 32:
        key.curve = "P-256" /* p256 */;
        break;
      default:
        throw new KeyInputError(`Cannot infer named curve from key length ${bytes}`);
    }
  }
}
function Key(properties) {
  const that = {};
  for (const key of JWK_KEYS) {
    if (properties[key] !== void 0) that[key] = properties[key];
  }
  function assign(name) {
    const d = Object.getOwnPropertyDescriptor(properties, name);
    if (d && d.value !== void 0) {
      that[name] = d.value;
    }
  }
  Object.entries(Aliases).forEach(([alias, target]) => {
    Object.defineProperty(that, alias, {
      get: () => that[target],
      set: (value) => that[target] = value
    });
    assign(alias);
  });
  Object.entries(Base64Codecs).forEach(([alias, target]) => {
    Object.defineProperty(that, alias, {
      get: () => that[target] !== void 0 && Base64.decode(that[target]),
      set: (value) => that[target] = value === void 0 ? void 0 : Base64.encode(value, true)
    });
    assign(alias);
  });
  Object.entries(Translators).forEach(([name, translator]) => {
    Object.defineProperty(that, name, translator);
  });
  Object.keys(Translators).forEach((name) => assign(name));
  Object.entries(AssertedAliases).forEach(([alias, target]) => {
    Object.defineProperty(that, alias, {
      get: () => {
        const result = that[target];
        if (result === void 0) {
          throw new KeyInputError(`Key field ${target} is not defined`);
        }
        return result;
      },
      set: (value) => {
        that[target] = value;
      }
    });
    assign(alias);
  });
  function derivePublicFromPrivate() {
    if (that.type !== "EC" /* EC */) throw new KeyInputError("EC key type required to compute public point");
    if (!that.private) throw new KeyInputError("EC private key required to compute public point");
    const crv = that.crv;
    let keyLength;
    switch (crv) {
      case "P-256" /* p256 */:
        keyLength = 32;
        break;
      default:
        throw new KeyInputError(`Unsupported elliptic curve ${crv}`);
    }
    const pubKeyBytes = getPublicKey(Bytes.of(that.privateKey));
    const ecKey = Point.fromBytes(pubKeyBytes);
    that.xBits = Bytes.fromBigInt(ecKey.x, keyLength);
    that.yBits = Bytes.fromBigInt(ecKey.y, keyLength);
  }
  if (that.type === "EC" /* EC */) {
    if (that.d) {
      inferCurve(that, that.privateKey.byteLength);
    } else if (that.xBits) {
      inferCurve(that, that.xBits.byteLength);
    }
    if (that.d && (!that.x || !that.y)) {
      derivePublicFromPrivate();
    }
  }
  return that;
}
function PrivateKey(privateKey, options) {
  let priv, pub;
  if (Bytes.isBytes(privateKey)) {
    priv = privateKey;
  } else {
    priv = privateKey.privateKey;
    pub = privateKey.publicKey;
  }
  return Key({
    type: "EC" /* EC */,
    privateKey: priv,
    publicKey: pub,
    ...options
  });
}
function PublicKey(publicKey, options) {
  return Key({
    type: "EC" /* EC */,
    publicKey,
    ...options
  });
}
function SymmetricKey(privateKey, options) {
  return Key({
    type: "oct" /* oct */,
    privateKey,
    ...options
  });
}
((Key2) => {
  function sharedSecretFor(key, peerKey) {
    return Bytes.of(getSharedSecret(Bytes.of(key.privateBits), Bytes.of(peerKey.publicBits))).slice(1, 33);
  }
  Key2.sharedSecretFor = sharedSecretFor;
})(Key || (Key = {}));
export {
  CurveType,
  Key,
  KeyType,
  PrivateKey,
  PublicKey,
  SymmetricKey
};
//# sourceMappingURL=Key.js.map
