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.
# 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// 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// 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:
| 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:
POST /auth/api/v1/authentication-management/sessionPOST /auth/api/v1/authentication-management/sessionFor the full request and the tokens it returns, see Authorizing an account.