/**
* Multi-Device Identity Management for Hyper-Registry
*
* STATUS: Implementation sketch - not yet functional
*/
import crypto from 'hypercore-crypto'
// import IdentityKey from 'keet-identity-key'
/**
* Device identity management
*/
export class DeviceIdentity {
constructor(options = {}) {
this.storagePath = options.storagePath
this.deviceKey = null
this.deviceSecret = null
this.deviceName = options.deviceName || null
}
/**
* Initialize device identity
* Creates or loads device keypair
*/
async init() {
// TODO: Load from secure storage if exists
// TODO: Generate new if not exists
const keypair = crypto.keyPair()
this.deviceKey = keypair.publicKey
this.deviceSecret = keypair.secretKey
}
/**
* Get device public key
*/
getPublicKey() {
return this.deviceKey.toString('hex')
}
/**
* Sign data with device key
*/
sign(data) {
const dataBuffer = typeof data === 'string' ? Buffer.from(data) : data
return crypto.sign(dataBuffer, this.deviceSecret)
}
/**
* Verify signature from another device
*/
static verify(data, signature, publicKey) {
const dataBuffer = typeof data === 'string' ? Buffer.from(data) : data
const sigBuffer = typeof signature === 'string' ? Buffer.from(signature, 'hex') : signature
const keyBuffer = typeof publicKey === 'string' ? Buffer.from(publicKey, 'hex') : publicKey
return crypto.verify(dataBuffer, sigBuffer, keyBuffer)
}
}
/**
* Root identity (mnemonic-derived)
*/
export class RootIdentity {
constructor(mnemonic = null) {
this.mnemonic = mnemonic
this.publicKey = null
this.secretKey = null
}
/**
* Generate new root identity with mnemonic
*/
static generate() {
// TODO: Use keet-identity-key
// const identity = IdentityKey.generate()
// return new RootIdentity(identity.mnemonic)
// Placeholder: just generate a keypair
const keypair = crypto.keyPair()
const instance = new RootIdentity('placeholder-mnemonic-not-real')
instance.publicKey = keypair.publicKey
instance.secretKey = keypair.secretKey
return instance
}
/**
* Recover root identity from mnemonic
*/
static fromMnemonic(mnemonic) {
// TODO: Use keet-identity-key
// const identity = new IdentityKey(mnemonic)
// const instance = new RootIdentity(mnemonic)
// instance.publicKey = identity.publicKey
// instance.secretKey = identity.secretKey
// return instance
throw new Error('Not implemented - requires keet-identity-key')
}
/**
* Get root public key
*/
getPublicKey() {
return this.publicKey.toString('hex')
}
/**
* Sign device authorization
*/
signDeviceAuth(device) {
const data = JSON.stringify({
key: device.key,
name: device.name,
canAuthorize: device.canAuthorize,
canRevoke: device.canRevoke,
resources: device.resources
})
return crypto.sign(Buffer.from(data), this.secretKey).toString('hex')
}
/**
* Verify device was authorized by root
*/
static verifyDeviceAuth(device, signature, rootPublicKey) {
const data = JSON.stringify({
key: device.key,
name: device.name,
canAuthorize: device.canAuthorize,
canRevoke: device.canRevoke,
resources: device.resources
})
return crypto.verify(
Buffer.from(data),
Buffer.from(signature, 'hex'),
Buffer.from(rootPublicKey, 'hex')
)
}
}
/**
* Device pairing protocol
*/
export class DevicePairing {
constructor(registry) {
this.registry = registry
this.pendingRequests = new Map()
}
/**
* Generate pairing code for new device
*/
generatePairingCode(deviceKey) {
const timestamp = Date.now()
const data = `${deviceKey}:${timestamp}`
const hash = crypto.hash(Buffer.from(data))
const shortCode = hash.toString('hex').slice(0, 8).toUpperCase()
// Format: XXXX-XXXX
const formatted = shortCode.slice(0, 4) + '-' + shortCode.slice(4, 8)
this.pendingRequests.set(shortCode, {
deviceKey,
timestamp,
expiresAt: timestamp + 300000 // 5 minutes
})
return {
code: formatted,
deviceKey,
expiresAt: timestamp + 300000
}
}
/**
* Verify pairing code and get device key
*/
verifyPairingCode(code) {
const normalized = code.replace('-', '').toUpperCase()
const request = this.pendingRequests.get(normalized)
if (!request) {
return null
}
if (Date.now() > request.expiresAt) {
this.pendingRequests.delete(normalized)
return null
}
return request.deviceKey
}
/**
* Complete pairing (authorize the device)
*/
async completePairing(code, options = {}) {
const deviceKey = this.verifyPairingCode(code)
if (!deviceKey) {
throw new Error('Invalid or expired pairing code')
}
// Authorize the device
await this.registry.authorizeDevice(deviceKey, {
name: options.name || 'new-device',
canAuthorize: options.canAuthorize || false,
canRevoke: options.canRevoke || false,
resources: options.resources || null
})
// Clean up
this.pendingRequests.delete(code.replace('-', '').toUpperCase())
return { deviceKey, authorized: true }
}
/**
* Clean up expired requests
*/
cleanup() {
const now = Date.now()
for (const [code, request] of this.pendingRequests) {
if (now > request.expiresAt) {
this.pendingRequests.delete(code)
}
}
}
}