all files / src/ ssh-util.ts

96.1% Statements 74/77
75% Branches 6/8
94.12% Functions 16/17
96% Lines 72/75
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133              2783×                   1749×       10× 10× 40×   10×     24× 24× 45×   24×     17×                     14×     14×                               14× 14× 12× 12×     14×              
/* eslint no-bitwise: 0 */
/* global base64urlDecode */
import {base64urlDecode} from './base64url';
 
// TODO: any
function arrayToString(a: any[]) {
  return String.fromCharCode.apply(null, a);
}
 
export function stringToArray(s: string) {
  // TODO: any
  return s.split("").map(c => (c as any).charCodeAt());
}
 
export function base64urlToArray(s: string) {
  return stringToArray(base64urlDecode(s));
}
 
function pemToArray(pem: string) {
  return stringToArray(window.atob(pem));
}
 
// TODO: any
function arrayToPem(a: any) {
  // TODO: any
  return window.btoa(a.map((c: any) => String.fromCharCode(c)).join(""));
}
 
// TODO: any
export function arrayToLen(a: any[]) {
  let result = 0;
  for (let i = 0; i < a.length; i += 1) {
    result = result * 256 + a[i];
  }
  return result;
}
 
export function integerToOctet(n: number) {
  const result = [];
  for (let i = n; i > 0; i >>= 8) {
    result.push(i & 0xff);
  }
  return result.reverse();
}
 
export function lenToArray(n: number) {
  const oct = integerToOctet(n);
  let i;
  for (i = oct.length; i < 4; i += 1) {
    oct.unshift(0);
  }
  return oct;
}
 
export function decodePublicKey(s: string) {
  const split = s.split(" ");
  const prefix = split[0];
  Iif (prefix !== "ssh-rsa") {
    throw new Error(`Unknown prefix: ${prefix}`);
  }
  const buffer = pemToArray(split[1]);
  const nameLen = arrayToLen(buffer.splice(0, 4));
  const type = arrayToString(buffer.splice(0, nameLen));
  Iif (type !== "ssh-rsa") {
    throw new Error(`Unknown key type: ${type}`);
  }
  const exponentLen = arrayToLen(buffer.splice(0, 4));
  const exponent = buffer.splice(0, exponentLen);
  const keyLen = arrayToLen(buffer.splice(0, 4));
  const key = buffer.splice(0, keyLen);
  return { type, exponent, key, name: split[2] };
}
 
// TODO: any
export function checkHighestBit(v: any[]) {
  if (v[0] >> 7 === 1) {
    // add leading zero if first bit is set
    v.unshift(0);
  }
  return v;
}
 
// TODO: any
function jwkToInternal(jwk: any) {
  return {
    type: "ssh-rsa",
    exponent: checkHighestBit(stringToArray(base64urlDecode(jwk.e))),
    name: "name",
    key: checkHighestBit(stringToArray(base64urlDecode(jwk.n))),
  };
}
 
// TODO: any
export function encodePublicKey(jwk: any, name: string) {
  const k = jwkToInternal(jwk);
  k.name = name;
  const keyLenA = lenToArray(k.key.length);
  const exponentLenA = lenToArray(k.exponent.length);
  const typeLenA = lenToArray(k.type.length);
  // TODO: remove ignore
  // @ts-ignore
  const array = [].concat((typeLenA as any), stringToArray(k.type), exponentLenA, k.exponent, keyLenA, k.key);
  const encoding = arrayToPem(array);
  return `${k.type} ${encoding} ${k.name}`;
}
 
export function asnEncodeLen(n: number) {
  let result = [];
  if (n >> 7) {
    result = integerToOctet(n);
    result.unshift(0x80 + result.length);
  } else {
    result.push(n);
  }
  return result;
}
 
// TODO: any
export function encodePrivateKey(jwk: any) {
  const order = ["n", "e", "d", "p", "q", "dp", "dq", "qi"];
  const list = order.map(prop => {
    const v = checkHighestBit(stringToArray(base64urlDecode(jwk[prop])));
    const len = asnEncodeLen(v.length);
    return [0x02].concat(len, v); // int tag is 0x02
  });
  let seq = [0x02, 0x01, 0x00]; // extra seq for SSH
  seq = seq.concat(...list);
  const len = asnEncodeLen(seq.length);
  const a = [0x30].concat(len, seq); // seq is 0x30
  return arrayToPem(a);
}