Implementing Digital Signatures

While digital signatures are generally considered highly secure and require zero knowledge of the original private key to verify ownership of the private key, here are a few hurdles you may encounter when implementing this authentication protocol.

  

Expected Values

  • Private keys should be 64 characters long as hex-encoded strings, not including any "0x" prefix. When signing with them, you may need to make sure the hex strings do not have a "0x" prefix, depending on your library.

  • Addresses should be 42 characters long as hex-encoded strings, including a "0x" prefix. When sending them, add the prefix if your library doesn't add it automatically.

  • Signatures should be 130 characters long as hex-encoded strings, not including any "0x" prefix. When sending them, you may need to ensure that they do not have a "0x" prefix.

  
# Use encoded_message in the request body and sig_hx
# in the appropriate signature header.
print(signature)
print(encoded_message)

#...

# You can also sign messages with the Sila-Python SDK.

from silasdk import EthWallet

EthWallet.signMessage("my_message", "private_key")
// Marshal the message to JSON; this function returns bytes.
    // (The bytes that get hashed should be guaranteed to be the
    // same as what is sent in the request.)
    // NOTE: if testing the example strings, you can just declare them as
    // strings and cast them to bytes, e.g. message := []byte("Sila")
    message, err := json.Marshal(&messageJSON)
    if err != nil {
        log.Fatal(err)
    }

    // Generate the message hash using the Keccak 256 algorithm.
    msgHash := crypto.Keccak256(message)

    // Create a signature using your private key and hashed message.
    sigBytes, err := crypto.Sign(msgHash, pk)
    if err != nil {
        log.Fatal(err)
    }

    // The signature just created is off by -27 from what the API
    // will expect. Correct that by converting the signature bytes 
    // to a big int and adding 27.
    var offset int64 = 27
    var bigSig = new(big.Int).SetBytes(sigBytes)
    sigBytes = bigSig.Add(bigSig, big.NewInt(offset)).Bytes()

    // The big library takes out any padding, but the resultant 
    // signature must be 130 characters (65 bytes) long. In some 
    // cases, you might find that sigBytes now has a length of 64 or
    // less, so you can fix that in this way (this prepends the hex 
    // value with "0" until the requisite length is reached).
    // Example: if two digits were required but the value was 1, you'd 
    // pass in 01.
    var sigBytesLength = 65 // length of a valid signature byte array
    var arr = make([]byte, sigBytesLength)
    copy(arr[(sigBytesLength-len(sigBytes)):], sigBytes)

    // Encode the bytes to a hex string.
    sig := hex.EncodeToString(arr)

    // The raw message should then be sent in an HTTP request body, and
    // the signature should be sent in a header.
    log.Println("Message:", string(message))
    log.Println("Signature:", sig, "Signature length:", len(sig))
  

Known Pitfalls

  • You'll (most likely) want to use a cryptographic library in whatever language(s) you use. Each library is different and may have its own quirks.

  • Make sure you have marshalled your request body to JSON (bytes or string) before you create the signature. Marshalling may or may not be a symmetric operation; that is, the keys in the resulting JSON string may be ordered differently when the marshalling operation is conducted a second time. Problems may arise if you have a struct, hashmap, or JSONObject that you marshal when signing, then marshal again when sending the request. Any difference in the marshalled JSON request body will completely change its signature and result in failed validation.

  • Some signing libraries may create a signature with a certain offset from what the Sila endpoint expects. For instance, signatures generated with a particular Go library are consistently off by 27. You will want to check your algorithm against some of our examples to make sure you don't have an offset issue.

  • If you do have an offset issue, you will need to convert the hex string to a big integer, add the offset to it, and convert it back to a hex-encoded string.

  • If you do have an offset issue and correct it, you may find that your new hex-encoded signature string may sometimes be less than 130 characters long. You can precede the string with 0s until it reaches the expected length to solve this issue.