Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions .github/workflows/vercel-deploy-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,17 @@ jobs:
vercel:
runs-on: ubuntu-latest
name: "Deploy application"
env:
VERCEL_ORG_ID: ${{ secrets.ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.PROJECT_ID }}
steps:
- uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5
- uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c
with:
node-version: '18'
node-version: '20'
registry-url: https://registry.npmjs.org/
- uses: amondnet/vercel-action@225d234cfe5340ca1f9a6cd158338126b5b6845f
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.ORG_ID}}
vercel-project-id: ${{ secrets.PROJECT_ID}}
working-directory: ./
vercel-args: '--prod'

- name: Deploy application
run: >
npx --yes vercel@59.1.3 deploy --prod --yes
--token "${{ secrets.VERCEL_TOKEN }}"
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{
"private": true,
"packageManager": "pnpm@8.7.6",
"engines": {
"node": "22.x"
},
"scripts": {
"format": "prettier-eslint --include-dot-files \"**/*.{js,ts,mts,vue,json,md}\"",
"format:check": "pnpm format --list-different",
Expand Down
8 changes: 7 additions & 1 deletion src/getting-started/calling-api-endpoints.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@

Once your registration application has been approved, the connection credentials are sent to the email address you provided during registration.

::: tip NOTE

The API is intended for automated integrations. To upload, flag or download fraud data manually, use the FIB Web App: it signs every request for you, and no code is needed. See [Tutorials: Web App](../tutorials-web.md), for example [Downloading fraud data](../tutorials-web/downloading-fraud-data.md).

:::

Using these credentials, you can implement calls to the FIB API endpoints into the code of your own application or system.

Following the basic API architecture, your application or system—the _API client_ in this relationship—connects to the FIB _API server_.

To integrate your API client with the FIB API server, you can use different programming languages and frameworks, including Python, Java, Node.js, RUST, depending on the requirements of your own application or system.

Some tutorials in this documentation provide code snippets in Kotlin/Java that are required in order to perform Iroha-related operations.
Some tutorials in this documentation provide code snippets for the operations that require a signature, such as [Signing user email addresses](../tutorials-api/signing-user-email-addresses.md).

The provided FIB [API endpoints](../api-specification.md) allow users to access the network's blockchain data, enabling users to perform operations such as [submitting](../tutorials-api/submitting-a-contribution.md), [retrieving](../tutorials-api/retrieving-contributions.md) and [flagging contributions](../tutorials-api/flagging-a-contribution.md), etc.

Expand Down
6 changes: 6 additions & 0 deletions src/overview/web-interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ Also see the following related tutorials:
- [Authorizing an account](../tutorials-api/authorizing-an-account.md)
- [Signing user email addresses](../tutorials-api/signing-user-email-addresses.md)

::: tip NOTE

If the private key is displayed as a 128-character string, it is the private key followed by the public key. Whenever an API tutorial asks for a private key, use its first 64 characters.

:::

### 'Blockchain' key pair {#bkp}

Displays public and private keys of the **Blockchain** key pair. These keys are unique for every user on the FIB network and are generated automatically at the time of account registration.
Expand Down
2 changes: 2 additions & 0 deletions src/tutorials-api/authorizing-an-account.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ For instructions on how to generate the `signature` string, see [Signing user em

The `authPublicKeyHex` string is taken from the user's **Authorization** key pair on the FIB Web App **Profile** screen. For details, see [Web App UI: 'Authorization' key pair](../overview/web-interface.md#akp).

The `signature` string must be produced with the private key of that same **Authorization** key pair. A request signed with the **Blockchain** key pair is rejected with a `422` response.

:::

### Expected result
Expand Down
182 changes: 92 additions & 90 deletions src/tutorials-api/signing-user-email-addresses.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,129 +2,131 @@

Every API request to [authorize a user in the system](../api-specification/auth-controller/authorizing-a-user-in-the-system.md) requires a signature of the user's email address.

The exact implementation to your system may vary depending on the specific SDK or programming language being used. However, the overall sequence of events to sign a user email address should be the following:
The signature is a standard Ed25519 signature over the BLAKE2b-256 hash of the email address, encoded as a Hex string:

1. Create a `keyPair` object from the public and private keys of the user's [**Authorization** key pair](../overview/web-interface.md#authorization-key-pair).
2. Obtain a `signature` of the user's email address.
3. Encode the `signature` as a Hex string.

The resulting encoded `signature` Hex string can be used as a part of the body for requests to the following endpoint:
```
signature = Hex( Ed25519-Sign( authPrivateKey, BLAKE2b-256( UTF-8(email) ) ) )
```

> [Authorizing a user in the system](../api-specification/auth-controller/authorizing-a-user-in-the-system.md)
Any cryptographic library that provides Ed25519 and BLAKE2b-256 can produce it, in any programming language. No blockchain SDK is required.

```http
POST /auth/api/v1/authentication-management/session
```
::: warning KEYS TO USE

## Iroha SDK references
Sign with the **Authorization** key pair, not with the **Blockchain** one. Both key pairs are shown on the FIB Web App **Profile** screen. For details, see [Web App UI: 'Authorization' key pair](../overview/web-interface.md#akp).

You can use [any Iroha SDK available](../index.md#what-is-iroha-2) to sign a user's email address. Below are references on how to sign a transaction using the following Iroha SDKs:
If the private key on that screen is 128 characters long, it is the private key followed by the public key. Use its first 64 characters as the private key.

::: code-group Iroha SDK references
A signature made with the wrong key pair is rejected with a `422` response.

```kotlin [Iroha Java/Kotlin SDK]
// Your package value
package something
:::

// Import dependencies
import jp.co.soramitsu.iroha2.keyPairFromHex
import jp.co.soramitsu.iroha2.sign
import jp.co.soramitsu.iroha2.toHex
## Examples

class SimpleSigner {
The examples below sign the address `alice@wonderland.space` with the [test key pair](#test-vector).

fun main(args: Array<String>) {
if (args.size != 3) {
println("Specify public_key, private_key and text to sign")
return
}
::: code-group

// The public key of the user's 'Authorization' key pair
val publicKey = args[0]
```python [Python]
# pip install cryptography
import hashlib

// The private key of the user's 'Authorization' key pair
val privateKey = args[1]
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey

// The user's email address
val toSign = args[2]

// Create a `keyPair` object from the Hex string of the public and private keys of the user's 'Authorization' key pair
val keyPair = keyPairFromHex(publicKey, privateKey)
def sign_email(email: str, auth_private_key_hex: str) -> str:
# A 128-character key is the private key followed by the public key.
key = Ed25519PrivateKey.from_private_bytes(bytes.fromhex(auth_private_key_hex[:64]))
digest = hashlib.blake2b(email.encode("utf-8"), digest_size=32).digest()
return key.sign(digest).hex()

// Obtain a `signature` of the user's email address
val signature = keyPair.private.sign(toSign.toByteArray(Charsets.UTF_8)).toHex()

// Encode the `signature` as a Hex string
println("Signed message (Hex): $signature")
}
}
print(sign_email(
"alice@wonderland.space",
"413b285d1819a6166b0daa762bb6bef2d082cffb9a13ce041cb0fda5e2f06dc3",
))
# => 57e7115dfb9faa9add2d2ceb321c20db8c1e7f468d2ffc122793fa61e8ed61581580faaeae83a07fe857894bb33defd61c4ba099b981020146fe8d2be00e630a
```

```js [Iroha JavaScript SDK]
// @ts-check
```js [Node.js]
// npm install @noble/hashes
import { createPrivateKey, sign } from 'node:crypto'
import { blake2b } from '@noble/hashes/blake2b'

import { crypto } from '@iroha2/crypto-target-node' // version: 1.1.1
import { freeScope } from '@iroha2/crypto-core' // version: 1.1.1
// DER header that turns 32 raw key bytes into a PKCS#8 Ed25519 key
const PKCS8_ED25519_PREFIX = '302e020100300506032b657004220420'

/**
* @param {string} publicKeyHex - ed25519 pub key hex
* @param {string} privateKeyHex - ed25519 private key hex
* @param {string} email
* @param {string} authPrivateKeyHex - a 128-character key is the private key
* followed by the public key
* @returns {string} - email signature hex
*/
function createEmailSignature(publicKeyHex, privateKeyHex, email) {
return freeScope(() => {
const keyPair = crypto.KeyPair.fromJSON({
public_key: 'ed0120' + publicKeyHex,
private_key: {
digest_function: 'ed25519',
payload: privateKeyHex
}
})

const hashedEmail = crypto.Hash.hash(
'array',
new TextEncoder().encode(email)
).bytes()

return keyPair.sign('array', hashedEmail).payload('hex')
function signEmail(email, authPrivateKeyHex) {
const key = createPrivateKey({
key: Buffer.from(
PKCS8_ED25519_PREFIX + authPrivateKeyHex.slice(0, 64),
'hex'
),
format: 'der',
type: 'pkcs8'
})

const digest = Buffer.from(
blake2b(new TextEncoder().encode(email), { dkLen: 32 })
)

return sign(null, digest, key).toString('hex')
}

// example signature
const signature = createEmailSignature(
'7fbedb314a9b0c00caef967ac5cabb982ec45da828a0c58a9aafc854f32422ac',
'413b285d1819a6166b0daa762bb6bef2d082cffb9a13ce041cb0fda5e2f06dc37fbedb314a9b0c00caef967ac5cabb982ec45da828a0c58a9aafc854f32422ac',
'alice@wonderland.space'
console.log(
signEmail(
'alice@wonderland.space',
'413b285d1819a6166b0daa762bb6bef2d082cffb9a13ce041cb0fda5e2f06dc3'
)
)

console.log(signature)
// => 9729e8fbcd425bfe48809cc996c9e6d3cecddf0848a51d8758582b3c84bb2caca8e41a8290018aa7064f0b9ec61d2b1a155d5e4c772bc992d918528cf6cb6308
// => 57e7115dfb9faa9add2d2ceb321c20db8c1e7f468d2ffc122793fa61e8ed61581580faaeae83a07fe857894bb33defd61c4ba099b981020146fe8d2be00e630a
```

```python [Iroha Python SDK]
# Import dependency
import iroha

# Example ed25519 key pair
key_pair = iroha.KeyPair.from_json("""
{
"public_key": "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0",
"private_key": {
"digest_function": "ed25519",
"payload": "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"
}
```kotlin [Kotlin/Java]
// Import dependencies
import jp.co.soramitsu.iroha2.keyPairFromHex
import jp.co.soramitsu.iroha2.sign
import jp.co.soramitsu.iroha2.toHex

// The SDK applies the BLAKE2b-256 hash inside `sign`,
// so the email address is passed to it as raw bytes.
fun signEmail(
email: String,
authPublicKeyHex: String,
authPrivateKeyHex: String,
): String {
val keyPair = keyPairFromHex(authPublicKeyHex, authPrivateKeyHex)

return keyPair.private.sign(email.toByteArray(Charsets.UTF_8)).toHex()
}
""")
```

:::

# Hash the user's email address:
hashed_email = iroha.hash(b"email@address")
## Test vector {#test-vector}

# Sign the user's email address:
signature = key_pair.sign(bytes(hashed_email))
Before you call the API, check your implementation against these values. All of them are public example data, not credentials of a real account:

# Retrieve the encoded Hex string of the user's `signature`
print(f"Encoded signature:\n{bytes(signature).hex()}")
| Field | Value |
| --- | --- |
| Email address | `alice@wonderland.space` |
| Authorization private key | `413b285d1819a6166b0daa762bb6bef2d082cffb9a13ce041cb0fda5e2f06dc3` |
| Authorization public key | `7fbedb314a9b0c00caef967ac5cabb982ec45da828a0c58a9aafc854f32422ac` |
| Signature | `57e7115dfb9faa9add2d2ceb321c20db8c1e7f468d2ffc122793fa61e8ed61581580faaeae83a07fe857894bb33defd61c4ba099b981020146fe8d2be00e630a` |

Ed25519 signatures are deterministic, so a correct implementation returns exactly this signature.

## Using the signature

Send the resulting Hex string as the `signature` field of the authorization request:

```http
POST /auth/api/v1/authentication-management/session
```

:::
For the full request and the tokens it returns, see [Authorizing an account](./authorizing-an-account.md).
Loading