How to verify SECP256K1 signed message in smart contract? - nearprotocol

I have signed the message, using ed25519
const msg = Buffer.from("hi");
const signerKeyPair = await keyStore.getKey(config.networkId, signerAccount);
const { signature } = signerKeyPair.sign(msg);
How to verify the signature in the smart contract if I have a signature and signer PublicKey?
I've found that near-core uses a near-crypto crate to do it, but I'm not sure that I can use it for the smart contracts.
an example using near-core
use near_crypto::{PublicKey, Signature, KeyType};
...
pub fn verify(&self, data: &[u8], public_key: String) -> bool {
let public_key = PublicKey::from_str(&public_key).unwrap();
let signature = Signature::empty(KeyType::SECP256K1);
signature.verify(data, &public_key)
}
...

Here is a solution
using ed25519-dalek = "1.0.1" crate
pub fn gimme_my_present(&mut self, signature: Vec<u8>) -> Promise {
let signature = ed25519_dalek::Signature::try_from(signature.as_ref())
.expect("Signature should be a valid array of 64 bytes [13, 254, 123, ...]");
let account_id = near_sdk::env::signer_account_id();
// Someone will give you the corresponding private key...
let public_key = ed25519_dalek::PublicKey::from_bytes(
&bs58::decode(
"H5ANpdUoXVwhYBgAgEi1ieMQZKJbwxjPJtHX4vkVcSnF",
)
.into_vec()
.unwrap(),
)
.unwrap();
near_sdk::env::log(
format!(
"Verifiying validity of signature ('{:?}') for string '{}'...",
signature, account_id
)
.as_bytes(),
);
if let Ok(_) = public_key.verify(account_id.as_bytes(), &signature) {
return Promise::new(account_id).transfer(16 * 16 * ONE_NEAR);
}
panic!("Ima no gonna give-ya the present without a signature! :-P");
}
source

Related

Cannot set name of NFT in Elrond Rust testing framework

I am writing tests for my smart contract using elrond-wasm 0.32.0.
My issue is that my smart contract returns the identifier of the ESDT token when I am getting the name.
Detailed steps
I am setting my ESDT token's name like so:
blockchain_wrapper.set_nft_balance_all_properties::<Empty>(
&cf_wrapper.address_ref(),
&ITEM_TO_EQUIP_ID,
ITEM_TO_EQUIP_NONCE,
&rust_biguint!(2u64),
&Empty,
0,
Option::Some(&owner_address),
Option::Some(ITEM_TO_EQUIP_NAME),
Option::Some(b""),
&[],
);
Then, I get my ESDT token's name :
blockchain_wrapper
.execute_query(&cf_wrapper, |sc| {
let data = sc.blockchain().get_esdt_token_data(
&ManagedAddress::from_address(&cf_wrapper.address_ref()),
&TokenIdentifier::from_esdt_bytes(ITEM_TO_EQUIP_ID),
ITEM_TO_EQUIP_NONCE,
);
// data.name equals ITEM_TO_EQUIP_ID, so it fail
assert_eq!(data.name, ManagedBuffer::new_from_bytes(ITEM_TO_EQUIP_NAME));
})
.assert_ok();
Did I forget something, or is it a bug?
Complete snippet
#[test]
fn set_name_test() {
const WASM_PATH: &'static str = "sc-customize-nft/output/customize_nft.wasm";
const ITEM_TO_EQUIP_ID: &[u8] = b"ITEM-a1a1a1";
const ITEM_TO_EQUIP_NAME: &[u8] = b"item name";
const ITEM_TO_EQUIP_NONCE: u64 = 1;
let rust_zero = rust_biguint!(0u64);
let mut blockchain_wrapper = BlockchainStateWrapper::new();
let owner_address = blockchain_wrapper.create_user_account(&rust_zero);
let cf_wrapper = blockchain_wrapper.create_sc_account(
&rust_zero,
Some(&owner_address),
customize_nft::contract_obj,
WASM_PATH,
);
// deploy contract
blockchain_wrapper
.execute_tx(&owner_address, &cf_wrapper, &rust_zero, |sc| {
let result = sc.init(managed_token_id!(PENGUIN_TOKEN_ID));
assert_eq!(result, SCResult::Ok(()));
})
.assert_ok();
blockchain_wrapper.add_mandos_set_account(cf_wrapper.address_ref());
blockchain_wrapper.set_nft_balance_all_properties::<Empty>(
&cf_wrapper.address_ref(),
&ITEM_TO_EQUIP_ID,
ITEM_TO_EQUIP_NONCE,
&rust_biguint!(2u64),
&Empty,
0,
Option::Some(&owner_address),
Option::Some(ITEM_TO_EQUIP_NAME),
Option::Some(b""),
&[],
);
blockchain_wrapper
.execute_query(&cf_wrapper, |sc| {
let data = sc.blockchain().get_esdt_token_data(
&ManagedAddress::from_address(&cf_wrapper.address_ref()),
&TokenIdentifier::from_esdt_bytes(ITEM_TO_EQUIP_ID),
ITEM_TO_EQUIP_NONCE,
);
println!("Name is {:?}", data.name);
assert_eq!(data.name, ManagedBuffer::new_from_bytes(ITEM_TO_EQUIP_NAME));
})
.assert_ok();
}
I got an answer from an Elrond Developer:
It's a bug in the mock. It's being set properly, but the get_esdt_token_data mock just returns the token ID instead of the name from metadata. Thanks for reporting!

Access and iterate over account metadata within a Solana(Anchor) Program?

To access and iterate over Solana account metadata on the client one can simply use solana/web3.js. But how can I access the same data within the solana(Anchor) program itself. How can I get the number of tweet accounts created and access the pubKeys of the account authors. I want this to be done on-chain to prevent any users from manipulating the front-end code for their own benefit, such as minting/transferring utility tokens to themselves rather than other users of the dApp. Basically how can the web3 code below be implemented on-chain?
Below is the web3.js version of what I want to do within the program.
const tweetAccounts = await program.account.tweet.all();
let totalAccounts = tweetAccounts.length;
for (let tweetAccount of tweetAccounts) {
let allKeys = [];
let theKey = tweetAccount.author.publicKey;
allKeys.push(theKey);
}
Solana Program Code I have to create tweet account.
pub fn send_tweet(ctx: Context<SendTweet>, topic: String, content: String, review: i32) -> ProgramResult {
let tweet: &mut Account<Tweet> = &mut ctx.accounts.tweet;
let author: &Signer = &ctx.accounts.author;
let clock: Clock = Clock::get().unwrap();
if topic.chars().count() > 50 {
return Err(ErrorCode::TopicTooLong.into())
}
if content.chars().count() > 280 {
return Err(ErrorCode::ContentTooLong.into())
}
tweet.author = *author.key;
tweet.timestamp = clock.unix_timestamp;
tweet.topic = topic;
tweet.content = content;
tweet.review = review;
Ok(())
}
#[derive(Accounts)]
pub struct SendTweet<'info> {
#[account(init, payer = author, space = Tweet::LEN)]
pub tweet: Account<'info, Tweet>,
#[account(mut)]
pub author: Signer<'info>,
pub system_program: Program<'info, System>,
}
It's impossible to dynamically load new accounts on-chain, meaning every account that you want to use must all be specified up front.
For your situation, this means that there is no immediate solution! The best way to get around this is to create a global account with all of the required counters, and update it as needed on every instruction.
I wish there were a more satisfying response, but this is the best I can come up with.

How to upgrade from using deprecated SecDigestGetData method?

At some point, it seems that Apple has removed the SecDigestGetData method from the Security framework. I'm trying to upgrade some old code that compares a certificate digest to see if we've found the right one. The original code looks like this:
uint8 candidate_sha1_hash[20];
CSSM_DATA digest;
digest.Length = sizeof(candidate_sha1_hash);
digest.Data = candidate_sha1_hash;
if ((SecDigestGetData(CSSM_ALGID_SHA1, &digest, &certData) == CSSM_OK) &&
(hashData.Length == digest.Length) &&
(!memcmp(hashData.Data, digest.Data, digest.Length))) {
found = TRUE;
break;
}
I get this error when it is compiled:
export_private_key.c:103:10: error: implicit declaration of function 'SecDigestGetData' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
if ((SecDigestGetData(CSSM_ALGID_SHA1, &digest, &certData) == CSSM_OK) &&
^
How can I upgrade this code?
You can calculate the fingerprint yourself by extracting the DER data and hashing it. In C it would look like this.
#include <Security/SecCertificate.h>
#include <CommonCrypto/CommonDigest.h>
CFDataRef _Nonnull digest_from_certificate(SecCertificateRef _Nonnull certificate) {
const CFDataRef certificateData = SecCertificateCopyData(certificate);
uint8 digest [CC_SHA1_DIGEST_LENGTH];
CC_SHA1(CFDataGetBytePtr(certificateData), (CC_LONG) CFDataGetLength(certificateData), digest);
return CFDataCreate(NULL, digest, CC_SHA1_DIGEST_LENGTH);
}
In Swift the implementation would look like this.
// Extracting the DER data.
let certificate: SecCertificate = ...
let certificateData = SecCertificateCopyData(certificate) as Data
Hashing can be done with CommonCrypto or, since iOS 13, with CryptoKit.
import CommonCrypto
let certificateBytes = Array(certificateData)
var digest = [UInt8](repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH))
_ = CC_SHA1(certificateBytes, CC_LONG(certificateBytes.count), &digest)
print(Data(digest) as NSData)
import CryptoKit
Insecure.SHA1.hash(data: SecCertificateCopyData(certificate) as Data)
An alternative approach would be to use SecCertificateCopyValues.
if let values = SecCertificateCopyValues(certificate, ["Fingerprints"] as CFArray, &error) as? [String: Any] {
if let fingerprints = (values["Fingerprints"] as? [CFString: Any])?[kSecPropertyKeyValue] as? [[CFString: Any]] {
if let sha1 = fingerprints.first(where: { ($0[kSecPropertyKeyLabel] as? String) == "SHA-1" }).map({ $0[kSecPropertyKeyValue] as? Data }) {
print(sha1 as NSData)
}
}
}
SecCertificateCopyValues returns a dictionary described by certificate property keys. The value of such dictionary can be another dictionary or an array of dictionaries. Now, as far as I know, certificates should have SHA-256 as a minimum (SHA-1 is merely used for backwards compatibility), meaning you'll likely get an array of fingerprints, hence my example.

Get a macOS Keychain certificate's SHA1 hash in Swift

I've retrieved the set of certificates in my keychain using this code:
let query: [String: Any] = [
kSecClass as String: kSecClassCertificate,
kSecMatchLimit as String: kSecMatchLimitAll,
kSecReturnAttributes as String: false,
kSecReturnData as String: true
]
var result: CFTypeRef?
var results : Set<CertsResult> = []
let status = SecItemCopyMatching(query as CFDictionary, &result)
//[Check status]
guard let certificateData = result as? [CFData] else {
//[Handle]
}
From here, I loop through certificateData and gather information about the certificates, but I need to get the SHA1 hash of the certificates as well. I've gathered from researching that I need to use import CommonCrypto and CC_SHA1, but what I've read doesn't use a CFData.
Is there a good way to get from this point to its SHA1?
You can achieve it by performing the hash yourself. The fingerprints are not part of the certificate itself. More info about that over here.
import CryptoKit
let certificate = ...
let der = SecCertificateCopyData(certificate) as Data
let sha1 = Insecure.SHA1.hash(data: der)
let sha256 = SHA256.hash(data: der)
This can be created in an extension too. I've used CommonCrypto in the extension.
import CommonCrypto
extension SecCertificate {
var sha1: Data {
var digest = [UInt8](repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH))
let der = SecCertificateCopyData(self) as Data
_ = CC_SHA1(Array(der), CC_LONG(der.count), &digest)
return Data(digest)
}
var sha256: Data {
var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
let der = SecCertificateCopyData(self) as Data
_ = CC_SHA256(Array(der), CC_LONG(der.count), &digest)
return Data(digest)
}
}
I'd like to mention that SHA-1 hashes of certificates are deprecated since like 2017 and websites and tech giants are starting to drop support for them.
Playground example
import CryptoKit
import Foundation
class CertificateStuff: NSObject, URLSessionDelegate {
func urlSession(
_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
) {
guard let serverTrust = challenge.protectionSpace.serverTrust else {
completionHandler(.rejectProtectionSpace, nil)
return
}
for index in 0 ..< SecTrustGetCertificateCount(serverTrust) {
let certificate = SecTrustGetCertificateAtIndex(serverTrust, index)!
let der = SecCertificateCopyData(certificate)
let sha1 = Insecure.SHA1.hash(data: der as Data)
let sha256 = SHA256.hash(data: der as Data)
print(certificate)
print(sha1)
print(sha256)
print()
}
completionHandler(.performDefaultHandling, nil)
}
func request(_ done: #escaping (Result<Data, Error>) -> Void) {
let url = URL(string: "https://security.stackexchange.com/questions/14330/what-is-the-actual-value-of-a-certificate-fingerprint")!
let request = URLRequest(url: url)
URLSession(configuration: .default, delegate: self, delegateQueue: nil).dataTask(with: request) { (d, r, e) in
if let e = e {
print(e)
return
}
print(d!)
}.resume()
}
}
CertificateStuff().request { result in print(result) }

How to use common crypto and/or calculate sha256 in swift 2 & 3

I am trying to make hash a password value according to sha256. I already search this but there is no info about swift 2. This solution did not worked for me
func sha256(data:String) -> String {
let data = self.dataUsingEncoding(NSUTF8StringEncoding)!
var digest = [UInt8](count:Int(CC_SHA256_DIGEST_LENGTH), repeatedValue: 0)
CC_SHA256(data.bytes, CC_LONG(data.length), &digest)
let hexBytes = digest.map { String(format: "%02hhx", $0) }
return hexBytes.joinWithSeparator("")
}
It gives error: Use of unresolved identifier CC_SHA256_DIGEST_LENGTH
Add a bridging header and add the import to it:
#import <CommonCrypto/CommonDigest.h>
Swift 3:
func sha256(string: String) -> Data? {
guard let messageData = string.data(using:String.Encoding.utf8) else { return nil }
var digestData = Data(count: Int(CC_SHA256_DIGEST_LENGTH))
_ = digestData.withUnsafeMutableBytes {digestBytes in
messageData.withUnsafeBytes {messageBytes in
CC_SHA256(messageBytes, CC_LONG(messageData.count), digestBytes)
}
}
return digestData
}
// Test
let shaData = sha256(string:"Here is the test string")
let shaHex = shaData!.map { String(format: "%02hhx", $0) }.joined()
print("shaHex: \(shaHex)")
shaHex: 6f5c446883a3049caf8368b4bad2d2ff045a39d467ee20a8d34d5698e649fe21
Swift 2:
func sha256(string string: String) -> NSData {
let digest = NSMutableData(length: Int(CC_SHA256_DIGEST_LENGTH))!
if let data :NSData = string.dataUsingEncoding(NSUTF8StringEncoding) {
CC_SHA256(data.bytes, CC_LONG(data.length),
UnsafeMutablePointer<UInt8>(digest.mutableBytes))
}
return digest
}
//Test:
let digest = sha256(string:"Here is the test string")
print("digest: \(digest)")
Output:
digest: 6f5c4468 83a3049c af8368b4 bad2d2ff 045a39d4 67ee20a8 d34d5698 e649fe21
Example from sunsetted documentation section:
HMAC with MD5, SHA1, SHA224, SHA256, SHA384, SHA512 (Swift 3+)
These functions will hash either String or Data input with one of eight cryptographic hash algorithms.
The name parameter specifies the hash function name as a String
Supported functions are MD5, SHA1, SHA224, SHA256, SHA384 and SHA512
This example requires Common Crypto
It is necessary to have a bridging header to the project:
#import <CommonCrypto/CommonCrypto.h>
Add the Security.framework to the project.
These functions takes a hash name, message to be hashed, a key and return a digest:
hashName: name of a hash function as String
message: message as Data
key: key as Data
returns: digest as Data
func hmac(hashName:String, message:Data, key:Data) -> Data? {
let algos = ["SHA1": (kCCHmacAlgSHA1, CC_SHA1_DIGEST_LENGTH),
"MD5": (kCCHmacAlgMD5, CC_MD5_DIGEST_LENGTH),
"SHA224": (kCCHmacAlgSHA224, CC_SHA224_DIGEST_LENGTH),
"SHA256": (kCCHmacAlgSHA256, CC_SHA256_DIGEST_LENGTH),
"SHA384": (kCCHmacAlgSHA384, CC_SHA384_DIGEST_LENGTH),
"SHA512": (kCCHmacAlgSHA512, CC_SHA512_DIGEST_LENGTH)]
guard let (hashAlgorithm, length) = algos[hashName] else { return nil }
var macData = Data(count: Int(length))
macData.withUnsafeMutableBytes {macBytes in
message.withUnsafeBytes {messageBytes in
key.withUnsafeBytes {keyBytes in
CCHmac(CCHmacAlgorithm(hashAlgorithm),
keyBytes, key.count,
messageBytes, message.count,
macBytes)
}
}
}
return macData
}
hashName: name of a hash function as String
message: message as String
key: key as String
returns: digest as Data
func hmac(hashName:String, message:String, key:String) -> Data? {
let messageData = message.data(using:.utf8)!
let keyData = key.data(using:.utf8)!
return hmac(hashName:hashName, message:messageData, key:keyData)
}
hashName: name of a hash function as String
message: message as String
key: key as Data
returns: digest as Data
func hmac(hashName:String, message:String, key:Data) -> Data? {
let messageData = message.data(using:.utf8)!
return hmac(hashName:hashName, message:messageData, key:key)
}
// Examples
let clearString = "clearData0123456"
let keyString = "keyData8901234562"
let clearData = clearString.data(using:.utf8)!
let keyData = keyString.data(using:.utf8)!
print("clearString: \(clearString)")
print("keyString: \(keyString)")
print("clearData: \(clearData as NSData)")
print("keyData: \(keyData as NSData)")
let hmacData1 = hmac(hashName:"SHA1", message:clearData, key:keyData)
print("hmacData1: \(hmacData1! as NSData)")
let hmacData2 = hmac(hashName:"SHA1", message:clearString, key:keyString)
print("hmacData2: \(hmacData2! as NSData)")
let hmacData3 = hmac(hashName:"SHA1", message:clearString, key:keyData)
print("hmacData3: \(hmacData3! as NSData)")
Output:
clearString: clearData0123456
keyString: keyData8901234562
clearData: <636c6561 72446174 61303132 33343536>
keyData: <6b657944 61746138 39303132 33343536 32>
hmacData1: <bb358f41 79b68c08 8e93191a da7dabbc 138f2ae6>
hmacData2: <bb358f41 79b68c08 8e93191a da7dabbc 138f2ae6>
hmacData3: <bb358f41 79b68c08 8e93191a da7dabbc 138f2ae6>
MD2, MD4, MD5, SHA1, SHA224, SHA256, SHA384, SHA512 (Swift 3+)
These functions will hash either String or Data input with one of eight cryptographic hash algorithms.
The name parameter specifies the hash function name as a String
Supported functions are MD2, MD4, MD5, SHA1, SHA224, SHA256, SHA384 and SHA512
a
This example requires Common Crypto
It is necessary to have a bridging header to the project:
#import <CommonCrypto/CommonCrypto.h>
Add the Security.framework to the project.
This function takes a hash name and String to be hashed and returns a Data:
name: A name of a hash function as a String
string: The String to be hashed
returns: the hashed result as Data
func hash(name:String, string:String) -> Data? {
let data = string.data(using:.utf8)!
return hash(name:name, data:data)
}
Examples:
let clearString = "clearData0123456"
let clearData = clearString.data(using:.utf8)!
print("clearString: \(clearString)")
print("clearData: \(clearData as NSData)")
let hashSHA256 = hash(name:"SHA256", string:clearString)
print("hashSHA256: \(hashSHA256! as NSData)")
let hashMD5 = hash(name:"MD5", data:clearData)
print("hashMD5: \(hashMD5! as NSData)")
Output:
clearString: clearData0123456
clearData: <636c6561 72446174 61303132 33343536>
hashSHA256: <aabc766b 6b357564 e41f4f91 2d494bcc bfa16924 b574abbd ba9e3e9d a0c8920a>
hashMD5: <4df665f7 b94aea69 695b0e7b baf9e9d6>

Resources