Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions sdk/js/src/__tests__/solana.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,18 @@ describe('solana support', () => {

consoleSpy.mockRestore()
})

it('should hash the full key material for GetKeyResponse (regression: previously fell through unhashed)', async () => {
const client = new DstackClient()
const result = await client.getKey('/', 'test')

const legacyKeypair = toKeypair(result)
const secureKeypair = toKeypairSecure(result)

// toKeypairSecure must apply SHA256 to the full key material, as its
// docstring promises. If it silently falls through to the raw key (the
// bug this test guards against), both public keys are identical.
expect(secureKeypair.publicKey.toBase58()).not.toBe(legacyKeypair.publicKey.toBase58())
})
})
})
13 changes: 13 additions & 0 deletions sdk/js/src/__tests__/viem.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,5 +90,18 @@ describe('viem support', () => {

consoleSpy.mockRestore()
})

it('should hash the full key material for GetKeyResponse (regression: previously fell through unhashed)', async () => {
const client = new DstackClient()
const result = await client.getKey('/', 'test')

const legacyAccount = toViemAccount(result)
const secureAccount = toViemAccountSecure(result)

// toViemAccountSecure must apply SHA256 to the full key material, as its
// docstring promises. If it silently falls through to the raw key (the
// bug this test guards against), both addresses are identical.
expect(secureAccount.address).not.toBe(legacyAccount.address)
})
})
})
2 changes: 1 addition & 1 deletion sdk/js/src/solana.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@ export function toKeypairSecure(keyResponse: GetTlsKeyResponse | GetKeyResponse)
const buf = sha256(keyResponse.asUint8Array())
return Keypair.fromSeed(buf)
}
return Keypair.fromSeed(keyResponse.key)
return Keypair.fromSeed(sha256(keyResponse.key))
}
2 changes: 1 addition & 1 deletion sdk/js/src/viem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@ export function toViemAccountSecure(keyResponse: GetKeyResponse | GetTlsKeyRespo
const hex = bytesToHex(sha256(keyResponse.asUint8Array()))
return privateKeyToAccount(`0x${hex}`)
}
const hex = Array.from(keyResponse.key).map(b => b.toString(16).padStart(2, '0')).join('')
const hex = bytesToHex(sha256(keyResponse.key))
return privateKeyToAccount(`0x${hex}`)
}
3 changes: 2 additions & 1 deletion sdk/python/src/dstack_sdk/ethereum.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,5 @@ def to_account_secure(
"to_account_secure: missing SHA256 support, please upgrade your system"
) from e
else: # GetKeyResponse
return Account.from_key(get_key_response.decode_key()) # type: ignore[no-any-return]
hashed_key = hashlib.sha256(get_key_response.decode_key()).digest()
return Account.from_key(hashed_key) # type: ignore[no-any-return]
3 changes: 2 additions & 1 deletion sdk/python/src/dstack_sdk/solana.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,5 @@ def to_keypair_secure(get_key_response: GetKeyResponse | GetTlsKeyResponse) -> K
"to_keypair_secure: missing SHA256 support, please upgrade your system"
) from e
else: # GetKeyResponse
return Keypair.from_seed(get_key_response.decode_key())
hashed_key = hashlib.sha256(get_key_response.decode_key()).digest()
return Keypair.from_seed(hashed_key)
14 changes: 14 additions & 0 deletions sdk/python/tests/test_ethereum.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,17 @@ def test_to_account_secure_with_tls_key():
assert len(w) == 1
assert issubclass(w[0].category, DeprecationWarning)
assert "Please don't use getTlsKey method" in str(w[0].message)


def test_to_account_secure_hashes_full_key_material_for_get_key_response():
"""Regression: the GetKeyResponse branch previously fell through unhashed,
silently contradicting the "SHA256 of full key material" docstring."""
mock_result = GetKeyResponse(
key="1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
signature_chain=["sig1", "sig2"],
)

legacy_account = to_account(mock_result)
secure_account = to_account_secure(mock_result)

assert secure_account.address != legacy_account.address
14 changes: 14 additions & 0 deletions sdk/python/tests/test_solana.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,17 @@ def test_to_keypair_secure_with_tls_key():
assert len(w) == 1
assert issubclass(w[0].category, DeprecationWarning)
assert "Please don't use getTlsKey method" in str(w[0].message)


def test_to_keypair_secure_hashes_full_key_material_for_get_key_response():
"""Regression: the GetKeyResponse branch previously fell through unhashed,
silently contradicting the "SHA256 of full key material" docstring."""
mock_result = GetKeyResponse(
key="1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
signature_chain=["sig1", "sig2"],
)

legacy_keypair = to_keypair(mock_result)
secure_keypair = to_keypair_secure(mock_result)

assert bytes(secure_keypair.pubkey()) != bytes(legacy_keypair.pubkey())