Reputation: 21
I need to sign a hash using my eth account, the private key to which is in AWS KMS.
Initially I just needed to sign some data with an eth private key. I implemented it this way and it worked fine:
from eth_account import Account
from hexbytes import HexBytes
KEY = 'ETH PRIVATE KEY'
DATA = 'SOME DATA'
account = Account.from_key(KEY)
data = HexBytes(DATA)
sig = account.signHash(data).signature.hex()
print(sig)
But now the situation is more complicated. Now I need to use AWS KMS. I created an asymmetric key there, specified "key usage" as "sign and verify" and selected "key spec" as "ECC_SECG_P256K1". I was able to get a new eth address from that key:
import boto3
import ethereum_kms_signer as signer
import hashlib
from eth_keys import keys
KEY_ID = 'AWS KEY ID'
DATA = 'SOME DATA'
kms_client = boto3.client('kms',
# secrets
)
address = signer.get_eth_address(KEY_ID, kms_client)
print(address)
Now I need to do the same operation as I did before, but without access to the private key. I tried several different variants. This is the last one I tried:
# imports, kms_client setup, get address etc.
hash = hashlib.sha256(DATA.encode('utf-8')).digest()
signature = kms_client.sign(KeyId=KEY_ID, Message=hash, SigningAlgorithm='ECDSA_SHA_256', MessageType='DIGEST')
r, s, v = signer.kms.get_sig_r_s_v(hash, signature['Signature'], address)
sig = keys.Signature(vrs=(v, r, s)).to_hex()
print(sig)
But no, this option doesn't work. I'm not quite sure why, but every call to kms_client.sign I get different values in signature['Signature']. And as a result I get different sigs. I send these signatures to the server and the server says it was signed by some unknown account and it's different every time.
Can someone point me in the right direction? Thank you in advance!
Upvotes: 1
Views: 531
Reputation: 21
I studied the source code of the "eth_account" module and simply rewrote the signHash function (from the first piece of code) to work with aws kms. I took some of the code from here: https://github.com/ethereum/eth-account/blob/master/eth_account/account.py
The final solution looks like this:
from hexbytes import HexBytes
import boto3
import ethereum_kms_signer as signer
from cytoolz import pipe
from eth_utils import to_bytes
KEY_ID = 'AWS KEY ID'
DATA = 'SOME 32 BIT DATA'
V_OFFSET = 27
kms_client = boto3.client('kms',
# region_name, api_version, aws_secret_access_key and aws_access_key_id
)
def to_eth_v(v_raw):
return v_raw + V_OFFSET
def _pad_to_eth_word(bytes_val):
return bytes_val.rjust(32, b"\0")
def to_bytes32(val):
return pipe(
val,
to_bytes,
_pad_to_eth_word,
)
hash = HexBytes(DATA)
signature = kms_client.sign(KeyId=KEY_ID, Message=hash, SigningAlgorithm='ECDSA_SHA_256', MessageType='DIGEST')
address = signer.get_eth_address(KEY_ID, kms_client)
r, s, v_raw = signer.kms.get_sig_r_s_v(hash, signature['Signature'], address)
v = to_eth_v(v_raw)
eth_signature_bytes = to_bytes32(r) + to_bytes32(s) + to_bytes(v)
sig = HexBytes(eth_signature_bytes).hex()
print(sig)
Upvotes: 1