RSA 暗号の基本原理

ユークリッドの互除法メニュー【合同式・modの演算・modの逆元・高速な累乗の計算・RSA暗号の基本原理】

【合同式】コード

reader.on('close', () => {
    const [N, A] = lines[0].split(' ').map(Number);
    const surplus = A % N;
    for (let i = 1; i <= 100000; i++) {  // 1〜100000まで 順番に…
      if (i % N === surplus) {  // i÷N の剰余と A÷N の剰余が同じなら
         console.log(i);  // iを表示
      }
   }
});
hogeちゃんの画像

剰余が一致するかどうか 順番に演算しています。

【mod の演算】コード 1/2《C++の実装例参照》

reader.on('close', () => {
   const N = Number(lines[0]);
   const [a, cal, b] = lines[1].split(' ');
   const [A, B] = [a, b].map(Number);  // a, b を数値に変換して A, B に代入
   let ans = 1;  // A[cal]B (mod N)
   switch (cal) {
   case '+':
      ans = (A % N + B % N) % N;
      break;
   case '-':
      ans = (A % N - B % N + N) % N;
      break;
   case '*':
      ans = (A % N) * (B % N) % N;
      break;
   case '^':
      for (let i = 0; i < B; i++){
         ans *= A;
         ans %= N;
      }
      break;
   }
   console.log(ans);
});
hogeちゃんの画像

A[cal]B (mod N) の計算でした。C++の実装例を参照しました。

cal が ‘-‘ の場合 A % NB % N が負の数になると演算結果がマイナスになるので N 加算してから剰余を計算しないとダメみたいね。

【mod の演算】コード 2/2 《数式の通り計算すると50点》

reader.on('close', () => {
   const N = Number(lines[0]);
   const [a, cal, b] = lines[1].split(' ');
   const [A, B] = [a, b].map(Number);
   let ans = 0;  // A[cal]B (mod N)
   switch (cal) {
   case '+':
      ans = (A + B) % N;
      break;
   case '-':
      ans = (A - B) % N;
      break;
   case '*':
      ans = (A * B) % N;
      break;
   case '^':
      ans = (A ** B) % N;
      break;
   }
   console.log(ans);
});
hogeちゃんの画像

イマイチ 納得できなかったので 数式の流れに沿ってコードを書くと (演算結果が負の数になるケース) と (大きな数値になるケース) は ☓ でした。

【mod の逆元】コード《拡張ユークリッドの互除法》

reader.on('close', () => {
   const extgcd = (a, b) => {  // 拡張ユークリッドの互除法
      if (b) {
         const [y, x] = extgcd(b, a % b);
         return [x, y - Math.floor(a / b) * x];
      }
      return [1, 0];
   };
   const [M, A] = lines[0].split(' ').map(Number);
   console.log((extgcd(M, A)[1] + M) % M);
});
hogeちゃんの画像

A × N = 1(mod M) となる 1 以上 M 未満の整数 N を 「mod M での A の mod 逆元」といい A^{-1} (mod M) と書く…そうです。

mod M での A の mod 逆元を求めるには、 x , y についての 1 次方程式 Ax + My = 1 の 解 x が分かれば良い…。

前出の 拡張ユークリッドの互除法  のコード3 の function ectgcd(a, b)を用いて求めています。

【高速な累乗の計算】コード1/3《for文で計算》

reader.on('close', () => {
   const [A, B, M] = lines[0].split(' ').map(Number);
   let ans = 1;
   for (let i = 0; i < B; i++){  // A ^ B(mod M)
      ans *= A;
      ans %= M;
   }
   console.log(ans);
});
hogeちゃんの画像
for文では 計算量オーバーになるかと思ったけど 通過できました。

【高速な累乗の計算】コード 2/3《繰り返し二乗法》

// A^B(mod M)
reader.on('close', () => {
   const pow = (a, b, m) => {
      b = b.toString(2).split('').map(Number);
      ans = 1;
      while (b[0]) {
         if (b.pop()) {
            ans = (ans * a) % m;
         }
         a = (a * a) % m;
      }
      return ans;
   };
   const [A, B, M] = lines[0].split(' ').map(Number);
   console.log(pow(A, B, M));
});
hogeちゃんの画像
シフト演算子 使ったことがなかったので b を 2進数の文字列に変換してから 更に数値化した配列に変換しました。

【高速な累乗の計算】コード 3/3《C++ の実装例参照》

reader.on('close', () => {
   const pow = (a, b, m) => {
      ans = 1;
      while (b > 0) {
         if (b & 1) {
            ans = (ans * a) % m;
         }
         a = (a * a) % m;
         b >>= 1;
      }
      return ans;
   };
   const [A, B, M] = lines[0].split(' ').map(Number);
   console.log(pow(A, B, M));
});
hogeちゃんの画像
C++ の実装例を書き換えましたが C++ と同じ演算子で OKでした。

【RSA 暗号の基本原理】コード

reader.on('close', () => {
   const extgcd = (a, b) => {  // 拡張ユークリッドの互除法
      if (b) {
         let [y, x] = extgcd(b, a % b);
         y -= Math.floor(a / b) * x;
         return [x, y];
      }
      return [1, 0];
   };

   const pow = (a, b, m) => {
      let ans = BigInt(1);
      while (b > 0) {
         if (b & 1) {
            ans = (ans * a) % m;
         }
         a = (a * a) % m;
         b >>= 1;
      }
      return Number(ans);
   };
   const [p, q, e, M] = lines[0].split(' ').map(Number);
   const [n, m] = [p * q, (p - 1) * (q - 1)];
   const d = (extgcd(m, e)[1] + m) % m;
   console.log(d);
   const E = pow(BigInt(M), e, BigInt(n));
   console.log(E);
   console.log(pow(BigInt(E), d, BigInt(n)));
});
hogeちゃんの画像

メニューの最初からやり直して やっと何とかできたって感じ。演算でBigInt()使うのって 久しぶり。

【RSA 暗号の基本原理】コード 2 《BigInt()使用せずエラー》

reader.on('close', () => {
   const extgcd = (a, b) => {  // 拡張ユークリッドの互除法
      if (b) {
         let [y, x] = extgcd(b, a % b);
         y -= Math.floor(a / b) * x;
         return [x, y];
      }
      return [1, 0];
   };
   
   const pow = (a, b, m) => {
      ans = 1;
      while (b > 0) {
         if (b & 1) {
            ans = (ans * a) % m;
         }
         a = (a * a) % m;
         b >>= 1;
      }
      return ans;
   };
   const [p, q, e, M] = lines[0].split(' ').map(Number);
   const [n, m] = [p * q, (p - 1) * (q - 1)];

   d = (extgcd(m, e)[1] + m) % m;
   console.log(d);
   E = pow(e, M, n) % n;
   console.log(E);
   console.log(pow(E, d, n));
});
hogeちゃんの画像

ほぼ正解だと思うのですが 0点 でした。BigInt() を使わないとダメだったのか…。

コメント