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

Fix Curve capabilities #5

Merged
merged 1 commit into from
Jul 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/components/dids/Embedded/MethodSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
KeyFormat,
Representation,
UsageFormat,
verificationRelationships,
} from "@/types/dids";
import { useState } from "react";
import DidInput from "../DidInput";
Expand Down Expand Up @@ -90,7 +89,7 @@ export default function EmbeddedMethodSettings({
<div>
<span className="opacity-50 font-bold">Used for</span>
<div className="form-control">
{verificationRelationships.map((method, indexMethod) => {
{method.material.curve.capabilities.map((method, indexMethod) => {
return (
<div key={indexMethod} className="flex justify-between w-full">
<div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/dids/Embedded/Summarize.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export default function SummarizeEmbeddedMethod({
{method.id}
</div>

<div><span className="opacity-50">Curve:</span> {method.material.curve}</div>
<div><span className="opacity-50">Curve:</span> {method.material.curve.name.display}</div>
<div className="flex flex-wrap gap-2">
<span className="opacity-50">Usage:</span>{" "}
{usages.length > 0 ? usages : "Unused"}
Expand Down
43 changes: 19 additions & 24 deletions src/components/dids/NewKeyMaterial.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { EmbeddedMaterial } from "@/lib/DidMaterial";
import { decodeJwk, generateKeyPair } from "@/lib/keys";
import { useState } from "react";
import { publicKeyJwkSchema } from "../../lib/didParser";
import { SupportedCurves } from "@/types/dids";
import Link from "next/link";
import { Curve, CurveEd25519, CurveP256, curveFromName, isEd25519, isP256 } from "@/lib/curves";

export default function NewKeyMaterial({
didDocument,
Expand All @@ -13,7 +13,7 @@ export default function NewKeyMaterial({
didDocument: DidDocument;
setMethod: (km: EmbeddedMaterial) => void;
}): JSX.Element {
const [curve, setCurve] = useState<SupportedCurves>("Ed25519");
const [curve, setCurve] = useState<Curve>(CurveEd25519);
const [isGeneratedKey, setIsGeneratedKey] = useState<boolean>(true);
const [importedKeyValidationStatus, setImportedKeyValidationStatus] =
useState<string | undefined>(undefined);
Expand Down Expand Up @@ -86,8 +86,8 @@ export default function NewKeyMaterial({
type="radio"
name="radio-10"
className="radio checked:bg-red-500"
onClick={() => setCurve("Ed25519")}
checked={curve === "Ed25519"}
onClick={() => setCurve(CurveEd25519)}
checked={isEd25519(curve)}
/>
</label>
</div>
Expand All @@ -98,8 +98,8 @@ export default function NewKeyMaterial({
type="radio"
name="radio-10"
className="radio checked:bg-blue-500"
onClick={() => setCurve("P-256")}
checked={curve === "P-256"}
onClick={() => setCurve(CurveP256)}
checked={isP256(curve)}
/>
</label>
</div>
Expand Down Expand Up @@ -147,29 +147,25 @@ export default function NewKeyMaterial({
P-256.
</span>
<span
className={`label-text-alt ${
importedKeyValidationStatus === "Valid" ? "text-success" : ""
} ${
importedKeyValidationStatus &&
importedKeyValidationStatus !== "Valid"
className={`label-text-alt ${importedKeyValidationStatus === "Valid" ? "text-success" : ""
} ${importedKeyValidationStatus &&
importedKeyValidationStatus !== "Valid"
? "text-error"
: ""
}`}
}`}
>
{importedKeyValidationStatus}
</span>
</label>
<textarea
className={`textarea bg-base-300 textarea-bordered w-full h-36 font-mono text-xs ${
importedKeyValidationStatus === "Valid"
? "textarea-success"
: ""
} ${
importedKeyValidationStatus &&
importedKeyValidationStatus !== "Valid"
className={`textarea bg-base-300 textarea-bordered w-full h-36 font-mono text-xs ${importedKeyValidationStatus === "Valid"
? "textarea-success"
: ""
} ${importedKeyValidationStatus &&
importedKeyValidationStatus !== "Valid"
? "textarea-error"
: ""
}`}
}`}
onChange={(e) => {
if (e.target.value === "") {
setImportedKeyValidationStatus(undefined);
Expand All @@ -181,7 +177,7 @@ export default function NewKeyMaterial({
const decoded = decodeJwk(parsedSchema);
setImportedKeyValidationStatus("Valid");
setKeyMaterial(decoded);
setCurve(parsedSchema.crv === "P-256" ? "P-256" : "Ed25519");
setCurve(curveFromName(parsedSchema.crv));
} catch (e) {
setImportedKeyValidationStatus("Invalid key or key format");
}
Expand All @@ -191,9 +187,8 @@ export default function NewKeyMaterial({
)}
</div>
<button
className={`btn btn-block btn-success ${
!keyMaterial ? "btn-disabled" : ""
}`}
className={`btn btn-block btn-success ${!keyMaterial ? "btn-disabled" : ""
}`}
onClick={() => {
completeSetup();
}}
Expand Down
3 changes: 2 additions & 1 deletion src/lib/DidMaterial.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
import { z } from "zod";
import { verificationRelationshipSchema } from "./didParser";
import { deriveIdentificationFragment, encodeJsonWebKey, encodeMultibaseKey } from "./keys";
import { isEd25519 } from "./curves";

export function isEmbeddedType(
vm: EmbeddedType | ReferencedType
Expand Down Expand Up @@ -109,7 +110,7 @@ export class EmbeddedMaterial implements DidMaterial {
return this.id;
}
let serializedKey = "";
if (this.material.curve == "Ed25519") {
if (isEd25519(this.material.curve)) {
if (this.material.format === "JsonWebKey2020") {
return {
id: this.id,
Expand Down
63 changes: 53 additions & 10 deletions src/lib/curves.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,65 @@
export type CurveP256 = "p256" | "P-256"
export type CurveEd25519 = "Ed25519" | "ed25519"
export type NSupportedCurves = CurveP256 | CurveEd25519;
import { VerificationRelationship } from "@/types/dids";

export const isP256 = (curve: NSupportedCurves): curve is CurveP256 => {
return curve === "p256" || curve === "P-256";
export type Curve = {
name: {
display: string,
jwk: string,
elliptic: string,
},
capabilities: VerificationRelationship[]
}

export const isEd25519 = (curve: NSupportedCurves): curve is CurveEd25519 => {
return curve === "Ed25519" || curve === "ed25519";
export const CurveP256: Curve = {
name: {
display: "P-256",
jwk: "P-256",
elliptic: "p256",
},
capabilities: [
"authentication",
"assertionMethod",
"keyAgreement",
"capabilityInvocation",
"capabilityDelegation"
] as VerificationRelationship[]
}

export function curveName<T extends NSupportedCurves>(curve: T, format: "jwk" | "elliptic"): NSupportedCurves {
if (curve === "p256" || curve === "P-256") {
export const CurveEd25519: Curve = {
name: {
display: "Ed25519",
jwk: "Ed25519",
elliptic: "ed25519",
},
capabilities: [
"authentication",
"assertionMethod",
"capabilityInvocation",
"capabilityDelegation"
] as VerificationRelationship[]
}

export const isP256 = (curve: Curve): boolean => {
return curve.name.jwk === "P-256" || curve.name.elliptic === "p256";
}

export const isEd25519 = (curve: Curve): boolean => {
return curve.name.jwk === "Ed25519" || curve.name.elliptic === "ed25519";
}

export function curveFromName(name: string): Curve {
if (name === 'p256' || name === 'P-256') return CurveP256
else if (name === "Ed25519" || name === "ed25519") return CurveEd25519
else throw new Error(`UnsupportedCurveError: ${name}`)
}

export function curveToName(curve: Curve, format: "jwk" | "elliptic"): string {
console.log(curve)
if (isP256(curve)) {
if (format === "elliptic") return "p256"
if (format === "jwk") return "P-256"
}

if (curve === "Ed25519" || curve === "ed25519") {
if (isEd25519(curve)) {
if (format === "elliptic") return "ed25519"
if (format === "jwk") return "Ed25519"
}
Expand Down
27 changes: 13 additions & 14 deletions src/lib/keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ import * as b58 from "multiformats/bases/base58";
import * as b64 from "multiformats/bases/base64";
import {
EmbeddedType,
SupportedCurves,
} from "@/types/dids";
import * as jose from 'jose'
import { NSupportedCurves, curveName, isEd25519, isP256 } from "./curves";
import { Curve, curveFromName, curveToName, isEd25519, isP256 } from "./curves";

export async function deriveIdentificationFragment(
material: EmbeddedType
Expand All @@ -24,8 +23,8 @@ export async function deriveIdentificationFragment(
return `${fragmentDelimiter}${fragment}`
}

export function generateKeyPair(curve: NSupportedCurves): { privateKey: JsonWebKey, publicKey: Uint8Array } {
const ec = new EC(curveName(curve, 'elliptic'))
export function generateKeyPair(curve: Curve): { privateKey: JsonWebKey, publicKey: Uint8Array } {
const ec = new EC(curveToName(curve, 'elliptic'))
const keyPair = ec.genKeyPair();
const publicKey = isP256(curve) ? new Uint8Array(keyPair.getPublic().encode("array", false)) : new Uint8Array(keyPair.getPublic().getX().toArray())
const privateKey = exportPrivateKey(keyPair, curve)
Expand All @@ -39,13 +38,13 @@ export function generateKeyPair(curve: NSupportedCurves): { privateKey: JsonWebK
// GENERIC HELPERS
// -----------------------------------------------------------------------------

function getCryptoSuite(curve: NSupportedCurves) {
const c = curveName(curve, 'elliptic')
if (isP256(c)) {
function getCryptoSuite(curve: Curve) {
const c = curveToName(curve, 'elliptic')
if (isP256(curve)) {
return new EC(c);
} else if (isEd25519(c)) {
} else if (isEd25519(curve)) {
return new EdDSA(c);
} else throw Error(`CryptographyError: Unsupported curve: ${curve}`)
} else throw Error(`CryptographyError: Unsupported curve ${curve}`)
}

function getKeyCompactForm(material: EmbeddedType): { bytes: Uint8Array, points: Record<string, number[]> } {
Expand Down Expand Up @@ -93,7 +92,7 @@ export function encodeMultibaseKey(material: EmbeddedType): string {
// JWK
// -----------------------------------------------------------------------------

function getJwkKty(curve: NSupportedCurves): string {
function getJwkKty(curve: Curve): string {
if (isP256(curve)) return "EC"
else if (isEd25519(curve)) return "OKP"
else throw Error(`EncodingError: JWK encoding unsupported for curve ${curve}`)
Expand All @@ -105,15 +104,15 @@ export function encodeJsonWebKey(
const kty = getJwkKty(material.curve)
const keyMaterialCompactForm = getKeyCompactForm(material)
return {
crv: curveName(material.curve, 'jwk'),
crv: curveToName(material.curve, 'jwk'),
kty,
...keyMaterialCompactForm.points,
}
}

export function decodeJwk(key: JsonWebKey): Uint8Array {
if (!key.crv) throw new Error('Invalid key')
const curve = key.crv as NSupportedCurves
const curve = curveFromName(key.crv)
const ec = getCryptoSuite(curve)
let bytes: Uint8Array
if (isEd25519(curve)) {
Expand All @@ -137,7 +136,7 @@ export function decodeJwk(key: JsonWebKey): Uint8Array {

export function exportPrivateKey(
keypair: EC.KeyPair,
curve: NSupportedCurves
curve: Curve
): JsonWebKey {
const dBytes = new Uint8Array(keypair.getPrivate().toArray());
const xBytes = new Uint8Array(keypair.getPublic().getX().toArray());
Expand All @@ -162,7 +161,7 @@ export function exportPrivateKey(
else throw new Error(`KeyExportError: Private key export for curve ${curve} not supported`)

return {
crv: curveName(curve, 'jwk'),
crv: curveToName(curve, 'jwk'),
d,
kty,
...points,
Expand Down
13 changes: 7 additions & 6 deletions src/lib/verificationMaterialBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,30 @@ import { z } from 'zod'
import { documentSchema, verificationRelationshipSchema, verificationRelationshipsSchema } from './didParser'
import * as b58 from 'multiformats/bases/base58'
import { ec as EC } from 'elliptic'
import { Representation, SupportedCurves, VerificationRelationship, KeyFormat, verificationRelationships } from '@/types/dids'
import { Representation, VerificationRelationship, KeyFormat, verificationRelationships } from '@/types/dids'
import { DidMaterial, EmbeddedMaterial, isEmbeddedType, ReferencedMaterial } from './DidMaterial'
import { decodeP256Jwk } from './keys'
import { DidDocument } from './DidDocument'
import { decodeJwk } from './keys'
import { Curve } from './curves'

export const decodeVerificationRelationship = (verificationMethod: z.infer<typeof verificationRelationshipSchema>, method: VerificationRelationship): DidMaterial => {
if (typeof verificationMethod === "string") {
return new ReferencedMaterial(verificationMethod, { usage: { [method]: 'Reference' } })
}

let keyMaterial
let curve: SupportedCurves | undefined
let curve: Curve | undefined
let representation: Representation | undefined
let format: KeyFormat | undefined

if (verificationMethod.type) {
const ec = new EC('p256')
const ec = new EC(curveFromName())
curve = 'P-256'
representation = 'Embedded'
if (verificationMethod.type === 'JsonWebKey2020') {
format = 'JsonWebKey2020'
if (!verificationMethod.publicKeyJwk) throw new Error('Invalid type: "JsonWebKey2020" must contain "publicKeyJwk"')
keyMaterial = decodeP256Jwk(verificationMethod.publicKeyJwk)
keyMaterial = decodeJwk(verificationMethod.publicKeyJwk)
}
else if (verificationMethod.type === 'P256Key2021') {
format = 'Multibase'
Expand Down Expand Up @@ -80,6 +81,6 @@ export const didDocumentDeserializer = (document: z.infer<typeof documentSchema>
}
}
}
return new DidDocument(document.id, document.controller, denormalized)
return new DidDocument(document.id, new Set<string>(document.controller), denormalized)
}

5 changes: 2 additions & 3 deletions src/types/dids.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NSupportedCurves } from "@/lib/curves";
import { Curve } from "@/lib/curves";

export const verificationRelationships = [
"authentication",
Expand All @@ -9,7 +9,6 @@ export const verificationRelationships = [
] as const;

export type KeyFormat = "JsonWebKey2020" | "Multibase";
export type SupportedCurves = "P-256" | "Ed25519";

export type ReferencedMaterialFormat = "Reference";
export type EmbeddedMaterialFormat = "Embedded";
Expand All @@ -22,7 +21,7 @@ export type UsageFormat<Type extends Representation> = {

export type EmbeddedType = {
controller?: string;
curve: NSupportedCurves;
curve: Curve;
keyMaterial: Uint8Array;
format: KeyFormat;
usage: UsageFormat<Representation>;
Expand Down
Loading