Skip to content

Commit

Permalink
Fix Curve capabilities
Browse files Browse the repository at this point in the history
  • Loading branch information
johanssonanton committed Jul 31, 2023
1 parent 8e742c6 commit 1380adb
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 61 deletions.
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

0 comments on commit 1380adb

Please sign in to comment.