FAQ
How do I generate a Avatar?
As in glossary, technically, avatars/
is the same as Ethereum wallet: they're both secp256k1
elliptic
curve asymmetric keypair.
So, generating and managing Persona should be the same as generating /
managing wallets. Search for an secp256k1
SDK in your project's
programming language to generate one.
Basiclly,
- secret key should be 32-bytes long
- public key has 2 shapes:
- Uncompressed(65-bytes, started with
0x04
), or - compressed(33-bytes, started with
0x02
or0x03
, in most case0x02
)
- Uncompressed(65-bytes, started with
I got "bad signature"
error in POST /v1/proof
- Check if
created_at
anduuid
is the same asPOST /v1/proof/payload
result. - Check if you're using Ethereum Personal Sign.
- If your DApp is using wallet SDK (e.g. MetaMask), make sure you're using correct signature RPC method.
- In pseudo-code,
personal_sign
is:
sign(keccak256("\x19Ethereum Signed Message:\n" + dataToSign.length + dataToSign)))
- Make sure
dataToSign.length
is length of bytes (Buffer
length in some language), not UTF-8 code point length.assert.Equal(4, len([]byte("🐎"))) // Not 1
- Signature should be 65-bytes long.
<<r::binary-size(32), s::binary-size(32), v::binary-size(1)>> = signature_binary
# v should be 0x00 or 0x01 or 0x1B or 0x1C
# 0x00 is equivalent to 0x1B
# 0x01 is equivalent to 0x1C
Here's a test case.
// Psuedo-code
// Given a signature payload
payload := "Test123123!"
// And a secret key
secret_key := "0x9deba3488458c0314e5fef8920d3b117dd76415569cf270db8fd864896c02732"
// The personal sign result should be
assert.Equal(
"0x52f210dadad13c4c8d0656e7380300a367a056631cf26950baa7de4f580187795c76b5fc94de5bd0b8af4d5df432687d900402cba86a12570af56be35ba8d56401",
personal_sign(secret_key, payload).ToHexString()
)
Sample code snippets for avatar-generating and signing
- Rust
- Go
- JavaScript
[dependencies]
libsecp256k1 = "0.7"
hex = "0.4"
sha3 = "0.10" # Keccak256
use libsecp256k1::{SecretKey, Message};
use sha3::{Keccak256, Digest};
const SECRET_KEY: &str = "9DEBA3488458C0314E5FEF8920D3B117DD76415569CF270DB8FD864896C02732";
const SIGN_PAYLOAD: &str = "Test123123!";
fn main() {
// Raw sign message
let sign_payload: String = SIGN_PAYLOAD.to_string();
// SecretKey instance
let secret_key = SecretKey::parse_slice(hex::decode(SECRET_KEY).unwrap().as_slice()).unwrap();
// Sign it
let personal_signature = personal_sign(&sign_payload, &secret_key);
// Verify it
println!("Signature: 0x{}", hex::encode(personal_signature));
// Signature: 0x52f210dadad13c4c8d0656e7380300a367a056631cf26950baa7de4f580187795c76b5fc94de5bd0b8af4d5df432687d900402cba86a12570af56be35ba8d56401
}
/// `web3.eth.personal.sign()`
fn personal_sign(payload: &String, secret_key: &SecretKey) -> Vec<u8> {
// Wrap personal.sign() required signature struct
let personal_message = format!("\x19Ethereum Signed Message:\n{}{}", payload.len(), payload);
// Keccak256 it into a digest.
let mut hasher = Keccak256::default();
hasher.update(personal_message);
let digest: [u8; 32] = hasher.finalize().into();
// Sign the digest.
let (r_and_s, v) = libsecp256k1::sign(&Message::parse(&digest), secret_key);
// Rebuild the sig into a [u8; 65]
let mut signature: Vec<u8> = vec![];
signature.extend_from_slice(&r_and_s.r.b32()); // r (32 bytes)
signature.extend_from_slice(&r_and_s.s.b32()); // s (32 bytes)
signature.push(v.serialize()); // v (1 byte)
if signature.len() != 65 {
panic!("Signature length is not 65 bytes");
}
signature
}
package main
import (
"crypto/ecdsa"
"fmt"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
)
const (
SECRET_KEY = "9deba3488458c0314e5fef8920d3b117dd76415569cf270db8fd864896c02732"
SIGN_PAYLOAD = "Test123123!"
)
func main() {
sk, err := crypto.HexToECDSA(SECRET_KEY)
if err != nil {
panic(err)
}
sign, err := signPersonal([]byte(SIGN_PAYLOAD), sk)
if err != nil {
panic(err)
}
fmt.Printf("Signature: %s\n", hexutil.Encode(sign))
// Signature: 0x52f210dadad13c4c8d0656e7380300a367a056631cf26950baa7de4f580187795c76b5fc94de5bd0b8af4d5df432687d900402cba86a12570af56be35ba8d56401
}
// signPersonal signs a payload using given secret key.
func signPersonal(payload []byte, sk *ecdsa.PrivateKey) (signature []byte, err error) {
digest := signPersonalDigest(payload)
signature, err = crypto.Sign(digest, sk)
if err != nil {
return nil, err
}
return signature, nil
}
// signPersonalDigest hashes the given payload with eth.personal.sign struct.
func signPersonalDigest(data []byte) []byte {
messsage := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data)
return crypto.Keccak256([]byte(messsage))
}
{
"dependencies": {
"ethereumjs-util": "^7.1.4"
}
}
import { ecsign, toRpcSig, keccakFromString } from 'ethereumjs-util'
async function personalSign(message: Buffer, privateKey: Buffer): Promise<Buffer> {
const messageHash = keccakFromString(`\x19Ethereum Signed Message:\n${message.length}${message}`, 256)
const signature = ecsign(messageHash, privateKey)
return Buffer.from(toRpcSig(signature.v, signature.r, signature.s).slice(2), 'hex')
}
async function main() {
const message = Buffer.from('Test123123!', 'utf8');
const secretKey = Buffer.from('9deba3488458c0314e5fef8920d3b117dd76415569cf270db8fd864896c02732', 'hex');
const signature = await personalSign(message, secretKey);
console.log(`Signature: 0x${signature.toString('hex')}`);
// Signature: 0x52f210dadad13c4c8d0656e7380300a367a056631cf26950baa7de4f580187795c76b5fc94de5bd0b8af4d5df432687d900402cba86a12570af56be35ba8d5641c
console.log(`Signature(base64): ${signature.toString('base64')}`);
// Signature(base64): UvIQ2trRPEyNBlbnOAMAo2egVmMc8mlQuqfeT1gBh3lcdrX8lN5b0LivTV30Mmh9kAQCy6hqElcK9WvjW6jVZBw=
}
main();
I want to contribute to ProofService, which platforms do you accept?
Well, basiclly every platform
can be accepted by us if it meets the following requirement:
Identity
(on targetplatform
) has the ability to post content specified by user.This content can be read publicly, at anytime, by anyone (human or program), after it is posted.
There is a mechanism to "encode" and "extract" signature into / from this content.
Depends on the shape this content represents, the mechanism to "extract" sig may vary. For example, assuming a
platform
which only supports posting user-generated pictures, you need to find a way to "encode" the signature into the user-generated pic, and make sure other people / program can "extract" from this pic.The most common case is text-based content, which is the easist to process. But if there is a limitation on posting text content on target platform, you should be prepared.