Skip to content

Commit

Permalink
Merge branch 'main' into chore/shared-config-module
Browse files Browse the repository at this point in the history
  • Loading branch information
wcalderipe committed Mar 25, 2024
2 parents fee7d14 + f9d815c commit 4d6e118
Show file tree
Hide file tree
Showing 50 changed files with 1,221 additions and 985 deletions.
64 changes: 64 additions & 0 deletions .github/workflows/signature.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: '@narval/signature CI'

on:
push:
paths:
- packages/signature/**
- .github/workflows/signature.yml
- jest.config.ts
- jest.preset.js
- .eslintrc.json
- .prettierrc
- package.json
- package-lock.json

jobs:
build-and-test:
name: Build and test

runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@master

- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: '20.4.0'

- name: Install dependencies
run: |
make install/ci
- name: Code format
shell: bash
run: |
make signature/format/check
make signature/lint/check
- name: Test types
shell: bash
run: |
make signature/test/type
- name: Test upstream application types
shell: bash
run: |
make policy-engine/test/type
- name: Test unit
shell: bash
run: |
make signature/test/unit
- name: Send Slack notification on failure
if: failure() && github.ref == 'refs/heads/main'
uses: 8398a7/action-slack@v3
with:
username: GitHub
author_name: '@narval/signature CI failed'
status: ${{ job.status }}
fields: message,commit,author
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
| ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| [@app/armory](./apps/armory/README.md) | <a href="https://github.com/narval-xyz/narval/actions/workflows/armory.yml" target="_blank"><img src="https://github.com/narval-xyz/narval/actions/workflows/armory.yml/badge.svg?branch=main" alt="@app/armory CI status" /></a> |
| [@app/policy-engine](./apps/policy-engine/README.md) | <a href="https://github.com/narval-xyz/narval/actions/workflows/policy-engine.yml" target="_blank"><img src="https://github.com/narval-xyz/narval/actions/workflows/policy-engine.yml/badge.svg?branch=main" alt="@app/policy-engine CI status" /></a> |
| [@narval/encryption-module](./packages/encryption-module/README.md) | N/A |
| [@narval/encryption](./packages/encryption/README.md) | <a href="https://github.com/narval-xyz/narval/actions/workflows/encryption.yml" target="_blank"><img src="https://github.com/narval-xyz/narval/actions/workflows/encryption.yml/badge.svg?branch=main" alt="@narval/encryption CI status" /></a> |
|
| [@narval/policy-engine-shared](./packages/policy-engine-shared/README.md) | <a href="https://github.com/narval-xyz/narval/actions/workflows/policy-engine-shared.yml" target="_blank"><img src="https://github.com/narval-xyz/narval/actions/workflows/policy-engine-shared.yml/badge.svg?branch=main" alt="@narval/policy-engine-shared CI status" /></a> |
| [@narval/signature](./packages/signature/README.md) | N/A |
| [@narval/signature](./packages/signature/README.md) | <a href="https://github.com/narval-xyz/narval/actions/workflows/signature.yml" target="_blank"><img src="https://github.com/narval-xyz/narval/actions/workflows/signature.yml/badge.svg?branch=main" alt="@narval/signature CI status" /></a> |
|
| [@narval/transaction-request-intent](./packages/transaction-request-intent/README.md) | <a href="https://github.com/narval-xyz/narval/actions/workflows/transaction-request-intent.yml" target="_blank"><img src="https://github.com/narval-xyz/narval/actions/workflows/transaction-request-intent.yml/badge.svg?branch=main" alt="@narval/transaction-request-intent CI status" /></a> |

## Getting started
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Feed, HistoricalTransfer, JwtString } from '@narval/policy-engine-shared'
import { Payload, SigningAlg, hash, hexToBase64Url, privateKeyToJwk, signJwt } from '@narval/signature'
import { Payload, SigningAlg, hash, hexToBase64Url, secp256k1PrivateKeyToJwk, signJwt } from '@narval/signature'
import { Injectable } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import { mapValues, omit } from 'lodash/fp'
Expand Down Expand Up @@ -37,7 +37,7 @@ export class HistoricalTransferFeedService implements DataFeed<HistoricalTransfe
}

const now = Math.floor(Date.now() / 1000)
const jwk = privateKeyToJwk(this.getPrivateKey())
const jwk = secp256k1PrivateKeyToJwk(this.getPrivateKey())
const payload: Payload = {
data: hash(data),
sub: account.address,
Expand Down
4 changes: 2 additions & 2 deletions apps/armory/src/data-feed/core/service/price-feed.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Action, AssetId, Feed, JwtString } from '@narval/policy-engine-shared'
import { Payload, SigningAlg, hash, hexToBase64Url, privateKeyToJwk, signJwt } from '@narval/signature'
import { Payload, SigningAlg, hash, hexToBase64Url, secp256k1PrivateKeyToJwk, signJwt } from '@narval/signature'
import { InputType, Intents, safeDecode } from '@narval/transaction-request-intent'
import { Injectable } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
Expand Down Expand Up @@ -36,7 +36,7 @@ export class PriceFeedService implements DataFeed<Prices> {
}

const now = Math.floor(Date.now() / 1000)
const jwk = privateKeyToJwk(this.getPrivateKey())
const jwk = secp256k1PrivateKeyToJwk(this.getPrivateKey())
const payload: Payload = {
data: hash(data),
sub: account.address,
Expand Down
6 changes: 3 additions & 3 deletions apps/devtool/src/app/components/EditorComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client'

import Editor from '@monaco-editor/react'
import { JWK, Payload, SigningAlg, hash, hexToBase64Url, signJwt } from '@narval/signature'
import { Jwk, Payload, SigningAlg, hash, hexToBase64Url, signJwt } from '@narval/signature'
import { getAccount, signMessage } from '@wagmi/core'
import axios from 'axios'
import Image from 'next/image'
Expand Down Expand Up @@ -46,8 +46,8 @@ const EditorComponent = () => {
const address = getAccount(config).address
if (!address) throw new Error('No address connected')

// Need real JWK
const jwk: JWK = {
// Need real Jwk
const jwk: Jwk = {
kty: 'EC',
crv: 'secp256k1',
alg: SigningAlg.ES256K,
Expand Down
7 changes: 6 additions & 1 deletion apps/policy-engine/src/engine/app.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,12 @@ export class AppController {
body
})

return this.evaluationService.evaluate(FIXTURE.ORGANIZATION.id, body)
const result = await this.evaluationService.evaluate(FIXTURE.ORGANIZATION.id, body)

this.logger.log({
message: 'Evaluation result',
body: result
})
}

@Post('/evaluation-demo')
Expand Down
21 changes: 8 additions & 13 deletions apps/policy-engine/src/engine/core/service/signing.service.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { JsonWebKey, toHex } from '@narval/policy-engine-shared'
import { toHex } from '@narval/policy-engine-shared'
import {
Alg,
Payload,
PrivateKey,
PublicKey,
SigningAlg,
buildSignerEip191,
buildSignerEs256k,
privateKeyToJwk,
secp256k1PrivateKeyToJwk,
signJwt
} from '@narval/signature'
import { Injectable } from '@nestjs/common'
Expand All @@ -17,8 +19,8 @@ type KeyGenerationOptions = {
}

type KeyGenerationResponse = {
publicKey: JsonWebKey
privateKey?: JsonWebKey
publicKey: PublicKey
privateKey?: PrivateKey
}

type SignOptions = {
Expand All @@ -32,7 +34,7 @@ export class SigningService {
async generateSigningKey(alg: Alg, options?: KeyGenerationOptions): Promise<KeyGenerationResponse> {
if (alg === Alg.ES256K) {
const privateKey = toHex(secp256k1.utils.randomPrivateKey())
const privateJwk = privateKeyToJwk(privateKey, options?.keyId)
const privateJwk = secp256k1PrivateKeyToJwk(privateKey, options?.keyId)

// Remove the privateKey from the public jwk
const publicJwk = {
Expand All @@ -49,21 +51,14 @@ export class SigningService {
throw new Error('Unsupported algorithm')
}

async sign(payload: Payload, jwk: JsonWebKey, opts: SignOptions = {}): Promise<string> {
async sign(payload: Payload, jwk: PrivateKey, opts: SignOptions = {}): Promise<string> {
const alg: SigningAlg = opts.alg || jwk.alg
if (alg === SigningAlg.ES256K) {
if (!jwk.d) {
throw new Error('Missing private key')
}
const pk = jwk.d

const jwt = await signJwt(payload, jwk, opts, buildSignerEs256k(pk))

return jwt
} else if (alg === SigningAlg.EIP191) {
if (!jwk.d) {
throw new Error('Missing private key')
}
const pk = jwk.d

const jwt = await signJwt(payload, jwk, opts, buildSignerEip191(pk))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export class TenantService {
this.tenantRepository.savePolicyStore(clientId, stores.policy)
])

this.logger.log('Tenant data stores synced', { clientId, stores })
this.logger.log('Tenant data stores synced', { clientId })

return true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
Then,
toHex
} from '@narval/policy-engine-shared'
import { SigningAlg, buildSignerEip191, hash, privateKeyToJwk, signJwt } from '@narval/signature'
import { SigningAlg, buildSignerEip191, hash, secp256k1PrivateKeyToJwk, signJwt } from '@narval/signature'
import { Path, PathValue } from '@nestjs/config'
import { Test, TestingModule } from '@nestjs/testing'
import { Config, load } from '../../../../policy-engine.config'
Expand All @@ -26,7 +26,7 @@ const ONE_ETH = toHex(BigInt('1000000000000000000'))
const UNSAFE_ENGINE_PRIVATE_KEY = '0x7cfef3303797cbc7515d9ce22ffe849c701b0f2812f999b0847229c47951fca5'

const getJwt = (option: { privateKey: Hex; request: Request; sub: string }): Promise<JwtString> => {
const jwk = privateKeyToJwk(option.privateKey)
const jwk = secp256k1PrivateKeyToJwk(option.privateKey)
const signer = buildSignerEip191(option.privateKey)

return signJwt(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,20 @@ import {
Entities,
EvaluationRequest,
EvaluationResponse,
JsonWebKey,
JwtString,
Policy
} from '@narval/policy-engine-shared'
import {
Hex,
Payload,
PrivateKey,
PublicKey,
SigningAlg,
base64UrlToHex,
buildSignerEip191,
decode,
hash,
privateKeyToJwk,
publicKeyToJwk,
secp256k1PrivateKeyToJwk,
signJwt,
verifyJwt
} from '@narval/signature'
Expand Down Expand Up @@ -187,9 +187,10 @@ export class OpenPolicyAgentEngine implements Engine<OpenPolicyAgentEngine> {
})
}

const jwk = publicKeyToJwk(credential.pubKey as Hex)
const { key } = credential

const validJwt = await verifyJwt(signature, jwk)
console.log('### credential', credential)
const validJwt = await verifyJwt(signature, key)

if (validJwt.payload.requestHash !== message) {
throw new OpenPolicyAgentException({
Expand Down Expand Up @@ -304,8 +305,8 @@ export class OpenPolicyAgentEngine implements Engine<OpenPolicyAgentEngine> {
}

private async sign(params: { principalCredential: CredentialEntity; message: string }): Promise<JwtString> {
const engineJwk: JsonWebKey = privateKeyToJwk(this.privateKey)
const principalJwk = publicKeyToJwk(params.principalCredential.pubKey as Hex)
const engineJwk: PrivateKey = secp256k1PrivateKeyToJwk(this.privateKey)
const principalJwk: PublicKey = params.principalCredential.key

const payload: Payload = {
requestHash: params.message,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ test_principal {
user = principal with input as request
with data.entities as entities

user == {"uid": "test-bob-uid", "role": "root"}
user == {"id": "test-bob-uid", "role": "root"}

groups = principalGroups with input as request
with data.entities as entities
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,23 +115,23 @@ request = {
entities = {
"users": {
"test-bob-uid": {
"uid": "test-bob-uid",
"id": "test-bob-uid",
"role": "root",
},
"test-alice-uid": {
"uid": "test-alice-uid",
"id": "test-alice-uid",
"role": "member",
},
"test-bar-uid": {
"uid": "test-bar-uid",
"id": "test-bar-uid",
"role": "admin",
},
"test-foo-uid": {
"uid": "test-foo-uid",
"id": "test-foo-uid",
"role": "admin",
},
"0xaaa8ee1cbaa1856f4550c6fc24abb16c5c9b2a43": {
"uid": "0xaaa8ee1cbaa1856f4550c6fc24abb16c5c9b2a43",
"id": "0xaaa8ee1cbaa1856f4550c6fc24abb16c5c9b2a43",
"role": "admin",
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,10 @@ e2e_req = {

e2e_entities = {
"users": {
"u:root_user": {"uid": "u:root_user", "role": "root"},
"[email protected]": {"uid": "[email protected]", "role": "admin"},
"[email protected]": {"uid": "[email protected]", "role": "admin"},
"[email protected]": {"uid": "[email protected]", "role": "admin"},
"u:root_user": {"id": "u:root_user", "role": "root"},
"[email protected]": {"id": "[email protected]", "role": "admin"},
"[email protected]": {"id": "[email protected]", "role": "admin"},
"[email protected]": {"id": "[email protected]", "role": "admin"},
},
"userGroups": {
"ug:dev-group": {"uid": "ug:dev-group", "name": "Dev", "users": ["[email protected]"]},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ getApprovalsCount(possibleApprovers) = result {
checkApproval(approval) = result {
approval.countPrincipal == true
approval.approvalEntityType == "Narval::User"
possibleApprovers = {entity | entity = approval.entityIds[_]} | {principal.uid}
possibleApprovers = {entity | entity = approval.entityIds[_]} | {principal.id}
result = getApprovalsCount(possibleApprovers)
}

Expand All @@ -39,7 +39,7 @@ checkApproval(approval) = result {
approval.approvalEntityType == "Narval::User"
possibleApprovers = {entity |
entity = approval.entityIds[_]
entity != principal.uid
entity != principal.id
}
result = getApprovalsCount(possibleApprovers)
}
Expand All @@ -53,7 +53,7 @@ checkApproval(approval) = result {
entity = approval.entityIds[_]
users = userGroupsEntities[entity].users
user = users[_]
} | {principal.uid}
} | {principal.id}

result = getApprovalsCount(possibleApprovers)
}
Expand All @@ -65,7 +65,7 @@ checkApproval(approval) = result {
entity = approval.entityIds[_]
users = userGroupsEntities[entity].users
user = users[_]
user != principal.uid
user != principal.id
}

result = getApprovalsCount(possibleApprovers)
Expand All @@ -76,21 +76,21 @@ checkApproval(approval) = result {
checkApproval(approval) = result {
approval.countPrincipal == true
approval.approvalEntityType == "Narval::UserRole"
possibleApprovers = {user.uid |
possibleApprovers = {user.id |
user = usersEntities[_]
user.role in approval.entityIds
} | {principal.uid}
} | {principal.id}

result = getApprovalsCount(possibleApprovers)
}

checkApproval(approval) = result {
approval.countPrincipal == false
approval.approvalEntityType == "Narval::UserRole"
possibleApprovers = {user.uid |
possibleApprovers = {user.id |
user = usersEntities[_]
user.role in approval.entityIds
user.uid != principal.uid
user.id != principal.id
}

result = getApprovalsCount(possibleApprovers)
Expand Down
Loading

0 comments on commit 4d6e118

Please sign in to comment.