Skip to content

Commit 8a0069b

Browse files
committed
refactor: improve module structure
1 parent 07df8a8 commit 8a0069b

File tree

13 files changed

+2278
-2164
lines changed

13 files changed

+2278
-2164
lines changed

packages/evolution/src/core/SignData.ts

Lines changed: 0 additions & 1936 deletions
This file was deleted.

packages/evolution/src/core/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export * as IPv6 from "./IPv6.js"
5555
export * as KesSignature from "./KesSignature.js"
5656
export * as KESVkey from "./KESVkey.js"
5757
export * as KeyHash from "./KeyHash.js"
58+
export * as MessageSigning from "./message-signing/index.js"
5859
export * as Mint from "./Mint.js"
5960
export * as MultiAsset from "./MultiAsset.js"
6061
export * as MultiHostName from "./MultiHostName.js"
@@ -89,7 +90,6 @@ export * as Script from "./Script.js"
8990
export * as ScriptDataHash from "./ScriptDataHash.js"
9091
export * as ScriptHash from "./ScriptHash.js"
9192
export * as ScriptRef from "./ScriptRef.js"
92-
export * as SignData from "./SignData.js"
9393
export * as SingleHostAddr from "./SingleHostAddr.js"
9494
export * as SingleHostName from "./SingleHostName.js"
9595
export * as StakeReference from "./StakeReference.js"
Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
/**
2+
* COSE key structures (RFC 8152).
3+
*
4+
* @since 2.0.0
5+
* @category Message Signing
6+
*/
7+
8+
import { Equal, Hash, Inspectable, ParseResult, Schema } from "effect"
9+
10+
import * as Bytes from "../Bytes.js"
11+
import * as CBOR from "../CBOR.js"
12+
import * as PrivateKey from "../PrivateKey.js"
13+
import * as VKey from "../VKey.js"
14+
import { HeaderMap, headerMapNew } from "./header.js"
15+
import type { Label } from "./label.js"
16+
import {
17+
AlgorithmId,
18+
CurveType,
19+
KeyOperation,
20+
KeyType,
21+
labelFromInt,
22+
labelFromText
23+
} from "./label.js"
24+
25+
// ============================================================================
26+
// COSEKey
27+
// ============================================================================
28+
29+
/**
30+
* COSE key representation (RFC 8152).
31+
*
32+
* @since 2.0.0
33+
* @category Model
34+
*/
35+
export class COSEKey extends Schema.Class<COSEKey>("COSEKey")({
36+
keyType: Schema.UndefinedOr(Schema.Enums(KeyType)),
37+
keyId: Schema.UndefinedOr(Schema.Uint8ArrayFromSelf),
38+
algorithmId: Schema.UndefinedOr(Schema.Enums(AlgorithmId)),
39+
keyOps: Schema.UndefinedOr(Schema.Array(Schema.Enums(KeyOperation))),
40+
baseInitVector: Schema.UndefinedOr(Schema.Uint8ArrayFromSelf),
41+
headers: Schema.instanceOf(HeaderMap)
42+
}) {
43+
toJSON() {
44+
return {
45+
_tag: "COSEKey" as const,
46+
keyType: this.keyType !== undefined ? KeyType[this.keyType] : undefined,
47+
keyId: this.keyId !== undefined ? Bytes.toHex(this.keyId) : undefined,
48+
algorithmId: this.algorithmId !== undefined ? AlgorithmId[this.algorithmId] : undefined,
49+
keyOps:
50+
this.keyOps !== undefined ? this.keyOps.map((op: KeyOperation) => KeyOperation[op]) : undefined,
51+
baseInitVector:
52+
this.baseInitVector !== undefined ? Bytes.toHex(this.baseInitVector) : undefined,
53+
headers: this.headers.toJSON()
54+
}
55+
}
56+
57+
toString(): string {
58+
return Inspectable.format(this.toJSON())
59+
}
60+
61+
[Inspectable.NodeInspectSymbol](): unknown {
62+
return this.toJSON()
63+
}
64+
65+
[Equal.symbol](that: unknown): boolean {
66+
return (
67+
that instanceof COSEKey &&
68+
Equal.equals(this.keyType, that.keyType) &&
69+
Equal.equals(this.keyId, that.keyId) &&
70+
Equal.equals(this.algorithmId, that.algorithmId) &&
71+
Equal.equals(this.keyOps, that.keyOps) &&
72+
Equal.equals(this.baseInitVector, that.baseInitVector) &&
73+
Equal.equals(this.headers, that.headers)
74+
)
75+
}
76+
77+
[Hash.symbol](): number {
78+
return Hash.combine(
79+
Hash.combine(
80+
Hash.combine(
81+
Hash.combine(Hash.combine(Hash.hash(this.keyType))(Hash.hash(this.keyId)))(
82+
Hash.hash(this.algorithmId)
83+
)
84+
)(Hash.hash(this.keyOps))
85+
)(Hash.hash(this.baseInitVector))
86+
)(Hash.hash(this.headers))
87+
}
88+
}
89+
90+
/**
91+
* CBOR bytes transformation schema for COSEKey.
92+
* Encodes COSEKey as a CBOR Map compatible with CSL.
93+
*
94+
* @since 2.0.0
95+
* @category Schemas
96+
*/
97+
export const COSEKeyFromCBORBytes = (options: CBOR.CodecOptions = CBOR.CML_DEFAULT_OPTIONS) =>
98+
Schema.transformOrFail(
99+
CBOR.FromBytes(options),
100+
Schema.typeSchema(COSEKey),
101+
{
102+
strict: true,
103+
decode: (cbor, _, ast) => {
104+
// COSEKey is encoded as a CBOR Map
105+
if (!(cbor instanceof Map)) {
106+
return ParseResult.fail(new ParseResult.Type(ast, cbor))
107+
}
108+
109+
// Decode standard COSE parameters and custom headers
110+
let keyType: KeyType | undefined
111+
let keyId: Uint8Array | undefined
112+
let algorithmId: AlgorithmId | undefined
113+
let keyOps: Array<KeyOperation> | undefined
114+
let baseInitVector: Uint8Array | undefined
115+
const customHeaders = new Map<Label, CBOR.CBOR>()
116+
117+
for (const [labelValue, value] of cbor.entries()) {
118+
// Convert CBOR value to Label
119+
const label = typeof labelValue === "bigint" ? labelFromInt(labelValue) :
120+
typeof labelValue === "string" ? labelFromText(labelValue) : labelFromInt(BigInt(labelValue))
121+
122+
// Standard COSE key parameters
123+
if (Equal.equals(label, labelFromInt(1n))) { // kty
124+
keyType = Number(value) as KeyType
125+
} else if (Equal.equals(label, labelFromInt(2n))) { // kid
126+
keyId = value as Uint8Array
127+
} else if (Equal.equals(label, labelFromInt(3n))) { // alg
128+
algorithmId = Number(value) as AlgorithmId
129+
} else if (Equal.equals(label, labelFromInt(4n))) { // key_ops
130+
keyOps = (value as Array<unknown>).map(op => Number(op) as KeyOperation)
131+
} else if (Equal.equals(label, labelFromInt(5n))) { // Base IV
132+
baseInitVector = value as Uint8Array
133+
} else {
134+
// Custom headers (curve, public key, etc.)
135+
customHeaders.set(label, value)
136+
}
137+
}
138+
139+
const headers = new HeaderMap({ headers: customHeaders }, { disableValidation: true })
140+
141+
return ParseResult.succeed(
142+
new COSEKey(
143+
{ keyType, keyId, algorithmId, keyOps, baseInitVector, headers },
144+
{ disableValidation: true }
145+
)
146+
)
147+
},
148+
encode: (coseKey) => {
149+
const map = new Map<CBOR.CBOR, CBOR.CBOR>()
150+
151+
// Encode standard COSE parameters
152+
if (coseKey.keyType !== undefined) map.set(1n, BigInt(coseKey.keyType))
153+
if (coseKey.keyId !== undefined) map.set(2n, coseKey.keyId)
154+
if (coseKey.algorithmId !== undefined) map.set(3n, BigInt(coseKey.algorithmId))
155+
if (coseKey.keyOps !== undefined) {
156+
map.set(4n, coseKey.keyOps.map(op => BigInt(op)))
157+
}
158+
if (coseKey.baseInitVector !== undefined) map.set(5n, coseKey.baseInitVector)
159+
160+
// Encode custom headers
161+
for (const [label, value] of coseKey.headers.headers.entries()) {
162+
map.set(label.value, value)
163+
}
164+
165+
return ParseResult.succeed(map)
166+
}
167+
}
168+
).annotations({ identifier: "COSEKeyFromCBORBytes" })
169+
170+
// ============================================================================
171+
// EdDSA25519Key
172+
// ============================================================================
173+
174+
/**
175+
* Ed25519 key for signing and verification.
176+
*
177+
* @since 2.0.0
178+
* @category Model
179+
*/
180+
export class EdDSA25519Key extends Schema.Class<EdDSA25519Key>("EdDSA25519Key")({
181+
privateKey: Schema.UndefinedOr(Schema.instanceOf(PrivateKey.PrivateKey)),
182+
publicKey: Schema.UndefinedOr(Schema.instanceOf(VKey.VKey))
183+
}) {
184+
toJSON() {
185+
return {
186+
_tag: "EdDSA25519Key" as const,
187+
hasPrivateKey: this.privateKey !== undefined,
188+
hasPublicKey: this.publicKey !== undefined
189+
}
190+
}
191+
192+
toString(): string {
193+
return Inspectable.format(this.toJSON())
194+
}
195+
196+
[Inspectable.NodeInspectSymbol](): unknown {
197+
return this.toJSON()
198+
}
199+
200+
[Equal.symbol](that: unknown): boolean {
201+
return (
202+
that instanceof EdDSA25519Key &&
203+
Equal.equals(this.privateKey, that.privateKey) &&
204+
Equal.equals(this.publicKey, that.publicKey)
205+
)
206+
}
207+
208+
[Hash.symbol](): number {
209+
return Hash.combine(Hash.hash(this.privateKey))(Hash.hash(this.publicKey))
210+
}
211+
212+
/**
213+
* Set the private key for signing.
214+
*
215+
* @since 2.0.0
216+
* @category Mutators
217+
*/
218+
setPrivateKey(privateKey: PrivateKey.PrivateKey): this {
219+
return new EdDSA25519Key(
220+
{
221+
privateKey,
222+
publicKey: VKey.fromPrivateKey(privateKey)
223+
},
224+
{ disableValidation: true }
225+
) as this
226+
}
227+
228+
/**
229+
* Check if key can be used for signing.
230+
*
231+
* @since 2.0.0
232+
* @category Predicates
233+
*/
234+
isForSigning(): boolean {
235+
return this.privateKey !== undefined
236+
}
237+
238+
/**
239+
* Check if key can be used for verification.
240+
*
241+
* @since 2.0.0
242+
* @category Predicates
243+
*/
244+
isForVerifying(): boolean {
245+
return this.publicKey !== undefined
246+
}
247+
248+
/**
249+
* Build a COSEKey from this Ed25519 key.
250+
*
251+
* @since 2.0.0
252+
* @category Conversion
253+
*/
254+
build(): COSEKey {
255+
const headers = headerMapNew()
256+
.setAlgorithmId(AlgorithmId.EdDSA)
257+
.setHeader(labelFromInt(1n), BigInt(KeyType.OKP))
258+
.setHeader(labelFromInt(-1n), BigInt(CurveType.Ed25519))
259+
260+
const headersWithKey =
261+
this.publicKey !== undefined
262+
? headers.setHeader(labelFromInt(-2n), this.publicKey.bytes)
263+
: headers
264+
265+
return new COSEKey(
266+
{
267+
keyType: KeyType.OKP,
268+
keyId: undefined,
269+
algorithmId: AlgorithmId.EdDSA,
270+
keyOps: undefined,
271+
baseInitVector: undefined,
272+
headers: headersWithKey
273+
},
274+
{ disableValidation: true }
275+
)
276+
}
277+
}

0 commit comments

Comments
 (0)