use std::convert::TryInto;
use std::ops::Add;
use crate::error::ContractError;
use crate::execute::CYBERSPACE_ID_MSG;
use bech32::{ToBase32, Variant};
use cosmwasm_std::{
from_json, to_json_binary, Addr, Binary, Deps, ReplyOn, StdError, StdResult, SubMsg, WasmMsg,
};
use cw_cyber_subgraph::msg::ExecuteMsg as SubgraphExecuteMsg;
use cyber_std::types::Link;
use cyber_std::CyberMsg;
use primitive_types::H256;
use ripemd160::Digest as Ripemd160Digest;
use ripemd160::Ripemd160;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use sha2::Sha256;
use sha3::Keccak256;
pub fn proof_address_ethereum(
deps: Deps,
address: String,
passport_owner: String,
message: String,
signature: Binary,
) -> Result<bool, ContractError> {
let mut hasher = Keccak256::new();
let msg = passport_owner.add(":").add(&message);
hasher.update(format!("\x19Ethereum Signed Message:\n{}", msg.len()));
hasher.update(msg);
let hash = hasher.finalize();
let sig = decode_signature(&signature.clone().to_string())?;
// Decompose signature
let (v, rs) = match sig.split_last() {
Some(pair) => pair,
None => {
return Err(ContractError::VerificationFailed {
msg: "Signature must not be empty".to_string(),
})
}
};
let recovery = get_recovery_param(*v)?;
// Verification
let calculated_pubkey = deps
.api
.secp256k1_recover_pubkey(&hash, rs, recovery)
.map_err(|_| ContractError::ErrorKeyRecovery {})?;
let calculated_address = ethereum_address_raw(&calculated_pubkey)?;
let signer_address = decode_address(address.clone().as_str())?;
if signer_address != calculated_address {
return Err(ContractError::VerificationFailed {
msg: "Signer address is not calculated address".to_string(),
});
}
deps.api
.secp256k1_verify(&hash, rs, &calculated_pubkey)
.map_err(|err| ContractError::VerificationFailed {
msg: err.to_string(),
})
}
fn get_recovery_param(v: u8) -> StdResult<u8> {
match v {
// 0 and 1 added to support ledger
0 => Ok(0),
1 => Ok(1),
27 => Ok(0),
28 => Ok(1),
_ => Err(StdError::generic_err(
"Values of v other than 0, 1, 27 and 28 not supported",
)),
}
}
/// Returns a raw 20 byte Ethereum address
fn ethereum_address_raw(pubkey: &[u8]) -> StdResult<[u8; 20]> {
let (tag, data) = match pubkey.split_first() {
Some(pair) => pair,
None => return Err(StdError::generic_err("Public key must not be empty")),
};
if *tag != 0x04 {
return Err(StdError::generic_err("Public key must start with 0x04"));
}
if data.len() != 64 {
return Err(StdError::generic_err("Public key must be 65 bytes long"));
}
let hash = Keccak256::digest(data);
hash[hash.len() - 20..]
.try_into()
.map_err(|_| StdError::generic_err("failed to convert hash to address"))
}
/// Returns a raw 20 byte Ethereum address from hex
pub fn decode_address(input: &str) -> StdResult<[u8; 20]> {
if input.len() != 42 {
return Err(StdError::generic_err(
"Ethereum address must be 42 characters long",
));
}
if !input.starts_with("0x") {
return Err(StdError::generic_err("Ethereum address must start with 0x"));
}
let data = hex::decode(&input[2..]).map_err(|_| StdError::generic_err("hex decoding error"))?;
data.try_into()
.map_err(|_| StdError::generic_err("failed to convert hex to address"))
}
/// Returns a raw 65 byte Ethereum signature from hex
pub fn decode_signature(input: &str) -> StdResult<[u8; 65]> {
if input.len() != 132 {
return Err(StdError::generic_err(
"Ethereum signature must be 132 characters long",
));
}
if !input.starts_with("0x") {
return Err(StdError::generic_err(
"Ethereum signature must start with 0x",
));
}
let data = hex::decode(&input[2..]).map_err(|_| StdError::generic_err("hex decoding error"))?;
data.try_into()
.map_err(|_| StdError::generic_err("failed to convert hex to signature"))
}
pub fn proof_address_cosmos(
deps: Deps,
address: String,
passport_owner: String,
message: String,
signature: Binary,
) -> Result<bool, ContractError> {
let (prefix, _, _) = bech32::decode(&address).map_err(|_| ContractError::ErrorDataParse {})?;
let sig: CosmosSignature = from_json(&signature)?;
let address_sig = pub_key_to_address(&deps, &sig.pub_key, &prefix)?;
// ADR-36 signed object, need to construct this object part by part and add encoded signed data with signer
// {
// "account_number":"0",
// "chain_id":"",
// "fee":{"amount":[],"gas":"0"},
// "memo":"",
// "msgs":[
// {
// "type":"sign/MsgSignData",
// "value":{
// "data": base64::encode(message),
// "signer": address
// }
// }],
// "sequence":"0"
// };
let msg = passport_owner.add(":").add(&message);
let mut msg_adr36: Vec<u8> = vec![
123, 34, 97, 99, 99, 111, 117, 110, 116, 95, 110, 117, 109, 98, 101, 114, 34, 58, 34, 48,
34, 44, 34, 99, 104, 97, 105, 110, 95, 105, 100, 34, 58, 34, 34, 44, 34, 102, 101, 101, 34,
58, 123, 34, 97, 109, 111, 117, 110, 116, 34, 58, 91, 93, 44, 34, 103, 97, 115, 34, 58, 34,
48, 34, 125, 44, 34, 109, 101, 109, 111, 34, 58, 34, 34, 44, 34, 109, 115, 103, 115, 34,
58, 91, 123, 34, 116, 121, 112, 101, 34, 58, 34, 115, 105, 103, 110, 47, 77, 115, 103, 83,
105, 103, 110, 68, 97, 116, 97, 34, 44, 34, 118, 97, 108, 117, 101, 34, 58, 123, 34, 100,
97, 116, 97, 34, 58, 34,
];
msg_adr36.append(&mut base64::encode(msg).as_bytes().to_vec());
msg_adr36.append(&mut vec![
34, 44, 34, 115, 105, 103, 110, 101, 114, 34, 58, 34,
]);
msg_adr36.append(&mut address.clone().as_bytes().to_vec());
msg_adr36.append(&mut vec![
34, 125, 125, 93, 44, 34, 115, 101, 113, 117, 101, 110, 99, 101, 34, 58, 34, 48, 34, 125,
]);
let hash = Sha256::digest(&msg_adr36);
if address != address_sig.to_string() {
return Err(ContractError::Unauthorized {});
}
deps.api
.secp256k1_verify(
hash.as_ref(),
sig.signature.as_slice(),
sig.pub_key.as_slice(),
)
.map_err(|err| ContractError::VerificationFailed {
msg: err.to_string(),
})
}
/// Converts user pubkey into Addr with given prefix
fn pub_key_to_address(_deps: &Deps, pub_key: &[u8], prefix: &str) -> StdResult<Addr> {
let compressed_pub_key = to_compressed_pub_key(pub_key)?;
let mut ripemd160_hasher = Ripemd160::new();
ripemd160_hasher.update(Sha256::digest(&compressed_pub_key));
let addr_bytes = ripemd160_hasher.finalize().to_vec();
let addr_str = bech32::encode(prefix, addr_bytes.to_base32(), Variant::Bech32)
.map_err(|e| StdError::generic_err(format!("bech32 encode failed: {}", e)))?;
Ok(Addr::unchecked(&addr_str))
}
/// Converts uncompressed pub key into compressed one
fn to_compressed_pub_key(pub_key: &[u8]) -> StdResult<Vec<u8>> {
match pub_key.len() {
// compressed
33 => Ok(pub_key.to_vec()),
// uncompressed
65 => {
let y = H256::from_slice(&pub_key[33..]);
let mut pub_key_compressed = pub_key[1..33].to_vec();
// Check whether even or odd
if y & H256::from_low_u64_be(1) == H256::zero() {
// 0x02
pub_key_compressed.insert(0, 2);
} else {
// 0x03
pub_key_compressed.insert(0, 3);
}
Ok(pub_key_compressed)
}
_ => Err(StdError::generic_err("PubKeyLengthIsNotValid")),
}
}
// {
// pub_key: "A+MXFp7YeLMvoVlAU66Uu0z3Wtc9Cuwq0eocUhtNOmnw",
// signature: "9O89CUdRRZj011BphnTs5JnYM9/0O0ch+XLG2DNiWqtYnA4xA5B0wmFQDOQogOxL5xKWILVMnv1IA/7s05QsIA=="
// };
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct CosmosSignature {
pub_key: Binary,
signature: Binary,
}
pub fn prepare_cyberlink_submsg(contract_addr: String, links: Vec<Link>) -> SubMsg<CyberMsg> {
SubMsg {
id: CYBERSPACE_ID_MSG,
msg: WasmMsg::Execute {
contract_addr,
msg: to_json_binary(&SubgraphExecuteMsg::Cyberlink { links })
.expect("failed to serialize cyberlink message"),
funds: vec![],
}
.into(),
gas_limit: None,
reply_on: ReplyOn::Error,
}
}
cw-cyber/contracts/cw-cyber-passport/src/helpers.rs
ฯ 0.0%
use TryInto;
use Add;
use crateContractError;
use crateCYBERSPACE_ID_MSG;
use ;
use ;
use ExecuteMsg as SubgraphExecuteMsg;
use Link;
use CyberMsg;
use H256;
use Digest as Ripemd160Digest;
use Ripemd160;
use JsonSchema;
use ;
use Sha256;
use Keccak256;
/// Returns a raw 20 byte Ethereum address
/// Returns a raw 20 byte Ethereum address from hex
/// Returns a raw 65 byte Ethereum signature from hex
/// Converts user pubkey into Addr with given prefix
/// Converts uncompressed pub key into compressed one
// {
// pub_key: "A+MXFp7YeLMvoVlAU66Uu0z3Wtc9Cuwq0eocUhtNOmnw",
// signature: "9O89CUdRRZj011BphnTs5JnYM9/0O0ch+XLG2DNiWqtYnA4xA5B0wmFQDOQogOxL5xKWILVMnv1IA/7s05QsIA=="
// };