/* * Copyright (C) 2018-2019 The ontology Authors * This file is part of The ontology library. * * The ontology is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The ontology is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with The ontology. If not, see <http://www.gnu.org/licenses/>. * */ package com.github.ontio.crypto.bip32; import com.github.ontio.common.Address; import com.github.ontio.common.Helper; import com.github.ontio.crypto.Base58; import com.github.ontio.crypto.Digest; import com.github.ontio.crypto.bip32.derivation.CkdFunction; import com.github.ontio.crypto.bip32.derivation.Derivation; import com.github.ontio.crypto.bip32.derivation.CkdFunctionDerive; import com.github.ontio.crypto.bip32.derivation.Derive; import com.github.ontio.sdk.exception.SDKException; import org.spongycastle.math.ec.ECPoint; import java.math.BigInteger; import static com.github.ontio.crypto.bip32.ByteArrayWriter.head32; import static com.github.ontio.crypto.bip32.ByteArrayWriter.tail32; import static com.github.ontio.crypto.bip32.HdKey.parse256; import static com.github.ontio.crypto.bip32.Secp256r1SC.gMultiplyAndAddPoint; import static com.github.ontio.crypto.bip32.Secp256r1SC.n; import static com.github.ontio.crypto.bip32.Secp256r1SC.pointSerP; import static com.github.ontio.crypto.bip32.derivation.CharSequenceDerivation.isHardened; public final class HdPublicKey implements Derive<HdPublicKey>, CKDpub { private static Deserializer<HdPublicKey> deserializer() { return HdPublicKeyDeserializer.DEFAULT; } public static Deserializer<HdPublicKey> deserializer(final Network network) { return new HdPublicKeyDeserializer(network); } private static final CkdFunction<HdPublicKey> CKD_FUNCTION = HdPublicKey::cKDpub; static HdPublicKey from(final HdKey hdKey) { return new HdPublicKey(new HdKey.Builder() .network(hdKey.getNetwork()) .neutered(true) .key(hdKey.getPoint()) .parentFingerprint(hdKey.getParentFingerprint()) .depth(hdKey.depth()) .childNumber(hdKey.getChildNumber()) .chainCode(hdKey.getChainCode()) .build()); } private final HdKey hdKey; HdPublicKey(final HdKey hdKey) { this.hdKey = hdKey; } @Override public HdPublicKey cKDpub(final int index) { if (isHardened(index)) { return null; } final HdKey parent = this.hdKey; final byte[] kPar = parent.getKey(); final byte[] data = new byte[37]; final ByteArrayWriter writer = new ByteArrayWriter(data); writer.concat(kPar, 33); writer.concatSer32(index); final byte[] I = Digest.hmacSha512(parent.getChainCode(), data); final byte[] Il = head32(I); final byte[] Ir = tail32(I); final BigInteger parse256_Il = parse256(Il); final ECPoint ki = gMultiplyAndAddPoint(parse256_Il, kPar); if (parse256_Il.compareTo(n()) >= 0 || ki.isInfinity()) { return cKDpub(index + 1); } final byte[] key = pointSerP(ki); return new HdPublicKey(new HdKey.Builder() .network(parent.getNetwork()) .neutered(true) .depth(parent.depth() + 1) .parentFingerprint(parent.calculateFingerPrint()) .key(key) .chainCode(Ir) .childNumber(index) .build()); } public static HdPublicKey base58Decode(String key) throws SDKException { return HdPublicKey.deserializer().deserialize(Base58.decode(key)); } public Address getAddress() { return Address.addressFromPubKey(hdKey.getKey()); } private Derive<HdPublicKey> derive() { return new CkdFunctionDerive<>(CKD_FUNCTION, this); } @Override public HdPublicKey fromPath(final CharSequence derivationPath) { final int length = derivationPath.length(); if (length == 0) throw new IllegalArgumentException("Path cannot be empty"); if (length == 1) return this; if (derivationPath.charAt(0) == 'm' && depth() == 0) { if (derivationPath.charAt(1) != '/') throw new IllegalArgumentException("Root key must be a master key if the path start with m/"); return derive().fromPath(derivationPath.subSequence(2, derivationPath.length())); } return derive().fromPath(derivationPath); } @Override public <Path> HdPublicKey fromPath(final Path derivationPath, final Derivation<Path> derivation) { return derive().fromPath(derivationPath, derivation); } public byte[] toByteArray() { return hdKey.serialize(); } public Network network() { return hdKey.getNetwork(); } public int depth() { return hdKey.depth(); } public int childNumber() { return hdKey.getChildNumber(); } public String toHexString() { return Helper.toHexString(hdKey.getKey()); } public String base58Encode() { return Base58.encode(toByteArray()); } }