Closed bshambaugh closed 2 years ago
I'm waffling on asking. I am opening this issue again to try to gain clarity. Maybe I just don't understand JavaScript well enough.
`const didJWT = require('did-jwt') var u8a = require('uint8arrays'); var EC = require('elliptic').ec;
var ec = new EC('p256');
const value = 'howdy';
function bytesToBase64url(b) { return u8a.toString(b, 'base64url') }
async function callToHSM(value) {
const privateKey = '0x040f1dbf0a2ca86875447a7c010b0fc6d39d76859c458fbe8f2bf775a40ad74a'
const keypairTemp = ec.keyFromPrivate(privateKey)
const buffferMsg = Buffer.from(value)
let hexSig = keypairTemp.sign(buffferMsg)
const xOctet = u8a.fromString(hexSig.r.toString(),'base10');
const yOctet = u8a.fromString(hexSig.s.toString(),'base10');
const hexR = u8a.toString(xOctet,'base16');
const hexS = u8a.toString(yOctet,'base16');
return u8a.fromString(hexR+hexS,'hex');
}
async function JsonWebToken(value) { const signatureBytes = await callToHSM(value) return bytesToBase64url(signatureBytes) }
let signerFour = async function() { await JsonWebToken(value) }
async function JsonWebTokenT() { let jwt = await didJWT.createJWT( { aud: 'did:ethr:0xf3beac30c498d9e26865f34fcaa57dbb935b0d74', exp: 1957463421, name: 'uPort Developer' }, { issuer: 'did:ethr:0xf3beac30c498d9e26865f34fcaa57dbb935b0d74', signerFour }, { alg: 'ES256K' } /// more correct to say alg: 'ES256' , but not supported yet ) console.log(jwt) }
JsonWebTokenT()`
signerFour could be rewritten in some other way.
let signerFour = async function() { await JsonWebToken(value) }
@bshambaugh I'm a bit confused. What are you trying to achieve?
A lot of my code is like: https://github.com/decentralized-identity/did-jwt/blob/master/README.md#1-create-a-did-jwt , except with my own modifications (everything that goes into signerFour) are in place of signer)
Hang on, I'm making a diagram describing my setup.
I am trying to develop a diagram explaining that the private key exists on a device (on the cryptographic co-processor connected by wire the the MCU) that is not the same one as where the javascript is running (On the Node.js server). I believe this is unlike what is normally expected for this library. Normally the private key comes internally by calling elliptic. (e.g.https://github.com/decentralized-identity/did-jwt/blob/master/src/signers/ES256KSigner.ts . ) The closest thing that I could find to this was: https://github.com/decentralized-identity/did-jwt/blob/master/docs/guides/index.md#creating-custom-signers-for-integrating-with-hsm .
Code for Running on Node.js server for Ceramic with key-did-provider-ed25519 (pulled from https://github.com/bshambaugh/BlinkyProject/tree/master/CubeCellandHeltecESP32_try7/esp8266_shop_websockets/CeramicToDo I think):
import KeyResolver from 'key-did-resolver'
import {Ed25519Provider} from 'key-did-provider-ed25519' /// replace with something for P-256, P-384, P-521 .... etc
import {randomBytes} from '@stablelib/random' /// not needed unless used for a nonce or something
// include did-jwt for verifiable cred or other library for nft (ceramic network makes assertions about did:key string)
// this stuff might be a bit beyond the scope
import { CeramicClient } from '@ceramicnetwork/http-client'
const API_URL = "https://ceramic-clay.3boxlabs.com"
const ceramic = new CeramicClient(API_URL)
//
const seed = randomBytes(32) // 32 bytes with high entropy /// this line makes no sense for a remote signer
const provider = new Ed25519Provider(seed) /// replace for provider for P-256, P-384, P-521 etc ... (signer is remote)
const did = new DID({ provider, resolver: KeyResolver.getResolver() })
const auth = await did.authenticate()
console.log('auth is')
console.log(auth) // this spits out the did:key
// see https://github.com/ceramicstudio/idx-assignment and https://github.com/ceramicstudio/js-glaze to create more documents
// create JWS ... specified in eip-2844 interface
const { jws, linkedBlock } = await did.createDagJWS({ hello: 'world' })
// create JWE ... specified in eip-2844 interface
const jwe = await did.createDagJWE({ very: 'secret' }, [did.id])
console.log('jwe is:');
console.log(jwe);
// decrypt JWE ... ... specified in eip-2844 interface
const decrypted = await did.decryptDagJWE(jwe)
console.log('decrypted is:');
console.log(decrypted);
// this stuff may be a bit beyold the scope
ceramic.did = did
ceramic.did.setProvider(provider)
// Contributor Author @bshambaugh bshambaugh 10 days ago `
https://github.com/ceramicnetwork/key-did-provider-ed25519 https://github.com/ceramicnetwork/js-ceramic
Code needed for key-did-provider-p256
Here is some scratch code with comments for key-did-provider-p256 that does not work yet: https://github.com/bshambaugh/key-did-provider-p256/blob/master/src/index.ts
https://github.com/bshambaugh/key-did-provider-p256/blob/master/src/index.ts#L40-L45
const kid = `${did}#${did.split(':')[2]}`
// need remote signer here as well: https://github.com/decentralized-identity/did-jwt/blob/cebf2e6f255e559a1275bb97b35146ce72ce27f5/docs/guides/index.md#creating-custom-signers-for-integrating-with-hsm
const signer = ES256Signer(u8a.toString(secretKey, B64)) // look at did-jwt tests to find what ES256Signer requires
const header = toStableObject(Object.assign(protectedHeader, { kid, alg: 'EdDSA' }))
return createJWS(typeof payload === 'string' ? payload : toStableObject(payload), signer, header)
https://github.com/bshambaugh/key-did-provider-p256/blob/master/src/index.ts#L62-L67
did_createJWS: async ({ did, secretKey }, params: CreateJWSParams & { did: string }) => {
const requestDid = params.did.split('#')[0]
if (requestDid !== did) throw new RPCError(4100, `Unknown DID: ${did}`)
const jws = await sign(params.payload, did, secretKey, params.protected)
return { jws: toGeneralJWS(jws) }
},
It's confusing to think of all the layers at once. Try to eliminate ceramic and anything else that is not absolutely essential from the picture at first.
The remote signer gets a bunch of data to be signed and return a signature. That's it. And that is what const signatureBytes = await call.to.your.HSM.backend(data)
means in the instructions for creating remote signers
As long as you can send some data to your HSM/arduino/etc and get some other data back, you have the premise to create a remote signer.
The private key will be stored on your arduino, and it is there that you will have to implement the signing logic, but that is way outside the scope of this library.
I hope this clarifies some things for you. Good luck with your implementation!
Okay, it turns out I was having trouble seeing the forest through the trees. This link isn't how I discovered the error in my thinking, but it does illustrate what should be in createJWT: https://github.com/decentralized-identity/did-jwt/blob/master/docs/guides/index.md#parameters
Here is the code that I have settled on:
const didJWT = require('did-jwt')
var u8a = require('uint8arrays');
var EC = require('elliptic').ec;
var ec = new EC('p256');
const value = 'howdy';
function bytesToBase64url(b: Uint8Array) {
return u8a.toString(b, 'base64url')
}
async function callToHSM(value: string) {
const privateKey = '0x040f1dbf0a2ca86875447a7c010b0fc6d39d76859c458fbe8f2bf775a40ad74a'
const keypairTemp = ec.keyFromPrivate(privateKey)
const buffferMsg = Buffer.from(value)
let hexSig = keypairTemp.sign(buffferMsg)
const xOctet = u8a.fromString(hexSig.r.toString(),'base10');
const yOctet = u8a.fromString(hexSig.s.toString(),'base10');
const hexR = u8a.toString(xOctet,'base16');
const hexS = u8a.toString(yOctet,'base16');
return u8a.fromString(hexR+hexS,'hex');
}
async function JsonWebToken(value: string) {
const signatureBytes = await callToHSM(value)
return bytesToBase64url(signatureBytes)
}
/*
let signerFour = async () => { await JsonWebToken(value) }
console.log(signerFour);
*/
let signer = async () => { await JsonWebToken(value) }
/*
const signer = didJWT.ES256KSigner(didJWT.hexToBytes('278a5de700e29faae8e40e366ec5012b5ec63d36ec77e8a2417154cc1d25383f'))
console.log(signer)
*/
async function JsonWebTokenT() {
let jwt = await didJWT.createJWT(
{ aud: 'did:ethr:0xf3beac30c498d9e26865f34fcaa57dbb935b0d74', exp: 1957463421, name: 'uPort Developer' },
{ issuer: 'did:ethr:0xf3beac30c498d9e26865f34fcaa57dbb935b0d74', signer }//,
// { alg: 'ES256' }
)
console.log(jwt)
}
JsonWebTokenT()
Of course the code for callToHSM will be different. Most of the logic as you said will be on the device.
Is there are proper way to set up a remote signer?
Here is a gist where I have been experimenting: https://gist.github.com/bshambaugh/ef2aee85fa5ae0091b3433ba26c6234b
I asked the following question in context of the gist. Is there a way to substitute signerTwo in place of signer in createJWT?