@blueid/access-cli 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc +10 -0
- package/.prettierrc +7 -0
- package/index.js +22 -0
- package/lib/createKeyPair/action.js +35 -0
- package/lib/createKeyPair/index.js +27 -0
- package/lib/createProvisioningData/action.js +34 -0
- package/lib/createProvisioningData/index.js +48 -0
- package/lib/createProvisioningData/registerDevice.js +101 -0
- package/lib/proto/BlueCore_pb.js +975 -0
- package/lib/proto/BlueLock_pb.js +46 -0
- package/lib/proto/BlueSDK_pb.js +73 -0
- package/lib/proto/BlueSystem_pb.js +249 -0
- package/lib/registerVendor/action.js +46 -0
- package/lib/registerVendor/index.js +18 -0
- package/lib/shared/api.js +25 -0
- package/lib/shared/config.js +35 -0
- package/lib/shared/createSignature.js +14 -0
- package/lib/shared/getBlueVersion.js +8 -0
- package/lib/shared/loadPrivateKey.js +25 -0
- package/lib/shared/outputBuffer.js +46 -0
- package/lib/shared/packageInfo.js +11 -0
- package/lib/shared/printCliHeader.js +25 -0
- package/lib/uuidToBytes/action.js +20 -0
- package/lib/uuidToBytes/index.js +12 -0
- package/package.json +34 -0
- package/syncproto.sh +3 -0
package/.eslintrc
ADDED
package/.prettierrc
ADDED
package/index.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#! /usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { program } from 'commander'
|
|
4
|
+
import chalk from 'chalk'
|
|
5
|
+
import os from 'os'
|
|
6
|
+
|
|
7
|
+
import printCliHeader from './lib/shared/printCliHeader.js'
|
|
8
|
+
|
|
9
|
+
// Just import all commands they'll register themselves
|
|
10
|
+
import './lib/createKeyPair/index.js'
|
|
11
|
+
import './lib/createProvisioningData/index.js'
|
|
12
|
+
import './lib/registerVendor/index.js'
|
|
13
|
+
import './lib/uuidToBytes/index.js'
|
|
14
|
+
|
|
15
|
+
printCliHeader()
|
|
16
|
+
|
|
17
|
+
if (os.endianness() !== 'LE') {
|
|
18
|
+
console.log(chalk.red('Your system is not little endian.'))
|
|
19
|
+
process.exit(-1)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
program.parse()
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import chalk from 'chalk'
|
|
2
|
+
import crypto from 'crypto'
|
|
3
|
+
|
|
4
|
+
import outputBuffer from '../shared/outputBuffer.js'
|
|
5
|
+
|
|
6
|
+
export default async ({ format, privateFile, publicFile }) => {
|
|
7
|
+
try {
|
|
8
|
+
const isPEM = format === 'PEM'
|
|
9
|
+
|
|
10
|
+
const { publicKey, privateKey } = crypto.generateKeyPairSync('ec', {
|
|
11
|
+
namedCurve: 'prime256v1',
|
|
12
|
+
publicKeyEncoding: {
|
|
13
|
+
type: 'spki',
|
|
14
|
+
format: isPEM ? 'pem' : 'der',
|
|
15
|
+
},
|
|
16
|
+
privateKeyEncoding: {
|
|
17
|
+
type: 'pkcs8',
|
|
18
|
+
format: isPEM ? 'pem' : 'der',
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
if (isPEM) {
|
|
23
|
+
console.log(privateKey)
|
|
24
|
+
console.log(publicKey)
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
console.log('-- PRIVATE KEY --')
|
|
29
|
+
outputBuffer(privateKey, format, privateFile)
|
|
30
|
+
console.log('-- PUBLIC KEY --')
|
|
31
|
+
outputBuffer(publicKey, format, publicFile)
|
|
32
|
+
} catch (e) {
|
|
33
|
+
console.error(chalk.red(e.message))
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { program, Option } from 'commander'
|
|
2
|
+
|
|
3
|
+
import { outputFormats } from '../shared/outputBuffer.js'
|
|
4
|
+
|
|
5
|
+
import action from './action.js'
|
|
6
|
+
|
|
7
|
+
program
|
|
8
|
+
.command('createKeyPair')
|
|
9
|
+
.description('Create a private and public key pair used for signing')
|
|
10
|
+
.addOption(
|
|
11
|
+
new Option('--format [format]', 'The output format to be used, if not PEM then generates DER format')
|
|
12
|
+
.choices(['PEM', 'file'].concat(outputFormats))
|
|
13
|
+
.makeOptionMandatory()
|
|
14
|
+
)
|
|
15
|
+
.addOption(
|
|
16
|
+
new Option(
|
|
17
|
+
'--privateFile [privateFile]',
|
|
18
|
+
'If output format is file must define the private file name to write into'
|
|
19
|
+
)
|
|
20
|
+
)
|
|
21
|
+
.addOption(
|
|
22
|
+
new Option(
|
|
23
|
+
'--publicFile [publicFile]',
|
|
24
|
+
'If output format is file must define the public file name to write into'
|
|
25
|
+
)
|
|
26
|
+
)
|
|
27
|
+
.action(action)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { protoDelimited } from '@bufbuild/protobuf'
|
|
2
|
+
|
|
3
|
+
import registerDevice from './registerDevice.js'
|
|
4
|
+
|
|
5
|
+
import outputBuffer from '../shared/outputBuffer.js'
|
|
6
|
+
|
|
7
|
+
import { BlueHardwareType } from '../proto/BlueCore_pb.js'
|
|
8
|
+
import { BlueSystemProvisioning } from '../proto/BlueSystem_pb.js'
|
|
9
|
+
|
|
10
|
+
export default async (
|
|
11
|
+
provisioningFile,
|
|
12
|
+
{ vendorIdentity, hardwareType, hardwareSerialNumber, hardwareVersion, hardwareName, meta, format }
|
|
13
|
+
) => {
|
|
14
|
+
const { deviceId, terminalPrivateKey, signaturePublicKey } = await registerDevice({
|
|
15
|
+
vendorIdentity,
|
|
16
|
+
hardwareSerialNumber,
|
|
17
|
+
hardwareType,
|
|
18
|
+
hardwareName,
|
|
19
|
+
hardwareVersion,
|
|
20
|
+
meta,
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
const provisionData = new BlueSystemProvisioning({
|
|
24
|
+
hardwareType: BlueHardwareType[hardwareType],
|
|
25
|
+
deviceId,
|
|
26
|
+
serialNumber: hardwareSerialNumber,
|
|
27
|
+
terminalPrivateKey,
|
|
28
|
+
signaturePublicKey,
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const buffer = protoDelimited.enc(provisionData)
|
|
32
|
+
|
|
33
|
+
outputBuffer(buffer, format, provisioningFile)
|
|
34
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { program, Option } from 'commander'
|
|
2
|
+
|
|
3
|
+
import { outputFormats } from '../shared/outputBuffer.js'
|
|
4
|
+
|
|
5
|
+
import { BlueHardwareType } from '../proto/BlueCore_pb.js'
|
|
6
|
+
|
|
7
|
+
import action from './action.js'
|
|
8
|
+
|
|
9
|
+
program
|
|
10
|
+
.command('createProvisioningData')
|
|
11
|
+
.description('Create a binary provisioning data package for provisioning a blueid device.')
|
|
12
|
+
.argument('[provisioningFile]', 'If output format is file must define the provisioning file name to write into')
|
|
13
|
+
.addOption(
|
|
14
|
+
new Option(
|
|
15
|
+
'--vendorIdentity [vendorIdentity]',
|
|
16
|
+
'The vendor identity to be used for authentication or "test" for test-data'
|
|
17
|
+
).makeOptionMandatory(true)
|
|
18
|
+
)
|
|
19
|
+
.addOption(
|
|
20
|
+
new Option('--hardwareType [hardwareType]', 'The type of the device')
|
|
21
|
+
.choices(Object.values(BlueHardwareType).filter((x) => typeof x === 'string'))
|
|
22
|
+
.makeOptionMandatory(true)
|
|
23
|
+
)
|
|
24
|
+
.addOption(
|
|
25
|
+
new Option(
|
|
26
|
+
'--hardwareVersion [hardwareVersion]',
|
|
27
|
+
'The actual hardware version of the device which is vendor specific'
|
|
28
|
+
)
|
|
29
|
+
)
|
|
30
|
+
.addOption(
|
|
31
|
+
new Option(
|
|
32
|
+
'--hardwareSerialNumber [hardwareSerialNumber]',
|
|
33
|
+
'The vendor serial number for the device which is vendor specific'
|
|
34
|
+
).makeOptionMandatory(true)
|
|
35
|
+
)
|
|
36
|
+
.addOption(
|
|
37
|
+
new Option(
|
|
38
|
+
'--hardwareName [hardwareName]',
|
|
39
|
+
'The human readable identifier of the device which is vendor specific'
|
|
40
|
+
).makeOptionMandatory(true)
|
|
41
|
+
)
|
|
42
|
+
.addOption(new Option('--meta [meta]', 'Optional meta data in JSON format for the device'))
|
|
43
|
+
.addOption(
|
|
44
|
+
new Option('--format [format]', 'The output format to be used')
|
|
45
|
+
.choices(['file'].concat(outputFormats))
|
|
46
|
+
.makeOptionMandatory()
|
|
47
|
+
)
|
|
48
|
+
.action(action)
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { createPrivateKey, createPublicKey } from 'crypto'
|
|
2
|
+
|
|
3
|
+
import loadPrivateKey from '../shared/loadPrivateKey.js'
|
|
4
|
+
import createSignature from '../shared/createSignature.js'
|
|
5
|
+
import { getConfigValue } from '../shared/config.js'
|
|
6
|
+
import { apiPost } from '../shared/api.js'
|
|
7
|
+
|
|
8
|
+
import { BlueSharedDemoData } from '../proto/BlueCore_pb.js'
|
|
9
|
+
|
|
10
|
+
export default async ({ vendorIdentity, hardwareType, hardwareVersion, hardwareSerialNumber, hardwareName, meta }) => {
|
|
11
|
+
if (vendorIdentity === 'test') {
|
|
12
|
+
return {
|
|
13
|
+
deviceId: BlueSharedDemoData.fields.findJsonName('deviceId').default,
|
|
14
|
+
terminalPrivateKey: BlueSharedDemoData.fields.findJsonName('terminalPrivateKey').default,
|
|
15
|
+
signaturePublicKey: BlueSharedDemoData.fields.findJsonName('signaturePublicKey').default,
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (meta) {
|
|
20
|
+
try {
|
|
21
|
+
// eslint-disable-next-line no-param-reassign
|
|
22
|
+
meta = JSON.parse(meta)
|
|
23
|
+
} catch (e) {
|
|
24
|
+
throw new Error(`Invalid meta JSON format, failed with: ${e.message}`)
|
|
25
|
+
}
|
|
26
|
+
} else {
|
|
27
|
+
meta = {}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let hardwareVersionNumber = 0
|
|
31
|
+
if (hardwareVersion) {
|
|
32
|
+
hardwareVersionNumber = Number.parseInt(hardwareVersion, 10)
|
|
33
|
+
if (Number.isNaN(hardwareVersionNumber)) {
|
|
34
|
+
throw new Error('Invalid number for hardwareVersion')
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const vendorConfig = getConfigValue(`vendors.${vendorIdentity}`, undefined)
|
|
39
|
+
if (!vendorConfig) {
|
|
40
|
+
throw new Error(`No registered vendor for ${vendorIdentity} found, please call registerVendor command, first`)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const vendorPrivateKey = loadPrivateKey(vendorConfig.privateKey)
|
|
44
|
+
|
|
45
|
+
const payload = {
|
|
46
|
+
vendorIdentity,
|
|
47
|
+
hardwareType,
|
|
48
|
+
hardwareVersion: hardwareVersionNumber,
|
|
49
|
+
hardwareSerialNumber,
|
|
50
|
+
hardwareName,
|
|
51
|
+
meta,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
payload.signature = createSignature(JSON.stringify(payload), vendorPrivateKey, 'base64')
|
|
56
|
+
} catch (e) {
|
|
57
|
+
throw new Error(`Failed to create signature with: ${e.message}`)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const response = await apiPost('/access/devices/register', payload)
|
|
61
|
+
|
|
62
|
+
const {
|
|
63
|
+
deviceId,
|
|
64
|
+
terminalPrivateKey: terminalPrivateKeyData,
|
|
65
|
+
signaturePublicKey: signaturePublicKeyData,
|
|
66
|
+
} = response.data
|
|
67
|
+
|
|
68
|
+
const terminalPrivateKey = Buffer.from(terminalPrivateKeyData, 'base64')
|
|
69
|
+
const signaturePublicKey = Buffer.from(signaturePublicKeyData, 'base64')
|
|
70
|
+
|
|
71
|
+
if (!deviceId || deviceId.length !== 8) {
|
|
72
|
+
throw new Error('Received invalid device id')
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Try to validate basics to ensure we got valid data
|
|
76
|
+
try {
|
|
77
|
+
createPrivateKey({
|
|
78
|
+
key: terminalPrivateKey,
|
|
79
|
+
format: 'der',
|
|
80
|
+
type: 'pkcs8',
|
|
81
|
+
})
|
|
82
|
+
} catch (e) {
|
|
83
|
+
throw new Error('Received invalid private terminal key')
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
createPublicKey({
|
|
88
|
+
key: signaturePublicKey,
|
|
89
|
+
format: 'der',
|
|
90
|
+
type: 'spki',
|
|
91
|
+
})
|
|
92
|
+
} catch (e) {
|
|
93
|
+
throw new Error('Received invalid public signature key')
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
deviceId: BlueSharedDemoData.fields.findJsonName('deviceId').default,
|
|
98
|
+
terminalPrivateKey,
|
|
99
|
+
signaturePublicKey,
|
|
100
|
+
}
|
|
101
|
+
}
|