質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.50%

  • C

    4277questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

gcry_mpi_t 内の公開鍵をビット列で取り出したい

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 270

yamahiroto

score 1

 gcry_mpi_t 内の公開鍵をビット列で取り出したい

libgcrypt1.7.6 を使ってRSA暗号をいじってく中で,
gcry_mpi_t型におさまっている公開鍵をビット列で取り出したいです.
(libgcrypt1.7.6を対象とするテスト用なのでこのバージョンで実行したいです)

char型の鍵をgcry_mpi_t型に入れることはできるんですが,その逆がうまくいきません.
つまりgcry_mpi_t型の公開鍵eをchar型にして取り出すとなぜか文字列が変わります.

発生している問題・エラーメッセージ

公開鍵eについてサンプル値「0x010001」を使って,一度gcry_mpi_t型に変えた後.
再びchar型として取り出すとなぜか「0x000001」に代わってしまいます.

※ちなみにchar型からgcry_mpi_t型に戻すとこれは正しく「0x010001」になってます.

該当のソースコード

#include "gcry.h"

/* A sample 2048 bit RSA key used for the selftests (public only).  */
static const char sample_public_key[] =
" (public-key"
"  (rsa"
"   (n #009F56231A3D82E3E7D613D59D53E9AB921BEF9F08A782AED0B6E46ADBC853EC"
"       7C71C422435A3CD8FA0DB9EFD55CD3295BADC4E8E2E2B94E15AE82866AB8ADE8"
"       7E469FAE76DC3577DE87F1F419C4EB41123DFAF8D16922D5EDBAD6E9076D5A1C"
"       958106F0AE5E2E9193C6B49124C64C2A241C4075D4AF16299EB87A6585BAE917"
"       DEF27FCDD165764D069BC18D16527B29DAAB549F7BBED4A7C6A842D203ED6613"
"       6E2411744E432CD26D940132F25874483DCAEECDFD95744819CBCF1EA810681C"
"       42907EBCB1C7EAFBE75C87EC32C5413EA10476545D3FC7B2ADB1B66B7F200918"
"       664B0E5261C2895AA28B0DE321E921B3F877172CCCAB81F43EF98002916156F6CB#)"
"   (e #010001#)))";


int main(int argc, char** argv){
  gcry_error_t err = 0;
  gcry_sexp_t pkey = NULL;


  /* Covert the S-expressions into the internal representation. */
  err = gcry_sexp_sscan(&pkey, NULL,
                         sample_public_key, strlen(sample_public_key));
  if (err){
    xerr("failed to scan sample key.");
    return 1;
  }


  /* pkey compornents */
  gcry_sexp_t e_pkey = gcry_sexp_find_token(pkey, "e", 0);
  gcry_mpi_t expo_pkey = gcry_sexp_nth_mpi(e_pkey, 1, GCRYMPI_FMT_USG);


  /*Dump MPIs */
  printf("### Dump MPIs ###\n");
  printf("pkey expo\n");
  gcry_mpi_dump(expo_pkey);
  printf("\n");


  /* bit length */
  printf("### bit length ###\n");
  unsigned int expo_len = gcry_mpi_get_nbits(expo_pkey);
  printf("expo_pkey :\t%d\n", expo_len);


  /* mpi_print */
  printf("### mpi print command ###\n");
  printf("mpi_print\n");
  unsigned char* expo;
  expo = (unsigned char*)malloc(3);
  if (expo == NULL){
    printf("expo is NULL\n");
    return 1;
  }
  err = gcry_mpi_print(GCRYMPI_FMT_USG, expo, 3, NULL, expo_pkey);
  if (err){
    xerr("failed: expo MPI_print");
  }


  /* check print */
  gcry_mpi_t res;
  size_t size;
  err = gcry_mpi_scan(&res, GCRYMPI_FMT_USG,
                        expo, 3, &size);
  if (err){
    xerr("failed to scan res");
  }
  printf("check mpi print\n");
  gcry_mpi_dump(res);
  printf("\n");
  // bit print
  printf("\n");
  printf("### bit sequance ###\n");
  printf("%4u\t%02x\n", *expo, *expo);
  for (int i = expo_len-1; i >= 0; i--){
    printf("%d", (*(expo)>>i) & 1);
    if (i % 4 == 0){
      printf(" ");
    }
  }
  printf("\n");
}

出力はこんな感じです↓↓

### Dump MPIs ###
pkey expo
010001 //これが正しい鍵値
### bit length ###
expo_pkey:      17 //ビット長
### mpi print command ###
mpi_print
check mpi print
010001 //char型で取り出した鍵をもう一度gcry_mpi_t型に戻してdumpしたもの

### bit sequance ###
   1    01
0 0000 0000 0000 0001 // 最上位ビットが「1」のはずだがならない
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

再びchar型として取り出すとなぜか「0x000001」に代わってしまいます.

変わっている訳ではなく、3バイトのexpoをダンプする以下のコードに問題があります。

  printf("%4u\t%02x\n", *expo, *expo);
  for (int i = expo_len-1; i >= 0; i--){
    printf("%d", (*(expo)>>i) & 1);
    if (i % 4 == 0){
      printf(" ");
    }
  }


expoはmalloc(3)で割り当てられたunsigned charの3バイトの配列ですが、*(expo)としている為、expo[0], expo[1], expo[2] と、ビット位置に応じて正しく参照できていません。

以下のコードにすることで、正しくダンプできます。(3バイトのビッグエンディアンを前提としています)

#define EXPO_LEN 3

  for (int i = expo_len - 1; i >= 0; i--) {

    // ビット位置に応じて、参照すべきexpo[n]のインデックスnを求める。
    int array_index = EXPO_LEN - ((i + 8) / 8);

    // 参照すべきビットの位置を求める。
    int bit_index = i % 8;

    // 参照すべきexpo[n]を取得
    unsigned char target_byte = *(expo + array_index);
//    printf("%d: %02X, bit_index=%d\n", array_index, target_byte, bit_index);

    int bit = (((target_byte >> bit_index) & 1) != 0) ? 1: 0;
    printf("%d", bit);
    if (i % 4 == 0) {
      printf(" ");
    }
  }

一応、テストしたコードを全掲載します。gcry.h とxerr()関数はおそらく質問者様が
独自に用意されたものだと思いましたので、改変しています。

//sample.c
//#include "gcry.h"
#include "gcrypt.h"
#define xerr(v)

int test3(void);

int main(int argc, char** argv){
  test3();
  return 0;
}

#define EXPO_LEN  3

/* A sample 2048 bit RSA key used for the selftests (public only).  */
static const char sample_public_key[] =
" (public-key"
"  (rsa"
"   (n #009F56231A3D82E3E7D613D59D53E9AB921BEF9F08A782AED0B6E46ADBC853EC"
"       7C71C422435A3CD8FA0DB9EFD55CD3295BADC4E8E2E2B94E15AE82866AB8ADE8"
"       7E469FAE76DC3577DE87F1F419C4EB41123DFAF8D16922D5EDBAD6E9076D5A1C"
"       958106F0AE5E2E9193C6B49124C64C2A241C4075D4AF16299EB87A6585BAE917"
"       DEF27FCDD165764D069BC18D16527B29DAAB549F7BBED4A7C6A842D203ED6613"
"       6E2411744E432CD26D940132F25874483DCAEECDFD95744819CBCF1EA810681C"
"       42907EBCB1C7EAFBE75C87EC32C5413EA10476545D3FC7B2ADB1B66B7F200918"
"       664B0E5261C2895AA28B0DE321E921B3F877172CCCAB81F43EF98002916156F6CB#)"
"   (e #010001#)))";

int test3(void) {
  gcry_error_t err = 0;
  gcry_sexp_t pkey = NULL;


  /* Covert the S-expressions into the internal representation. */
  err = gcry_sexp_sscan(&pkey, NULL,
                         sample_public_key, strlen(sample_public_key));
  if (err){
    xerr("failed to scan sample key.");
    return 1;
  }


  /* pkey compornents */
  gcry_sexp_t e_pkey = gcry_sexp_find_token(pkey, "e", 0);
  gcry_mpi_t expo_pkey = gcry_sexp_nth_mpi(e_pkey, 1, GCRYMPI_FMT_USG);


  /*Dump MPIs */
  printf("### Dump MPIs ###\n");
  printf("pkey expo\n");
  gcry_mpi_dump(expo_pkey);
  printf("\n");


  /* bit length */
  printf("### bit length ###\n");
  unsigned int expo_len = gcry_mpi_get_nbits(expo_pkey);
  printf("expo_pkey :\t%d\n", expo_len);


  /* mpi_print */
  printf("### mpi print command ###\n");
  printf("mpi_print\n");
  unsigned char* expo;
  expo = (unsigned char*)malloc(EXPO_LEN);
  if (expo == NULL){
    printf("expo is NULL\n");
    return 1;
  }
  err = gcry_mpi_print(GCRYMPI_FMT_USG, expo, EXPO_LEN, NULL, expo_pkey);
  if (err){
    xerr("failed: expo MPI_print");
  }


  /* check print */
  gcry_mpi_t res;
  size_t size;
  err = gcry_mpi_scan(&res, GCRYMPI_FMT_USG, expo, EXPO_LEN, &size);
  if (err){
    xerr("failed to scan res");
  }
  printf("check mpi print\n");
  gcry_mpi_dump(res);
  printf("\n");
  // bit print
  printf("\n");
  printf("### bit sequance ###\n");
  printf("%4u\t%02x\n", *expo, *expo);

//  printf("%02X, %02X, %02X\n", *(expo + 0), *(expo + 1), *(expo + 2));

  for (int i = expo_len - 1; i >= 0; i--) {

    // ビット位置に応じて、参照すべきexpo[3]のインデックスを求める。
    int array_index = EXPO_LEN - ((i + 8) / 8);

    // 参照すべきビットの位置を求める。
    int bit_index = i % 8;

    // 参照すべきexpo[n]を取得
    unsigned char target_byte = *(expo + array_index);
//    printf("%d: %02X, bit_index=%d\n", array_index, target_byte, bit_index);

    int bit = (((target_byte >> bit_index) & 1) != 0) ? 1: 0;
    printf("%d", bit);
    if (i % 4 == 0) {
      printf(" ");
    }
  }

  printf("\n");
  return 0;
}

Cygwin32ビット上での実行結果です。gcrypt はlibgcrypt1.7.6を用意できなかったので1.7.8 ですが、
今回の件には問題無いと思います。

$ gcc -Wall sample.c -lgcrypt

$ ./a.exe
### Dump MPIs ###
pkey expo
010001
### bit length ###
expo_pkey :     17
### mpi print command ###
mpi_print
check mpi print
010001

### bit sequance ###
   1    01
1 0000 0000 0000 0001

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/11/04 20:17

    回答ありがとうございます!
    無事問題解決ができました.ポインタあたりがまだしっかり理解できていなかったのでとても勉強になりました.

    キャンセル

  • 2017/11/04 21:07

    経過のご報告ありがとうございます。他の例ですが、4バイト迄のデータであれば例えばunsigned longの変数1つにパックして、変数1つでビット演算するほうがループをまわすのも簡単になって良いこともあります。場合に応じてどうぞ。
    例:
    int main() {
    unsigned char array[3] = { 0x81, 0x23, 0x45 };
    unsigned char* expo = array;

    unsigned long ul = *expo << 16 | *(expo + 1) << 8 | *(expo + 2);
    printf("ul=0x%08lX\n", ul);
    return 0;
    }

    実行結果
    ul=0x00812345

    キャンセル

同じタグがついた質問を見る

  • C

    4277questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。