- :white_check_mark: SampledDataSignReadings (type: boolean) (units: -) **(vendor-specific)**
- :white_check_mark: SampledDataSignStartedReadings (type: boolean) (units: -) **(vendor-specific)**
- :white_check_mark: SampledDataSignUpdatedReadings (type: boolean) (units: -) **(vendor-specific)**
+- :white_check_mark: SigningMethod (type: string) (units: -) **(vendor-specific)**
- :white_check_mark: StartTxnSampledData (type: memberlist) (units: -) **(vendor-specific)**
### Version 2.0.x
VendorParametersKey.AlignedDataSignUpdatedReadings,
buildConfigKey(OCPP20ComponentName.AlignedDataCtrlr, VendorParametersKey.SignUpdatedReadings),
],
+ [
+ VendorParametersKey.MeterPublicKey,
+ buildConfigKey(OCPP20ComponentName.FiscalMetering, VendorParametersKey.PublicKey),
+ ],
[
VendorParametersKey.PublicKeyWithSignedMeterValue,
buildConfigKey(
VendorParametersKey.SampledDataSignUpdatedReadings,
buildConfigKey(OCPP20ComponentName.SampledDataCtrlr, VendorParametersKey.SignUpdatedReadings),
],
+ [
+ VendorParametersKey.SigningMethod,
+ buildConfigKey(OCPP20ComponentName.FiscalMetering, VendorParametersKey.SigningMethod),
+ ],
[
VendorParametersKey.StartTxnSampledData,
buildConfigKey(OCPP20ComponentName.SampledDataCtrlr, StandardParametersKey.TxStartedMeasurands),
isWithinInterval,
} from 'date-fns'
+import type { SigningMethodEnumType } from '../../../types/index.js'
+
import {
type ChargingStation,
getConfigurationKey,
timestamp,
transactionId,
},
- includePublicKey ? signingConfig.publicKeyHex : undefined
+ includePublicKey ? signingConfig.publicKeyHex : undefined,
+ signingConfig.signingMethod
)
return {
publicKeyIncluded: includePublicKey && signingConfig.publicKeyHex != null,
OCPP16VendorParametersKey.PublicKeyWithSignedMeterValue
)?.value
),
+ signingMethod: getConfigurationKey(chargingStation, OCPP16VendorParametersKey.SigningMethod)
+ ?.value as SigningMethodEnumType | undefined,
}
}
}
sampledValue.signedMeterValue = {
...generateSignedMeterData(
signedMeterDataParams,
- includePublicKey ? signingConfig.publicKeyHex : undefined
+ includePublicKey ? signingConfig.publicKeyHex : undefined,
+ signingConfig.signingMethod
),
}
publicKeyIncluded = includePublicKey && signingConfig.publicKeyHex != null
PersistenceEnumType,
PublicKeyWithSignedMeterValueEnumType,
ReasonCodeEnumType,
+ SigningMethodEnumType,
type VariableName,
} from '../../../types/index.js'
import {
[buildRegistryKey(OCPP20ComponentName.FiscalMetering, OCPP20VendorVariableName.SigningMethod)]: {
component: OCPP20ComponentName.FiscalMetering,
dataType: DataEnumType.string,
- defaultValue: 'ECDSA-secp256r1-SHA256',
+ defaultValue: SigningMethodEnumType.ECDSA_secp256r1_SHA256,
description:
'Method used to create the digital signature for signed meter values. See OCA Application Note v1.0 Table 12 for valid values.',
mutability: MutabilityEnumType.ReadOnly,
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
-import type { BootReasonEnumType } from '../../types/index.js'
+import type { BootReasonEnumType, SigningMethodEnumType } from '../../types/index.js'
import { buildConfigKey } from '../../charging-station/ConfigurationKeyUtils.js'
import { type ChargingStation, getConfigurationKey } from '../../charging-station/index.js'
chargingStation,
buildConfigKey(OCPP20ComponentName.FiscalMetering, VendorParametersKey.PublicKey)
)?.value
+ const signingMethod = getConfigurationKey(
+ chargingStation,
+ buildConfigKey(OCPP20ComponentName.FiscalMetering, VendorParametersKey.SigningMethod)
+ )?.value as SigningMethodEnumType | undefined
signingConfig = {
enabled: true,
meterSerialNumber: chargingStation.stationInfo.meterSerialNumber ?? 'UNKNOWN',
publicKeyWithSignedMeterValue: parsePublicKeyWithSignedMeterValue(
publicKeyWithSignedMeterValueStr
),
+ signingMethod,
transactionId,
}
}
import { createHash } from 'node:crypto'
-import { type JsonObject, MeterValueContext, MeterValueUnit } from '../../types/index.js'
+import {
+ type JsonObject,
+ MeterValueContext,
+ MeterValueUnit,
+ SigningMethodEnumType,
+} from '../../types/index.js'
import { roundTo } from '../../utils/index.js'
export interface SignedMeterData extends JsonObject {
encodingMethod: string
publicKey: string
signedMeterData: string
- signingMethod: string
+ signingMethod: SigningMethodEnumType
}
export interface SignedMeterDataParams {
transactionId: number | string
}
-const SIGNING_METHOD = 'ECDSA-secp256r1-SHA256'
+const DEFAULT_SIGNING_METHOD = SigningMethodEnumType.ECDSA_secp256r1_SHA256
const ENCODING_METHOD = 'OCMF'
const contextToTxCode = (context: MeterValueContext): string => {
export const generateSignedMeterData = (
params: SignedMeterDataParams,
- publicKeyHex?: string
+ publicKeyHex?: string,
+ signingMethod?: SigningMethodEnumType
): SignedMeterData => {
+ const resolvedSigningMethod = signingMethod ?? DEFAULT_SIGNING_METHOD
const txCode = contextToTxCode(params.context)
const meterValueKwh =
params.meterValueUnit === MeterValueUnit.KILO_WATT_HOUR
const simulatedSignature = createHash('sha256').update(JSON.stringify(ocmfPayload)).digest('hex')
- const ocmfString = `OCMF|${JSON.stringify(ocmfPayload)}|{"SA":"${SIGNING_METHOD}","SD":"${simulatedSignature}"}`
+ const ocmfString = `OCMF|${JSON.stringify(ocmfPayload)}|{"SA":"${resolvedSigningMethod}","SD":"${simulatedSignature}"}`
return {
encodingMethod: ENCODING_METHOD,
publicKey: publicKeyHex != null ? buildPublicKeyValue(publicKeyHex) : '',
signedMeterData: Buffer.from(ocmfString).toString('base64'),
- signingMethod: '',
+ signingMethod: resolvedSigningMethod,
}
}
import { BaseError } from '../../exception/index.js'
-import { PublicKeyWithSignedMeterValueEnumType, type SampledValue } from '../../types/index.js'
+import {
+ PublicKeyWithSignedMeterValueEnumType,
+ type SampledValue,
+ type SigningMethodEnumType,
+} from '../../types/index.js'
export interface SampledValueSigningConfig extends SigningConfig {
enabled: boolean
meterSerialNumber: string
publicKeyHex?: string
publicKeyWithSignedMeterValue: PublicKeyWithSignedMeterValueEnumType
+ signingMethod?: SigningMethodEnumType
}
const PUBLIC_KEY_WITH_SIGNED_METER_VALUE_VALUES = new Set<string>(
ConnectorPhaseRotation,
type OCPPConfigurationKey,
PublicKeyWithSignedMeterValueEnumType,
+ SigningMethodEnumType,
StandardParametersKey,
SupportedFeatureProfiles,
VendorParametersKey,
SampledDataSignReadings = 'SampledDataSignReadings',
SampledDataSignStartedReadings = 'SampledDataSignStartedReadings',
SampledDataSignUpdatedReadings = 'SampledDataSignUpdatedReadings',
+ SigningMethod = 'SigningMethod',
StartTxnSampledData = 'StartTxnSampledData',
}
import type { EmptyObject } from '../../EmptyObject.js'
import type { JsonObject } from '../../JsonType.js'
+import { type SigningMethodEnumType } from '../Configuration.js'
+
export enum OCPP16MeterValueContext {
INTERRUPTION_BEGIN = 'Interruption.Begin',
INTERRUPTION_END = 'Interruption.End',
encodingMethod: string
publicKey: string
signedMeterData: string
- signingMethod: string
+ signingMethod: '' | SigningMethodEnumType
}
import type { JsonObject } from '../../JsonType.js'
import type { CustomDataType, OCPP20UnitEnumType } from './Common.js'
+import { type SigningMethodEnumType } from '../Configuration.js'
+
export enum OCPP20LocationEnumType {
Body = 'Body',
Cable = 'Cable',
encodingMethod: string // maxLength: 50
publicKey: string // Base64 encoded, maxLength: 2500
signedMeterData: string // Base64 encoded, maxLength: 2500
- signingMethod: string // maxLength: 50
+ signingMethod: '' | SigningMethodEnumType // maxLength: 50
}
export interface OCPP20UnitOfMeasure extends JsonObject {
OncePerTransaction = 'OncePerTransaction',
}
+export enum SigningMethodEnumType {
+ ECDSA_brainpool256r1_SHA256 = 'ECDSA-brainpool256r1-SHA256',
+ ECDSA_brainpool384r1_SHA256 = 'ECDSA-brainpool384r1-SHA256',
+ ECDSA_secp192k1_SHA256 = 'ECDSA-secp192k1-SHA256',
+ ECDSA_secp192r1_SHA256 = 'ECDSA-secp192r1-SHA256',
+ ECDSA_secp256k1_SHA256 = 'ECDSA-secp256k1-SHA256',
+ ECDSA_secp256r1_SHA256 = 'ECDSA-secp256r1-SHA256',
+ ECDSA_secp384r1_SHA256 = 'ECDSA-secp384r1-SHA256',
+}
+
export type ConfigurationKeyType = StandardParametersKey | string | VendorParametersKey
export interface OCPPConfigurationKey extends JsonObject {
type OCPP16SignedMeterValue,
OCPP16VendorParametersKey,
OCPPVersion,
+ SigningMethodEnumType,
} from '../../../../src/types/index.js'
import { standardCleanup, withMockTimers } from '../../../helpers/TestLifecycleHelpers.js'
import { createMockChargingStation } from '../../ChargingStationTestUtils.js'
assert.strictEqual(typeof parsed.signedMeterData, 'string')
assert.strictEqual(typeof parsed.publicKey, 'string')
assert.strictEqual(parsed.encodingMethod, 'OCMF')
- assert.strictEqual(parsed.signingMethod, '')
+ assert.strictEqual(parsed.signingMethod, SigningMethodEnumType.ECDSA_secp256r1_SHA256)
})
})
OCPPVersion,
PublicKeyWithSignedMeterValueEnumType,
type SampledValueTemplate,
+ SigningMethodEnumType,
} from '../../../../src/types/index.js'
import { Constants } from '../../../../src/utils/index.js'
import { standardCleanup } from '../../../helpers/TestLifecycleHelpers.js'
)
assert.ok(sampledValue.signedMeterValue != null)
- assert.strictEqual(sampledValue.signedMeterValue.signingMethod, '')
+ assert.strictEqual(
+ sampledValue.signedMeterValue.signingMethod,
+ SigningMethodEnumType.ECDSA_secp256r1_SHA256
+ )
assert.strictEqual(sampledValue.signedMeterValue.encodingMethod, 'OCMF')
})
) as OCPP20SampledValue | undefined
assert.notStrictEqual(energySampledValue, undefined)
assert.ok(energySampledValue?.signedMeterValue != null)
- assert.strictEqual(energySampledValue.signedMeterValue.signingMethod, '')
+ assert.strictEqual(
+ energySampledValue.signedMeterValue.signingMethod,
+ SigningMethodEnumType.ECDSA_secp256r1_SHA256
+ )
assert.strictEqual(energySampledValue.signedMeterValue.encodingMethod, 'OCMF')
})
generateSignedMeterData,
type SignedMeterDataParams,
} from '../../../src/charging-station/ocpp/OCPPSignedMeterDataGenerator.js'
-import { MeterValueContext, MeterValueUnit } from '../../../src/types/index.js'
+import {
+ MeterValueContext,
+ MeterValueUnit,
+ SigningMethodEnumType,
+} from '../../../src/types/index.js'
const DEFAULT_PARAMS: SignedMeterDataParams = {
context: MeterValueContext.SAMPLE_PERIODIC,
assert.ok(decoded.startsWith('OCMF|'))
})
- await it('should set signingMethod to empty string when included in signedMeterData', () => {
+ await it('should set signingMethod to ECDSA-secp256r1-SHA256', () => {
const result = generateSignedMeterData(DEFAULT_PARAMS)
- assert.strictEqual(result.signingMethod, '')
+ assert.strictEqual(result.signingMethod, SigningMethodEnumType.ECDSA_secp256r1_SHA256)
})
await it('should set encodingMethod to OCMF', () => {
import assert from 'node:assert/strict'
import { describe, it } from 'node:test'
-import { OCPP16MeterValueFormat, type OCPP16SignedMeterValue } from '../../../../src/types/index.js'
+import {
+ OCPP16MeterValueFormat,
+ type OCPP16SignedMeterValue,
+ SigningMethodEnumType,
+} from '../../../../src/types/index.js'
await describe('OCPP 1.6 meter value types', async () => {
await describe('OCPP16MeterValueFormat', async () => {
encodingMethod: 'OCMF',
publicKey: 'b2NhOmJhc2UxNjphc24xOmZha2VrZXk=', // cspell:disable-line
signedMeterData: 'T0NNRnx7fXxmYWtlc2lnbmF0dXJl', // cspell:disable-line
- signingMethod: 'ECDSA-secp256r1-SHA256',
+ signingMethod: SigningMethodEnumType.ECDSA_secp256r1_SHA256,
}
assert.strictEqual(signedMeterValue.encodingMethod, 'OCMF')
- assert.strictEqual(signedMeterValue.signingMethod, 'ECDSA-secp256r1-SHA256')
+ assert.strictEqual(
+ signedMeterValue.signingMethod,
+ SigningMethodEnumType.ECDSA_secp256r1_SHA256
+ )
assert.ok(signedMeterValue.publicKey.length > 0)
assert.ok(signedMeterValue.signedMeterData.length > 0)
})
)
})
+ await it('should have SigningMethod key', () => {
+ assert.strictEqual(OCPP16VendorParametersKey.SigningMethod, 'SigningMethod')
+ })
+
await it('should have StartTxnSampledData key', () => {
assert.strictEqual(OCPP16VendorParametersKey.StartTxnSampledData, 'StartTxnSampledData')
})