
IOST--私钥、公钥(Onwerkey )生成
Hello , 我是09
此篇内容为我2018~2020年在【库神】工作时,对部分重要知识做的学习总结
随着比特币冲破10万美金,行业又再次兴起,希望相关从业者可以学习到你需要的内容
前言
IOST通过助记词推导出的IOST路径和其他的币种不同,它和新经币一样只保留增强的路径: m/44’/291’/0’。
IOST的私钥与公钥的可视化展示都是经过base58编码后得到的。
IOST的分层确定性推算方法和比特系列是不同的,它用的标准的ED25519的推算方法。
场景
Seed : super copper normal bronze almost habit razor bottom trip dutch unusual order
Slip44 : 291
Path : m/44'/291'/0'
算法 : ED25519
重点
标准ED25519分层确定性:
#pragma mark -- 这个是标准的ED25519分层确定性 --
void hdnode_private_ckd_ed25519(UInt256 k, UInt256 c, uint32_t i)
{
uint8_t buf[sizeof(BRECPoint) + sizeof(i)];
UInt512 I;
if (i & BIP32_HARD) {
buf[0] = 0;
(UInt256 )&buf[1] = *k;
}else {
NSLog(@"数据有误");
}
(uint32_t )&buf[sizeof(BRECPoint)] = CFSwapInt32HostToBig(i);
CCHmac( kCCHmacAlgSHA512, c, sizeof(*c), buf, sizeof(buf), &I );
k = (UInt256 *)&I;
c = (UInt256 *)&I.u8[sizeof(UInt256)];
memset(buf, 0, sizeof(buf));
memset(&I, 0, sizeof(I));
}
私钥生成
通过助记词进行经过PBKDF算法后推导出助记符代码,推导的相关代码请查看基础支持模块。
NSString *masterSeed = [KSHotWalletImplement_Mnemonic o_deriveKeyFromPhrase:seed withPassphrase:nil];
// 我们得到的助记符代码
74fe2e4c74e09b654bf6e9cdb043deb05548e95b22cd4cf34423401401c96e1c320c9e1f55546d2e530399cf047608bcd6a64129662fe1178f8c8698699316ed
我们讲助记符代码拆分为秘钥和链码,(这里有些同学称呼为 私钥和链码,都可以,但作者分析过很多国外的项目,觉得称为秘钥和链码最为合适)
#define BIP32_SEED_KEY_IOST @"ed25519 seed"
// 1. 将助记符代码进行HMac运算,运算中运用hash512,并加入key。
UInt512 hmac_data = (UInt512 )[masterSeed SDK_HMACWithAlgorithm:kCCHmacAlgSHA512 key:BIP32_SEED_KEY_IOST].bytes;
// 2. SHA512的结果为64个Byte,我们将结果进行拆分,前32个Byte就是秘钥、后32个Byte就是链码
UInt256 secret = (UInt256 )&hmac_data;
UInt256 chain = (UInt256 )&hmac_data.u8[sizeof(UInt256)];
// 打印数据
hmac_data结果:cae09bc9de80adecab04a47fd758d48f84c3654d7aed924ef22e018774db94937b300b536fbef8e101b04f7763a1c49b529c2c582dd6267e521997eceb8bbd03
secret :cae09bc9de80adecab04a47fd758d48f84c3654d7aed924ef22e018774db9493
chain :7b300b536fbef8e101b04f7763a1c49b529c2c582dd6267e521997eceb8bbd03
得到最上层的秘钥和链码后,我们就可以通过封层确定性来推出指定路径的秘钥和链码。
+ (NSString )p_GetIOSTXEMPrivateKeyWithMasterSeed:(NSData )masterSeed path:(NSString *)path slip44Code:(uint32_t)slip44_code
{
// 1. 将助记符代码进行HMac运算,运算中运用hash512,并加入key。
UInt512 hmac_data = (UInt512 )[masterSeed SDK_HMACWithAlgorithm:kCCHmacAlgSHA512 key:BIP32_SEED_KEY_IOST].bytes;
UInt256 secret = (UInt256 )&hmac_data;
UInt256 chain = (UInt256 )&hmac_data.u8[sizeof(UInt256)];
// 2. SHA512的结果为64个Byte,我们将结果进行拆分,前32个Byte就是秘钥、后32个Byte就是链码
NSArray *pathArray = [path componentsSeparatedByString:@"/"];
for (NSString *path_info in pathArray) {
if ([path_info isEqualToString:@"m"]) {
continue;
}else if ([path_info rangeOfString:@"'"].location != NSNotFound){
// 2.5 将路径的某一个增强层级转换为索引
unsigned int index = [[path_info stringByReplacingOccurrencesOfString:@"'" withString:@""] intValue];
// 2.6 执行分层确定性进行分层运算
hdnode_private_ckd_ed25519(&secret, &chain, index | BIP32_HARD);
}
}
}
// 第一层结果打印
Path : m/44'/
secret :2ead62c97341eb99f81ed7bca36d640785a5a61f9d78d00d8f5f72e9ad1f166f
chain :325ebb3488faf46d5832e001d7d825cf8ea75c38ba3ddf6357ff0f34f448625b
// 第二层结果打印
Path : m/44'/291’
secret :76edf06cfad75c3a88864b5d0905a59de504536109a7b4f2ffc6190d1f392783
chain :ece82ad47e8483dc55880d1061854721871a57b041c6927486031d50a05a8153
// 第三层结果打印
Path : m/44'/291'/0'
secret :4486c6bed9c43cf1bf95ab793117d270c9747b6076b22fcfeb5dfb01499db37a
chain :8cb3b546f08ee763946252ee612cb93bc89c22961611ef5d98831e0f922d71d6
通过最后一层的秘钥和链码推导私钥
// 1. 将秘钥和链码合并转换为Data,非OC语言可以理解为转换为Byte
NSMutableData *secret_data = [NSMutableData data];
[secret_data appendData:[NSData dataWithBytes:&secret length:sizeof(secret)]];
[secret_data appendData:[NSData dataWithBytes:&chain length:sizeof(chain)]];
// 打印
secret_data结果:4486c6bed9c43cf1bf95ab793117d270c9747b6076b22fcfeb5dfb01499db37a8cb3b546f08ee763946252ee612cb93bc89c22961611ef5d98831e0f922d71d6
// 2. 将结合后的结果进行base58得到可视化私钥
size_t len = 100;
char base[100];
eos_encode_base58(base, &len, secret_data.bytes,64);
// 打印
Po [NSString stringWithUTF8String:base]
结果:2NTtrXqourN1ULTNxk6uW1H8W58ZZN5K9TzvrVnYVpb4cRwtDFqs9ZUuW9Cswo5zUJptHe7qXSqCH324dLocPvdT
上面的代码中,将秘钥和链码结合后其实就是我们要获取的私钥。
市面上部分钱包时这么做的,也有部分钱包后面拼接的是各种运算公钥得到的结果,并不是很统一,没有一个统一的格式,但是这并不影响我们签名。
在IOST的签名中,其实我们真正用到的就是前32个Byte,后32个Byte根本就没有用到,至于为什么要这样,作者有时间找官方的人问一下再回来解答。
最后进行base58是将私钥转换为可视化私钥的一种方法,不同的私钥base58编码后长度lenght有可能是88和87。
公钥(Onwerkey)生成
我们先将私钥的原因就在于需要通过私钥来计算公钥,用到的算法为Ed25519。
// 1. 第一步我们获取私钥,只需要前32个Byte也就是分层确定性后的secret(秘钥)
NSMutableData *private_data = [NSMutableData dataWithBytes:&seckey length:sizeof(seckey)];
// 打印
private_data结果 :4486c6bed9c43cf1bf95ab793117d270c9747b6076b22fcfeb5dfb01499db37a
// 2. ED25519算法推算公钥
NSMutableData *pub_data = [NSMutableData dataWithLength:32];
ed25519_publickey(private_data.bytes, pub_data.mutableBytes);
// 打印
pub_data结果 :b1eaab369b795519fe52201f1f00cfe894d7127cf3a2f6ae1020c6759af5e852
// 3. 进行base58编码
size_t len = 100;
char base[100];
eos_encode_base58(base, &len, pub_data.bytes,32);
// 打印
Po [NSString stringWithUTF8String:base]
结果:CyWhstPxdauhZcM9xFKMWWSGAHHQkHhPtnyZsgXdhsQM
公钥的Base58编码其实就是真正注册新账户用到的Onwerkey,以及我们查询一个公钥下有哪些账户用到的也是公钥的Base58编码后的结果。
总结
大家要知道Base58编码只是为了让编码的内容更方便大家来看而已,真正的签名用到的秘钥是将编码后的数据解码后的数据。
要支持IOST的小伙伴需要注意,私钥Base58解码后得到的长度时64个Byte,但签名的时候只会取前32个Byte,后面32个Byte没有用到。官方也没有统一标准,所以私钥导入的钱包,最好保存用户输入的完整私钥,不要截取,否则导出的时候直接呈现给用户后用户会一脸懵逼。
分层确定性不懂得小伙伴可以看一下基础知识模块内容,谢谢支持!