/******************************************************************************
* Copyright © 2013-2016 The Nxt Core Developers. *
* *
* See the AUTHORS.txt, DEVELOPER-AGREEMENT.txt and LICENSE.txt files at *
* the top-level directory of this distribution for the individual copyright *
* holder information and the developer policies on copyright and licensing. *
* *
* Unless otherwise agreed in a custom licensing agreement, no part of the *
* Nxt software, including this file, may be copied, modified, propagated, *
* or distributed except according to the terms contained in the LICENSE.txt *
* file. *
* *
* Removal or modification of this copyright notice is prohibited. *
* *
******************************************************************************/
package nxt;
import nxt.crypto.Crypto;
import nxt.util.Convert;
import java.util.Arrays;
public final class Token {
public static String generateToken(String secretPhrase, String messageString) {
return generateToken(secretPhrase, Convert.toBytes(messageString));
}
public static String generateToken(String secretPhrase, byte[] message) {
byte[] data = new byte[message.length + 32 + 4];
System.arraycopy(message, 0, data, 0, message.length);
System.arraycopy(Crypto.getPublicKey(secretPhrase), 0, data, message.length, 32);
int timestamp = Nxt.getEpochTime();
data[message.length + 32] = (byte)timestamp;
data[message.length + 32 + 1] = (byte)(timestamp >> 8);
data[message.length + 32 + 2] = (byte)(timestamp >> 16);
data[message.length + 32 + 3] = (byte)(timestamp >> 24);
byte[] token = new byte[100];
System.arraycopy(data, message.length, token, 0, 32 + 4);
System.arraycopy(Crypto.sign(data, secretPhrase), 0, token, 32 + 4, 64);
StringBuilder buf = new StringBuilder();
for (int ptr = 0; ptr < 100; ptr += 5) {
long number = ((long)(token[ptr] & 0xFF)) | (((long)(token[ptr + 1] & 0xFF)) << 8) | (((long)(token[ptr + 2] & 0xFF)) << 16)
| (((long)(token[ptr + 3] & 0xFF)) << 24) | (((long)(token[ptr + 4] & 0xFF)) << 32);
if (number < 32) {
buf.append("0000000");
} else if (number < 1024) {
buf.append("000000");
} else if (number < 32768) {
buf.append("00000");
} else if (number < 1048576) {
buf.append("0000");
} else if (number < 33554432) {
buf.append("000");
} else if (number < 1073741824) {
buf.append("00");
} else if (number < 34359738368L) {
buf.append("0");
}
buf.append(Long.toString(number, 32));
}
return buf.toString();
}
public static Token parseToken(String tokenString, String website) {
return parseToken(tokenString, Convert.toBytes(website));
}
public static Token parseToken(String tokenString, byte[] messageBytes) {
byte[] tokenBytes = new byte[100];
int i = 0, j = 0;
for (; i < tokenString.length(); i += 8, j += 5) {
long number = Long.parseLong(tokenString.substring(i, i + 8), 32);
tokenBytes[j] = (byte)number;
tokenBytes[j + 1] = (byte)(number >> 8);
tokenBytes[j + 2] = (byte)(number >> 16);
tokenBytes[j + 3] = (byte)(number >> 24);
tokenBytes[j + 4] = (byte)(number >> 32);
}
if (i != 160) {
throw new IllegalArgumentException("Invalid token string: " + tokenString);
}
byte[] publicKey = new byte[32];
System.arraycopy(tokenBytes, 0, publicKey, 0, 32);
int timestamp = (tokenBytes[32] & 0xFF) | ((tokenBytes[33] & 0xFF) << 8) | ((tokenBytes[34] & 0xFF) << 16) | ((tokenBytes[35] & 0xFF) << 24);
byte[] signature = new byte[64];
System.arraycopy(tokenBytes, 36, signature, 0, 64);
byte[] data = new byte[messageBytes.length + 36];
System.arraycopy(messageBytes, 0, data, 0, messageBytes.length);
System.arraycopy(tokenBytes, 0, data, messageBytes.length, 36);
byte[] announcedPublicKey = Account.getPublicKey(Account.getId(publicKey));
boolean isValid = Crypto.verify(signature, data, publicKey, true)
&& (announcedPublicKey == null || Arrays.equals(publicKey, announcedPublicKey));
return new Token(publicKey, timestamp, isValid);
}
private final byte[] publicKey;
private final int timestamp;
private final boolean isValid;
private Token(byte[] publicKey, int timestamp, boolean isValid) {
this.publicKey = publicKey;
this.timestamp = timestamp;
this.isValid = isValid;
}
public byte[] getPublicKey() {
return publicKey;
}
public int getTimestamp() {
return timestamp;
}
public boolean isValid() {
return isValid;
}
}