Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an exportable option to default-plugins #109

Merged
merged 11 commits into from
Mar 15, 2024
21 changes: 16 additions & 5 deletions packages/default-plugins/src/ed25519/keypair.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as ed25519 from "@stablelib/ed25519"
import * as crypto from "./crypto.js"

import { DidableKey, Encodings, ExportableKey } from "@ucans/core"
import { PrivateKeyJwk } from "../types.js"


export class EdKeypair implements DidableKey, ExportableKey {
Expand All @@ -28,7 +29,7 @@ export class EdKeypair implements DidableKey, ExportableKey {
}

static fromSecretKey(key: string, params?: {
format?: Encodings,
format?: Encodings
exportable?: boolean
}): EdKeypair {
const { format = "base64pad", exportable = false } = params || {}
Expand All @@ -45,17 +46,27 @@ export class EdKeypair implements DidableKey, ExportableKey {
return ed25519.sign(this.secretKey, msg)
}

async export(format: Encodings = "base64pad"): Promise<string> {
async export(): Promise<PrivateKeyJwk> {
if (!this.exportable) {
throw new Error("Key is not exportable")
}

return uint8arrays.toString(this.secretKey, format)
const jwk: PrivateKeyJwk = {
kty: "EC",
crv: "Ed25519",
d: uint8arrays.toString(this.secretKey, "base64pad"),
}
return jwk
}
matheus23 marked this conversation as resolved.
Show resolved Hide resolved

static async import(secretKey: string, params?: { exportable: boolean }): Promise<EdKeypair> {
static async import(jwk: PrivateKeyJwk, params?: { exportable: boolean }): Promise<EdKeypair> {
const { exportable = false } = params || {}
return EdKeypair.fromSecretKey(secretKey, { exportable })

if (jwk.kty !== "EC" || jwk.crv !== "Ed25519") {
throw new Error("Cannot import key of type: ${jwk.kty} curve: ${jwk.crv} into ED25519 key")
}

return EdKeypair.fromSecretKey(jwk.d, { exportable })
}
}

Expand Down
4 changes: 2 additions & 2 deletions packages/default-plugins/src/p256/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ export const importKeypairJwk = async (

export const exportPrivateKeyJwk = async (
keyPair: AvailableCryptoKeyPair
): Promise<JsonWebKey> => {
return await webcrypto.subtle.exportKey("jwk", keyPair.privateKey)
): Promise<PrivateKeyJwk> => {
return await webcrypto.subtle.exportKey("jwk", keyPair.privateKey) as PrivateKeyJwk
}

export const exportKey = async (key: CryptoKey): Promise<Uint8Array> => {
Expand Down
8 changes: 3 additions & 5 deletions packages/default-plugins/src/p256/keypair.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { webcrypto } from "one-webcrypto"
import * as uint8arrays from "uint8arrays"
import { DidableKey, Encodings, ExportableKey } from "@ucans/core"
import { DidableKey, ExportableKey } from "@ucans/core"

import * as crypto from "./crypto.js"
import {
Expand Down Expand Up @@ -66,11 +64,11 @@ export class EcdsaKeypair implements DidableKey, ExportableKey {
return await crypto.sign(msg, this.keypair.privateKey)
}

async export(format: Encodings = "base64pad"): Promise<string> {
async export(): Promise<PrivateKeyJwk> {
if (!this.exportable) {
throw new Error("Key is not exportable")
}
return JSON.stringify(await crypto.exportPrivateKeyJwk(this.keypair))
return await crypto.exportPrivateKeyJwk(this.keypair)
}

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/default-plugins/src/rsa/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const importKey = async (key: Uint8Array): Promise<CryptoKey> => {
}

export const importKeypairJwk = async (
privKeyJwk: JsonWebKey,
privKeyJwk: PrivateKeyJwk,
exportable = false
): Promise<AvailableCryptoKeyPair> => {
const privateKey = await webcrypto.subtle.importKey(
Expand Down
13 changes: 4 additions & 9 deletions packages/default-plugins/src/rsa/keypair.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import { webcrypto } from "one-webcrypto"
import * as uint8arrays from "uint8arrays"

import * as crypto from "./crypto.js"
import { AvailableCryptoKeyPair, PrivateKeyJwk, isAvailableCryptoKeyPair } from "../types.js"
import { DidableKey, Encodings, ExportableKey } from "@ucans/core"
import { PrivateKeyInput } from "crypto"
import { DidableKey, ExportableKey } from "@ucans/core"


export class RsaKeypair implements DidableKey, ExportableKey {
Expand Down Expand Up @@ -42,15 +38,14 @@ export class RsaKeypair implements DidableKey, ExportableKey {
return await crypto.sign(msg, this.keypair.privateKey)
}

async export(format: Encodings = "base64pad"): Promise<string> {
async export(): Promise<PrivateKeyJwk> {
if (!this.exportable) {
throw new Error("Key is not exportable")
}
const exported = await crypto.exportPrivateKeyJwk(this.keypair)
return JSON.stringify(exported)
return await crypto.exportPrivateKeyJwk(this.keypair) as PrivateKeyJwk
}

static async importFromJwk(jwk: JsonWebKey, params: { exportable: true }): Promise<RsaKeypair> {
static async importFromJwk(jwk: PrivateKeyJwk, params: { exportable: true }): Promise<RsaKeypair> {
const { exportable = false } = params || {}
const keypair = await crypto.importKeypairJwk(jwk, exportable)

Expand Down
11 changes: 9 additions & 2 deletions packages/default-plugins/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,15 @@ export interface AvailableCryptoKeyPair {
export type PublicKeyJwk = {
kty: string
crv: string
x: string
y: string

// For P256 curves
x?: string
y?: string

// For RSA curves
n?: string
e?: string

}

export type PrivateKeyJwk = PublicKeyJwk & { d: string }
Expand Down
15 changes: 8 additions & 7 deletions packages/default-plugins/tests/ecdsa.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as uint8arrays from 'uint8arrays'
import * as uint8arrays from "uint8arrays"
import { p256Plugin } from "../src/p256/plugin.js"
import EcdsaKeypair from "../src/p256/keypair.js"

Expand Down Expand Up @@ -63,8 +63,8 @@ describe("ecdsa did:key", () => {
})

describe("import and exporting a key", () => {
let exportableKeypair: EcdsaKeypair;
let nonExportableKeypair: EcdsaKeypair;
let exportableKeypair: EcdsaKeypair
let nonExportableKeypair: EcdsaKeypair

beforeAll(async () => {
exportableKeypair = await EcdsaKeypair.create({ exportable: true })
Expand All @@ -73,19 +73,20 @@ describe("import and exporting a key", () => {

it("can export a key using jwk", async () => {
const exported = await exportableKeypair.export()
expect(exported.length).toBeGreaterThan(0)
expect(exported.kty).toBe("EC")
expect(exported.crv).toBe("P-256")
})

it("won't export a non exportable keypar", async () => {
await expect(nonExportableKeypair.export())
.rejects
.toThrow('Key is not exportable')
.toThrow("Key is not exportable")
})

it('Can export a key and re-import from it', async () => {
it("Can export a key and re-import from it", async () => {
const exported = await exportableKeypair.export()

const jwk = JSON.parse(exported)
const jwk = exported
const newKey = await EcdsaKeypair.import(jwk)

const msg = uint8arrays.fromString("test message", "utf-8")
Expand Down
2 changes: 1 addition & 1 deletion packages/default-plugins/tests/rsa.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ describe("ASN", () => {

it("can import an exported key", async () => {
const exported = await exportableKey.export()
const newKey = await RsaKeypair.import(JSON.parse(exported))
const newKey = await RsaKeypair.import(exported)

expect(newKey.did()).toEqual(exportableKey.did())

Expand Down
Loading