Skip to content
Orillion logo
Soramitsu logo

Signing user email addresses

Every API request to authorize a user in the system requires a signature of the user's email address.

The signature is a standard Ed25519 signature over the BLAKE2b-256 hash of the email address, encoded as a Hex string:

signature = Hex( Ed25519-Sign( authPrivateKey, BLAKE2b-256( UTF-8(email) ) ) )
signature = Hex( Ed25519-Sign( authPrivateKey, BLAKE2b-256( UTF-8(email) ) ) )

Any cryptographic library that provides Ed25519 and BLAKE2b-256 can produce it, in any programming language. No blockchain SDK is required.

KEYS TO USE

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.

A private key is 64 characters long. In Iroha SDK format the public key is appended to it, making a 128-character string: the Iroha SDKs expect that longer form, every other library expects the 64-character key. If the key you copied is 128 characters, its first 64 characters are the private key.

A signature made with the wrong key pair is rejected with a 422 response.

Examples

The examples below sign the address alice@wonderland.space with the test key pair.

python
# pip install cryptography
import hashlib

from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey


def sign_email(email: str, auth_private_key_hex: str) -> str:
    # In Iroha SDK format the key is 128 characters: private key then 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()


print(sign_email(
    "alice@wonderland.space",
    "413b285d1819a6166b0daa762bb6bef2d082cffb9a13ce041cb0fda5e2f06dc3",
))
# => 57e7115dfb9faa9add2d2ceb321c20db8c1e7f468d2ffc122793fa61e8ed61581580faaeae83a07fe857894bb33defd61c4ba099b981020146fe8d2be00e630a
# pip install cryptography
import hashlib

from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey


def sign_email(email: str, auth_private_key_hex: str) -> str:
    # In Iroha SDK format the key is 128 characters: private key then 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()


print(sign_email(
    "alice@wonderland.space",
    "413b285d1819a6166b0daa762bb6bef2d082cffb9a13ce041cb0fda5e2f06dc3",
))
# => 57e7115dfb9faa9add2d2ceb321c20db8c1e7f468d2ffc122793fa61e8ed61581580faaeae83a07fe857894bb33defd61c4ba099b981020146fe8d2be00e630a
js
// npm install @noble/hashes
import { createPrivateKey, sign } from 'node:crypto'
import { blake2b } from '@noble/hashes/blake2b'

// DER header that turns 32 raw key bytes into a PKCS#8 Ed25519 key
const PKCS8_ED25519_PREFIX = '302e020100300506032b657004220420'

/**
 * @param {string} email
 * @param {string} authPrivateKeyHex - in Iroha SDK format the key is 128
 *                                     characters: private key then public key
 * @returns {string} - email signature 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')
}

console.log(
  signEmail(
    'alice@wonderland.space',
    '413b285d1819a6166b0daa762bb6bef2d082cffb9a13ce041cb0fda5e2f06dc3'
  )
)
// => 57e7115dfb9faa9add2d2ceb321c20db8c1e7f468d2ffc122793fa61e8ed61581580faaeae83a07fe857894bb33defd61c4ba099b981020146fe8d2be00e630a
// npm install @noble/hashes
import { createPrivateKey, sign } from 'node:crypto'
import { blake2b } from '@noble/hashes/blake2b'

// DER header that turns 32 raw key bytes into a PKCS#8 Ed25519 key
const PKCS8_ED25519_PREFIX = '302e020100300506032b657004220420'

/**
 * @param {string} email
 * @param {string} authPrivateKeyHex - in Iroha SDK format the key is 128
 *                                     characters: private key then public key
 * @returns {string} - email signature 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')
}

console.log(
  signEmail(
    'alice@wonderland.space',
    '413b285d1819a6166b0daa762bb6bef2d082cffb9a13ce041cb0fda5e2f06dc3'
  )
)
// => 57e7115dfb9faa9add2d2ceb321c20db8c1e7f468d2ffc122793fa61e8ed61581580faaeae83a07fe857894bb33defd61c4ba099b981020146fe8d2be00e630a
kotlin
// 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()
}
// 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()
}

Test vector

Before you call the API, check your implementation against these values. All of them are public example data, not credentials of a real account:

FieldValue
Email addressalice@wonderland.space
Authorization private key413b285d1819a6166b0daa762bb6bef2d082cffb9a13ce041cb0fda5e2f06dc3
Authorization public key7fbedb314a9b0c00caef967ac5cabb982ec45da828a0c58a9aafc854f32422ac
Signature57e7115dfb9faa9add2d2ceb321c20db8c1e7f468d2ffc122793fa61e8ed61581580faaeae83a07fe857894bb33defd61c4ba099b981020146fe8d2be00e630a

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
POST /auth/api/v1/authentication-management/session

For the full request and the tokens it returns, see Authorizing an account.