@i3m/non-repudiation-library
Library for handling non-repudiation proofs in the i3-MARKET ecosystem. It is a core element of the Conflict Resolution system in i3-MARKET (Read more here).
The library enables implementation of:
- The non-repudiation protocol of a data exchange
- The Conflict-Resolver Service, which can be queried to check completeness of the non-repudiation protocol and/or solve a dispute.
API reference documentation
Check the API
Usage
@i3m/non-repudiation-library
can be imported to your project with npm
:
npm install @i3m/non-repudiation-library
Then either require (Node.js CJS):
const nonRepudiationLibrary = require('@i3m/non-repudiation-library')
or import (JavaScript ES module):
import * as nonRepudiationLibrary from '@i3m/non-repudiation-library'
The appropriate version for browser or node is automatically exported.
You can also download the IIFE bundle, the ESM bundle or the UMD bundle and manually add it to your project, or, if you have already installed @i3m/non-repudiation-library
in your project, just get the bundles from node_modules/@i3m/non-repudiation-library/dist/bundles/
.
Example for an i3-MARKET Provider running the Non-Repudiation Protocol
The NRP provider is likely a service (machine) and therefore will likely run a server wallet, which has all the functionalities of the i3M Wallet but requires no user interaction. Data sharing agreements, however, should be signed by a person, hereby the provider operator, which is also responsible for creating the public-private key pair for the provider service.
Before starting the agreement you need:
-
A private key for signing the non-repudiation proofs in one of the EC supported curves (P-256, P-384, P-521). Key format must be JSON Web Key (JWK).
The provider operator can easily create the key pair with the generateKeys
utility function. For example:
const providerJwks = await nonRepudiationLibrary.generateKeys('ES256')
-
Import a DLT account to the (service) provider wallet with funds to execute the NRP In this example we assume that the provider runs a @i3m/server-wallet
.
You can easily create a provider server wallet and import a private key with funds.
Assuming you have the server-wallet encrypted storage in path STORAGE_PATH
encrypted with password STORAGE_PASSWORD
, and a DLT private key of an account with enough funds in DLT_PRIVATE_KEY
:
serverWalletBuilder = (await import('@i3m/server-wallet')).serverWalletBuilder
providerWallet = await serverWalletBuilder({ password: STORAGE_PASSWORD, reset: true, filepath: STORAGE_PATH })
await providerWallet.importDid({
alias: 'provider',
privateKey: DLT_PRIVATE_KEY
})
const availableIdentities = await providerWallet.identityList({ alias: 'provider' })
const providerDid = availableIdentities[0]
const providerDltAddress = nonRepudiationLibrary.getDltAddress(DLT_PRIVATE_KEY)
-
The provider operator has already agreed and signed with the consumer a DataSharingAgreement
that is stored in object variable dataSharingAgreement
and that contains a given DataExchangeAgreement
in dataSharingAgreement.dataExchangeAgreement
such as:
{
orig: '{"alg":"ES256","crv":"P-256","kty":"EC","x":"GjUjtzZWRjA9QSpXPDiN8-OO2Ui93mxbxhbLiP0lw4k","y":"YUtjUCIHbqq71Y467ub4Silqqms39RqR_bMPhiso4ws"}',
dest: '{"alg":"ES256","crv":"P-256","kty":"EC","x":"VXsBuOZwVjhofJV4kAhba6wn1EYDwUIkgXb2fVnL8xc","y":"h4fL5Qv4EYt7XdKqdIy1ZJs4_QWYDkY1zUzSoI61N7Y"}',
encAlg: 'A256GCM',
signingAlg: 'ES256',
hashAlg: 'SHA-256',
ledgerContractAddress: '0x8d407A1722633bDD1dcf221474be7a44C05d7c2F',
ledgerSignerAddress: '0x17bd12C2134AfC1f6E9302a532eFE30C19B9E903',
pooToPorDelay: 10000,
pooToPopDelay: 30000,
pooToSecretDelay: 180000
}
The provider operator has stored the dataSharingAgreement
in the provider wallet with:
await providerWallet.resourceCreate({
type: 'Contract',
resource: {
dataSharingAgreement,
keyPair: {
publicJwk: await nonRepudiationLibrary.parseJwk(providerJwks.publicJwk, true),
privateJwk: await nonRepudiationLibrary.parseJwk(providerJwks.privateJwk, true)
}
}
})
The wallet will verify the agreement schema, the signatures (made by the consumer and the provider operator), the provided keyPair
, and that the keyPair.publcJwk
matches dataSharingAgreement.dataExchangeAgreement.orig
.
And now you are ready to start a dataExchange
for a given block of data block
of a given DataExchangeAgreement
.
async nrp() => {
const dataExchangeAgreement: nonRepudiationLibrary.DataExchangeAgreement = dataSharingAgreement.dataExchangeAgreement
providerDltAgent = new nonRepudiationLibrary.I3mServerWalletAgentOrig(providerWallet, providerDid)
if (dataExchangeAgreement.ledgerSignerAddress !== await providerWallet.getAddress()) {
throw new Error('not maching')
}
const nrpProvider = new nonRepudiationLibrary.NonRepudiationProtocol.NonRepudiationOrig(dataExchangeAgreement, providerJwks.privateJwk, block, providerDltAgent)
const poo = await nrpProvider.generatePoO()
const resource = await providerWallet.resourceCreate({
type: 'NonRepudiationProof',
resource: poo.jws
})
...
...
await nrpProvider.verifyPoR(por)
const resource = await providerWallet.resourceCreate({
type: 'NonRepudiationProof',
resource: por.jws
})
const pop = await nrpProvider.generatePoP()
const resource = await providerWallet.resourceCreate({
type: 'NonRepudiationProof',
resource: pop.jws
})
...
verificationRequest = await nrpProvider.generateVerificationRequest()
...
const { payload } = await nonRepudiationLibrary.ConflictResolution.verifyResolution<nonRepudiationLibrary.VerificationResolutionPayload>(resolution, crsPublicKey)
if (payload.resolution === 'completed') {
}
)
nrp()
Example for an i3-MARKET Consumer running the Non-Repudiation Protocol
Before starting the protocol you need connect with your wallet, and set up the pair of public private keys that are required for the NRP.
You can easily create the key pair with the generateKeys
utility function:
const consumerJwks = await nonRepudiationLibrary.generateKeys('ES256')
We will assume that the consumer is using the i3-MARKET Wallet Desktop App
For connecting to the i3M-Wallet application, you need to pair with the wallet in order to obtain a session token:
- Set your wallet in pairing mode. A PIN appears in the screen
- Connect a browser to http://localhost:29170/pairing
- If session is ON (PIN is not requested), click "Remove session" and then "Start protocol"
- Fill in the PIN
- After successful pairing, click "Session to clipboard"
- Paste the copied session to create a
sessionObj
import { HttpInitiatorTransport, Session } from '@i3m/wallet-protocol'
import { WalletApi } from '@i3m/wallet-protocol-api'
const sessionObj = JSON.parse('<PASTE HERE>')
const transport = new HttpInitiatorTransport()
const session = await Session.fromJSON(transport, sessionObj)
consumerWallet = new WalletApi(session)
const availableIdentities = await providerWallet.identityList({ alias: 'consumer' })
const consumerDid = availableIdentities[0]
It is also assumed that consumer and provider have already agreed a DataSharingAgreement
that is stored in object variable dataSharingAgreement
and that contains a given DataExchangeAgreement
in dataSharingAgreement.dataExchangeAgreement
. Go to the provider example for an in-depth explanation of the dataExchangeAgreement
.
The agreement is stored in the consumer wallet:
await consumerWallet.resources.create({
type: 'Contract',
identity: consumerDid,
resource: {
dataSharingAgreement,
keyPair: {
publicJwk: await _pkg.parseJwk(consumerJwks.publicJwk, true),
privateJwk: await _pkg.parseJwk(consumerJwks.privateJwk, true)
}
}
})
Notice that, contrarily to what we did with the provider, we are bounding the data sharing agreement to the consumerDid (identity), and thus wallet will also verify that this identity is the one signing as 'consumer' the agreement. It did not make sense in the provider wallet since the signer is the provider operator (who uses another wallet) and not the actual (service) provider.
And now you are ready to start a DataExchange
for a given block of data block
of a given DataExchangeAgreement
.
async nrp() => {
const dataExchangeAgreement: nonRepudiationLibrary.DataExchangeAgreement = dataSharingAgreement.dataExchangeAgreement
const consumerDltAgent = new nonRepudiationLibrary.I3mWalletAgentDest(consumerWallet, dids.consumer)
const nrpConsumer = new nonRepudiationLibrary.NonRepudiationProtocol.NonRepudiationDest(dataExchangeAgreement, consumerJwks.privateJwk, consumerDltAgent)
...
await nrpConsumer.verifyPoO(poo.jws, cipherblock)
await consumerWallet.resources.create({
type: 'NonRepudiationProof',
resource: poo.jws
})
const por = await nrpConsumer.generatePoR()
await consumerWallet.resources.create({
type: 'NonRepudiationProof',
resource: por.jws
})
...
...
await nrpConsumer.verifyPoP(pop)
await consumerWallet.resources.create({
type: 'NonRepudiationProof',
resource: pop.jws
})
await nrpConsumer.getSecretFromLedger()
try {
const decryptedBlock = await nrpConsumer.decrypt()
} catch(error) {
const disputeRequest = await nrpConsumer.generateDisputeRequest()
...
const { resolutionPayload } = await nonRepudiationLibrary.ConflictResolution.verifyResolution<nonRepudiationLibrary.DisputeResolutionPayload>(disputeResolution)
if (resolutionPayload.resolution === 'accepted') {
} else {
}
}
)
nrp()