Skip to content

Commit 494cc21

Browse files
committed
SignatureValidationData
1 parent 360dfdc commit 494cc21

File tree

6 files changed

+99
-39
lines changed

6 files changed

+99
-39
lines changed

packages/sdk/src/signature/BrowserSignatureValidation.mts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55
import * as Comlink from 'comlink'
66
import { SignatureValidationContext } from './SignatureValidationContext.js'
7-
import { SignatureValidationResult } from './signatureValidation.js'
7+
import { SignatureValidationResult, toSignatureValidationData } from './signatureValidation.js'
88
import type { SignatureValidationWorkerApi } from './SignatureValidationWorker.js'
99
import { StreamMessage } from '../protocol/StreamMessage.js'
1010

@@ -22,7 +22,9 @@ export default class BrowserSignatureValidation implements SignatureValidationCo
2222
}
2323

2424
async validateSignature(message: StreamMessage): Promise<SignatureValidationResult> {
25-
return this.workerApi.validateSignature(message)
25+
// Convert class instance to plain serializable data before sending to worker
26+
const data = toSignatureValidationData(message)
27+
return this.workerApi.validateSignature(data)
2628
}
2729

2830
destroy(): void {

packages/sdk/src/signature/ServerSignatureValidation.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Worker } from "worker_threads"
44
import { StreamMessage } from '../protocol/StreamMessage'
55
import { SignatureValidationContext } from './SignatureValidationContext'
66
import { SignatureValidationWorkerApi } from './SignatureValidationWorker'
7-
import { SignatureValidationResult } from './signatureValidation'
7+
import { SignatureValidationResult, toSignatureValidationData } from './signatureValidation'
88
import { join } from 'path'
99

1010
export default class ServerSignatureValidation implements SignatureValidationContext {
@@ -21,8 +21,9 @@ export default class ServerSignatureValidation implements SignatureValidationCon
2121
}
2222

2323
async validateSignature(message: StreamMessage): Promise<SignatureValidationResult> {
24-
console.log('validateSignature', message)
25-
return this.workerApi.validateSignature(message)
24+
// Convert class instance to plain serializable data before sending to worker
25+
const data = toSignatureValidationData(message)
26+
return this.workerApi.validateSignature(data)
2627
}
2728

2829
// eslint-disable-next-line class-methods-use-this

packages/sdk/src/signature/SignatureValidationWorker.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import * as Comlink from 'comlink'
2-
import { validateSignatureData, SignatureValidationResult } from './signatureValidation'
3-
import { StreamMessage } from '../protocol/StreamMessage'
2+
import { validateSignatureData, SignatureValidationResult, SignatureValidationData } from './signatureValidation'
43

54
const workerApi = {
6-
validateSignature: async (data: StreamMessage): Promise<SignatureValidationResult> => {
5+
validateSignature: async (data: SignatureValidationData): Promise<SignatureValidationResult> => {
76
return validateSignatureData(data)
87
}
98
}

packages/sdk/src/signature/createLegacySignaturePayload.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { binaryToHex, binaryToUtf8 } from '@streamr/utils'
22
import { EncryptedGroupKey, EncryptionType } from '@streamr/trackerless-network'
3-
import { MessageID } from '../protocol/MessageID'
4-
import { MessageRef } from '../protocol/MessageRef'
3+
import { MessageIdLike, MessageRefLike } from './createSignaturePayload'
54

65
const serializeGroupKey = ({ id, data }: EncryptedGroupKey): string => {
76
return JSON.stringify([id, binaryToHex(data)])
@@ -11,10 +10,10 @@ const serializeGroupKey = ({ id, data }: EncryptedGroupKey): string => {
1110
* Only to be used for LEGACY_SECP256K1 signature type.
1211
*/
1312
export const createLegacySignaturePayload = (opts: {
14-
messageId: MessageID
13+
messageId: MessageIdLike
1514
content: Uint8Array
1615
encryptionType: EncryptionType
17-
prevMsgRef?: MessageRef
16+
prevMsgRef?: MessageRefLike
1817
newGroupKey?: EncryptedGroupKey
1918
}): Uint8Array => {
2019
const prev = ((opts.prevMsgRef !== undefined) ? `${opts.prevMsgRef.timestamp}${opts.prevMsgRef.sequenceNumber}` : '')

packages/sdk/src/signature/createSignaturePayload.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,34 @@ import {
33
GroupKeyRequest as NewGroupKeyRequest,
44
GroupKeyResponse as NewGroupKeyResponse
55
} from '@streamr/trackerless-network'
6-
import { utf8ToBinary } from '@streamr/utils'
7-
import { MessageID } from '../protocol/MessageID'
8-
import { MessageRef } from '../protocol/MessageRef'
6+
import { StreamID, UserID, utf8ToBinary } from '@streamr/utils'
97
import { StreamMessageType } from '../protocol/StreamMessage'
108

9+
/**
10+
* Plain data for message ID - accepts class instances or plain objects with the same properties.
11+
*/
12+
export interface MessageIdLike {
13+
streamId: StreamID
14+
streamPartition: number
15+
timestamp: number
16+
sequenceNumber: number
17+
publisherId: UserID
18+
msgChainId: string
19+
}
20+
21+
/**
22+
* Plain data for message reference - accepts class instances or plain objects with the same properties.
23+
*/
24+
export interface MessageRefLike {
25+
timestamp: number
26+
sequenceNumber: number
27+
}
28+
1129
export const createSignaturePayload = (opts: {
12-
messageId: MessageID
30+
messageId: MessageIdLike
1331
content: Uint8Array
1432
messageType: StreamMessageType
15-
prevMsgRef?: MessageRef
33+
prevMsgRef?: MessageRefLike
1634
newGroupKey?: EncryptedGroupKey
1735
}): Uint8Array | never => {
1836
const header = Buffer.concat([

packages/sdk/src/signature/signatureValidation.ts

Lines changed: 63 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
* Core signature validation logic - shared between worker and main thread implementations.
33
* This file contains pure cryptographic validation functions without any network dependencies.
44
*/
5-
import { SigningUtil, toUserIdRaw } from '@streamr/utils'
6-
import { SignatureType } from '@streamr/trackerless-network'
5+
import { SigningUtil, StreamID, toUserIdRaw, UserID } from '@streamr/utils'
6+
import { EncryptedGroupKey, EncryptionType, SignatureType } from '@streamr/trackerless-network'
77
import { IDENTITY_MAPPING } from '../identity/IdentityMapping'
8-
import { createSignaturePayload } from './createSignaturePayload'
8+
import { createSignaturePayload, MessageIdLike, MessageRefLike } from './createSignaturePayload'
99
import { createLegacySignaturePayload } from './createLegacySignaturePayload'
10-
import { StreamMessage } from '../protocol/StreamMessage'
10+
import { StreamMessage, StreamMessageType } from '../protocol/StreamMessage'
1111

1212
// Lookup structure SignatureType -> SigningUtil
1313
const signingUtilBySignatureType: Record<number, SigningUtil> = Object.fromEntries(
@@ -24,46 +24,87 @@ export type SignatureValidationResult =
2424
| { type: 'invalid' }
2525
| { type: 'error', message: string }
2626

27+
/**
28+
* Plain data type for signature validation that can be serialized to a worker.
29+
* This contains only primitive values and simple objects (no class instances).
30+
*/
31+
export interface SignatureValidationData {
32+
messageId: MessageIdLike
33+
prevMsgRef?: MessageRefLike
34+
messageType: StreamMessageType
35+
content: Uint8Array
36+
signature: Uint8Array
37+
signatureType: SignatureType
38+
encryptionType: EncryptionType
39+
newGroupKey?: EncryptedGroupKey
40+
}
41+
42+
/**
43+
* Extract plain serializable data from a StreamMessage for worker communication.
44+
*/
45+
export function toSignatureValidationData(message: StreamMessage): SignatureValidationData {
46+
return {
47+
messageId: {
48+
streamId: message.messageId.streamId,
49+
streamPartition: message.messageId.streamPartition,
50+
timestamp: message.messageId.timestamp,
51+
sequenceNumber: message.messageId.sequenceNumber,
52+
publisherId: message.messageId.publisherId,
53+
msgChainId: message.messageId.msgChainId,
54+
},
55+
prevMsgRef: message.prevMsgRef ? {
56+
timestamp: message.prevMsgRef.timestamp,
57+
sequenceNumber: message.prevMsgRef.sequenceNumber,
58+
} : undefined,
59+
messageType: message.messageType,
60+
content: message.content,
61+
signature: message.signature,
62+
signatureType: message.signatureType,
63+
encryptionType: message.encryptionType,
64+
newGroupKey: message.newGroupKey,
65+
}
66+
}
67+
2768
/**
2869
* Validate signature using extracted data.
2970
* This is the core validation logic that can be run in a worker.
3071
*/
31-
export async function validateSignatureData(message: StreamMessage): Promise<SignatureValidationResult> {
72+
export async function validateSignatureData(data: SignatureValidationData): Promise<SignatureValidationResult> {
3273
try {
33-
const signingUtil = signingUtilBySignatureType[message.signatureType]
74+
const signingUtil = signingUtilBySignatureType[data.signatureType]
3475
// Common case: standard signature types
3576
if (signingUtil) {
3677
const payload = createSignaturePayload({
37-
messageId: message.messageId,
38-
content: message.content,
39-
messageType: message.messageType,
40-
prevMsgRef: message.prevMsgRef,
41-
newGroupKey: message.newGroupKey,
78+
messageId: data.messageId,
79+
content: data.content,
80+
messageType: data.messageType,
81+
prevMsgRef: data.prevMsgRef,
82+
newGroupKey: data.newGroupKey,
4283
})
4384
const isValid = await signingUtil.verifySignature(
44-
toUserIdRaw(message.messageId.publisherId),
85+
toUserIdRaw(data.messageId.publisherId),
4586
payload,
46-
message.signature
87+
data.signature
4788
)
4889
return isValid ? { type: 'valid' } : { type: 'invalid' }
4990
}
5091
// Special handling: legacy signature type
51-
if (message.signatureType === SignatureType.ECDSA_SECP256K1_LEGACY) {
92+
if (data.signatureType === SignatureType.ECDSA_SECP256K1_LEGACY) {
5293
const payload = createLegacySignaturePayload({
53-
messageId: message.messageId,
54-
content: message.content,
55-
encryptionType: message.encryptionType,
56-
prevMsgRef: message.prevMsgRef,
57-
newGroupKey: message.newGroupKey,
94+
messageId: data.messageId,
95+
content: data.content,
96+
encryptionType: data.encryptionType,
97+
prevMsgRef: data.prevMsgRef,
98+
newGroupKey: data.newGroupKey,
5899
})
59100
const isValid = await evmSigner.verifySignature(
60-
toUserIdRaw(message.messageId.publisherId),
101+
toUserIdRaw(data.messageId.publisherId),
61102
payload,
62-
message.signature
103+
data.signature
63104
)
64105
return isValid ? { type: 'valid' } : { type: 'invalid' }
65106
}
66-
return { type: 'error', message: `Unsupported signatureType: "${message.signatureType}"` }
107+
return { type: 'error', message: `Unsupported signatureType: "${data.signatureType}"` }
67108
} catch (err) {
68109
return { type: 'error', message: String(err) }
69110
}

0 commit comments

Comments
 (0)