/*
*******************************************************************************
* Java Card Bitcoin Hardware Wallet
* (c) 2015 Ledger
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************
*/
package com.ledger.wallet;
import javacard.framework.Util;
import javacard.security.Signature;
public class Bip32 {
protected static final short OFFSET_DERIVATION_INDEX = (short)64;
private static final byte BITCOIN_SEED[] = {
'B', 'i', 't', 'c', 'o', 'i', 'n', ' ', 's', 'e', 'e', 'd'
};
private static final short OFFSET_TMP = (short)100;
private static final short OFFSET_BLOCK = (short)127;
// seed : scratch, offset 0 -> result in masterDerived
// keyHmac was duplicated because
// depending on the implementation, if a native transient HMAC is used, the key size might be fixed
// on the first call
// if that's the case, power cycle / deselect between initial seed derivation and all other key derivations
// would solve it
public static void deriveSeed(byte seedLength) {
if (Crypto.signatureHmac != null) {
Crypto.keyHmac2.setKey(BITCOIN_SEED, (short)0, (short)BITCOIN_SEED.length);
if ((LedgerWalletApplet.proprietaryAPI != null) && (LedgerWalletApplet.proprietaryAPI.hasHmacSHA512())) {
LedgerWalletApplet.proprietaryAPI.hmacSHA512(Crypto.keyHmac2, LedgerWalletApplet.scratch256, (short)0, seedLength, LedgerWalletApplet.masterDerived, (short)0);
}
else {
Crypto.signatureHmac.init(Crypto.keyHmac2, Signature.MODE_SIGN);
Crypto.signatureHmac.sign(LedgerWalletApplet.scratch256, (short)0, seedLength, LedgerWalletApplet.masterDerived, (short)0);
}
}
else {
HmacSha512.hmac(BITCOIN_SEED, (short)0, (short)BITCOIN_SEED.length, LedgerWalletApplet.scratch256, (short)0, seedLength, LedgerWalletApplet.masterDerived, (short)0, LedgerWalletApplet.scratch256, (short)64);
}
}
// scratch255 : 0-64 : key + chain / 64-67 : derivation index / 100-165 : tmp
// apduBuffer : block (128, starting at 127)
// result : scratch255 0-64
public static boolean derive(byte[] apduBuffer) {
boolean isZero = true;
byte i;
if ((LedgerWalletApplet.scratch256[OFFSET_DERIVATION_INDEX] & (byte)0x80) == 0) {
if (LedgerWalletApplet.proprietaryAPI != null) {
LedgerWalletApplet.proprietaryAPI.getUncompressedPublicPoint(LedgerWalletApplet.scratch256, (short)0, LedgerWalletApplet.scratch256, OFFSET_TMP);
}
else {
if (!Bip32Cache.copyLastPublic(LedgerWalletApplet.scratch256, OFFSET_TMP)) {
return false;
}
}
AddressUtils.compressPublicKey(LedgerWalletApplet.scratch256, OFFSET_TMP);
}
else {
LedgerWalletApplet.scratch256[OFFSET_TMP] = 0;
Util.arrayCopyNonAtomic(LedgerWalletApplet.scratch256, (short)0, LedgerWalletApplet.scratch256, (short)(OFFSET_TMP + 1), (short)32);
}
Util.arrayCopyNonAtomic(LedgerWalletApplet.scratch256, OFFSET_DERIVATION_INDEX, LedgerWalletApplet.scratch256, (short)(OFFSET_TMP + 33), (short)4);
if (Crypto.signatureHmac != null) {
Crypto.keyHmac.setKey(LedgerWalletApplet.scratch256, (short)32, (short)32);
if ((LedgerWalletApplet.proprietaryAPI != null) && (LedgerWalletApplet.proprietaryAPI.hasHmacSHA512())) {
LedgerWalletApplet.proprietaryAPI.hmacSHA512(Crypto.keyHmac, LedgerWalletApplet.scratch256, OFFSET_TMP, (short)37, LedgerWalletApplet.scratch256, OFFSET_TMP);
}
else {
Crypto.signatureHmac.init(Crypto.keyHmac, Signature.MODE_SIGN);
Crypto.signatureHmac.sign(LedgerWalletApplet.scratch256, OFFSET_TMP, (short)37, LedgerWalletApplet.scratch256, OFFSET_TMP);
}
}
else {
HmacSha512.hmac(LedgerWalletApplet.scratch256, (short)32, (short)32, LedgerWalletApplet.scratch256, OFFSET_TMP, (short)37, LedgerWalletApplet.scratch256, OFFSET_TMP, apduBuffer, OFFSET_BLOCK);
}
if (MathMod256.ucmp(LedgerWalletApplet.scratch256, OFFSET_TMP, Secp256k1.SECP256K1_R, (short)0) >= 0) {
return false;
}
MathMod256.addm(LedgerWalletApplet.scratch256, (short)0, LedgerWalletApplet.scratch256, OFFSET_TMP, LedgerWalletApplet.scratch256, (short)0, Secp256k1.SECP256K1_R, (short)0);
for (i=0; i<(byte)32; i++) {
if (LedgerWalletApplet.scratch256[i] != 0) {
isZero = false;
break;
}
}
if (isZero) {
return false;
}
Util.arrayCopyNonAtomic(LedgerWalletApplet.scratch256, (short)(OFFSET_TMP + 32), LedgerWalletApplet.scratch256, (short)32, (short)32);
return true;
}
}