jwkからdid:key を作ってみる

単純なdid記述としてdid:keyというmethodがある。その仕様書は[The did:key Method v0.7](https://w3c-ccg.github.io/did-method-key)にあり、具体的には、以下のように書けるそうだ。
```
did:key:zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N
```
これが何を意味しているかは、[universal resolver](https://dev.uniresolver.io/)に放り込むとわかるそうで、放り込んだ結果は以下のとおりである。
```
EcdsaSecp256k1VerificationKey2019
{
　"kty":"EC",
　"crv":"secp256k1",
 　"x":"tS0TJpT9-UUpJvjMZUyA0C0oI9l7VW8d2ADptYRJVdM",
   "y":"RQEb5Z7oO52oHNpYk9lbbuwZmA_GFNenqSjX4joDh-A"
}
```
この変換に関する理論的な解説は、以下の記事に詳しいものの、具体的にsecp256k1 jwkからdid:keyを算出方法が知りたかったので調べてみた。
　　[DID Method 探訪：The did:key Method](https://qiita.com/kazuhideYS/items/598b0f8da19deacf57ca)

上記記事によると、base58を使う場合のkey formatは下記の通りになるとのこと。
```
"did:key:" + "z" + base58(multicodec + pubKey)
```
### step1 multicodeを決定
　まずsecp256k1 jwkから、did:keyを算出するためには、[The did:key Method v0.7](https://w3c-ccg.github.io/did-method-key)仕様に従って、multicodeを決める必要がある。下記の表に従えば、secp256k1は、```0xe7```である。これをunsigned variant表記にすると```0xe701```となる。
 ![multicode.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624321/816b87a1-76a6-8ec3-de0f-8f8efd05dd17.png)

### step2 pubkeyを決定
　```0xe7```の定義にあるように、pukeyには、compressedのkey formatを記載すればいいことがわかる。compressedのkey formatに関しては、「[learn me a bitcoin](https://learnmeabitcoin.com/technical/public-key)」に詳しいが、簡単にいえばy値がoddのときは0x03+x値, y値がevenのときは、0x02+x値となる。
　上記のsecp256k1 jwkをhex表記に変更
 ```
 　"x":"tS0TJpT9-UUpJvjMZUyA0C0oI9l7VW8d2ADptYRJVdM"
   "y":"RQEb5Z7oO52oHNpYk9lbbuwZmA_GFNenqSjX4joDh-A"
 → 
   "x": 0xB52D132694FDF9452926F8CC654C80D02D2823D97B556F1DD800E9B5844955D3 
   "y": 0x45011BE59EE83B9DA81CDA5893D95B6EEC19980FC614D7A7A928D7E23A0387E0

 ```
"y"値が偶数なので、compressed なkey formatは次のようになる
```
   0x02 B52D132694FDF9452926F8CC654C80D02D2823D97B556F1DD800E9B5844955D3
``` 

### step3 did:keyを作る
 secp256k1のdid:key formatにstep1, step2の結果を入れると以下のようになる
 ```
   did:key: + "z" + base58("E701"+ 
        "02B52D132694FDF9452926F8CC654C80D02D2823D97B556F1DD800E9B5844955D3")
 ```
 base58エンコードすると以下のようになり、仕様書に書かれていたsample値(```did:key:zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N```)と一致する
 ```
 did:key: + "z" + Q3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N
 ```

 ## サンプルコード
 上記ステップをコーディングし、動作を確認してみた
 ### 実行
 ```
 node server.js
 →
 --------generate key for did -------------------
 d : XoxqzQ/r9P/XCu/RdL8cx/x94boZuoFf0izq//RevN4=
 x (b64): BzYnIwfodS6WVAIc3JTbNfPOqNUxz9s3d+JpdEBVckM=
 y (b64): OOaOapbUUCEzQJaXhF9CBovBB3pLkQIGUE1/W4Gkyzk=
-----------------------------

did -> did:key:zQ3shf8FsfJSvDGmM4GNmhgNocBQfUkEZwAAHTCNb7Kkicpw4
 ```

 ``` server.js
const crypto = require('crypto');
const elliptic = require('elliptic');
const base58 = require('bs58');

let did = createId();
console.log("did -> "+did)


function createId(){
  const key = crypto.randomBytes(32).toString("hex");
  const ec = new elliptic.ec('secp256k1');
  const prv = ec.keyFromPrivate(key, 'hex');
  const pub = prv.getPublic();
  const ecjwk = {
    kty:"EC", 
    crv:"secp256k1", 
    "x":pub.x.toBuffer().toString('base64'),
    "y":pub.y.toBuffer().toString('base64')
  };
  const ecjwkpri = {
    kty:"EC", 
    crv:"secp256k1", 
    "x":pub.x.toBuffer().toString('base64'),
    "y":pub.y.toBuffer().toString('base64'),
    "d":prv.getPrivate().toBuffer().toString('base64')
  };
  console.log("--------generate key for did -------------------")
  console.log(` d : ${prv.getPrivate().toBuffer().toString('base64')}`);
  console.log(` x (b64): ${pub.x.toBuffer().toString('base64')}`);
  console.log(` y (b64): ${pub.y.toBuffer().toString('base64')}`);
  console.log("-----------------------------\n")
  let _key={"publicJwk":ecjwk,"privateJwk":ecjwkpri};
  let yhex = pub.y.toBuffer().toString('hex')
  if ((parseInt(yhex.slice( -1 ), 16) %2)==1){
    parity_flag = "03";
  }else{
    parity_flag = "02";
  }
  let tmp = "E701"+parity_flag+pub.x.toBuffer().toString('hex');
  let _did = "did:key:z"+base58.encode(Buffer.from(tmp, 'hex'));
  return _did;
}
 ```