Skip to content

Commit

Permalink
move sanity check prog into logicsig def
Browse files Browse the repository at this point in the history
  • Loading branch information
ahangsu committed Aug 25, 2022
1 parent 7cae4f5 commit 0edecf8
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 91 deletions.
59 changes: 57 additions & 2 deletions src/main/java/com/algorand/algosdk/crypto/LogicsigSignature.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import org.apache.commons.codec.binary.Base64;

/**
* Serializable logicsig class.
Expand All @@ -39,6 +40,60 @@ public class LogicsigSignature {
@JsonProperty("msig")
public MultisigSignature msig;


private static boolean isAsciiPrintable(final byte symbol) {
char symbolChar = (char) (symbol & 0xFF);
// linebreak existence check in program byte
boolean isBreakLine = symbolChar == '\n';
// printable ascii between range 32 (space) and 126 (tilde ~)
boolean isStdPrintable = symbolChar >= ' ' && symbolChar <= '~';
return isBreakLine || isStdPrintable;
}

private static boolean isAsciiPrintable(final byte[] program) {
for (byte b : program) {
if (!isAsciiPrintable(b))
return false;
}
return true;
}

/**
* Performs heuristic program validation:
* check if passed in bytes are Algorand address or is B64 encoded, rather than Teal bytes
* @param program
*/
private static void sanityCheckProgram(final byte[] program) {
if (program == null || program.length == 0)
throw new IllegalArgumentException("empty program");
// in any case, if a slice of "program-bytes" is full of ASCII printable,
// then the slice of bytes can't be Teal program bytes.
// need to check what possible kind of bytes are passed in.
if (isAsciiPrintable(program)) {
// maybe the bytes passed in are representing an Algorand address
boolean isAddress = false;
try {
new Address(new String(program));
isAddress = true;
} catch (NoSuchAlgorithmException | IllegalArgumentException e) {
// if exception is IllegalArgException, it means bytes are not Algorand address
if (e instanceof NoSuchAlgorithmException)
throw new IllegalArgumentException("cannot check if program bytes are Algorand address" + e);
}
if (isAddress)
throw new IllegalArgumentException("requesting program bytes, get Algorand address");

// or maybe these bytes are some B64 encoded bytes representation
if (Base64.isBase64(program))
throw new IllegalArgumentException("program should not be b64 encoded");

// can't further analyze, but it is more than just B64 encoding at this point
throw new IllegalArgumentException(
"program bytes are all ASCII printable characters, not looking like Teal byte code"
);
}
}

@JsonCreator
public LogicsigSignature(
@JsonProperty("l") byte[] logic,
Expand All @@ -49,7 +104,7 @@ public LogicsigSignature(
this.logic = Objects.requireNonNull(logic, "program must not be null");
this.args = args;

Logic.sanityCheckProgram(this.logic);
sanityCheckProgram(this.logic);

if (sig != null) this.sig = new Signature(sig);
this.msig = msig;
Expand Down Expand Up @@ -118,7 +173,7 @@ public boolean verify(Address singleSigner) throws NoSuchAlgorithmException {
return false;
}

Logic.sanityCheckProgram(this.logic);
sanityCheckProgram(this.logic);

PublicKey pk;
try {
Expand Down
91 changes: 4 additions & 87 deletions src/main/java/com/algorand/algosdk/logic/Logic.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,39 @@

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.algorand.algosdk.crypto.Address;
import org.apache.commons.codec.binary.Base64;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
* Logic class provides static checkProgram function
* that can be used for client-side program validation for size and execution cost.
*
* @deprecated this class is deprecated for relying on metadata (`langspec.json`) that
* does not accurately represent opcode behavior across program versions.
*/
@Deprecated
public class Logic {

@Deprecated
private static final int MAX_COST = 20000;
@Deprecated
private static final int MAX_LENGTH = 1000;

@Deprecated
private static final int INTCBLOCK_OPCODE = 32;
@Deprecated
private static final int BYTECBLOCK_OPCODE = 38;
@Deprecated
private static final int PUSHBYTES_OPCODE = 128;
@Deprecated
private static final int PUSHINT_OPCODE = 129;

@Deprecated
private class LangSpec {
public int EvalMaxVersion;
public int LogicSigVersion;
public Operation[] Ops;
}

@Deprecated
private class Operation {
int Opcode;
String Name;
Expand All @@ -58,7 +51,6 @@ private class Operation {
/**
* Metadata related to a teal program.
*/
@Deprecated
public static class ProgramData {
public final boolean good;
public final List<Integer> intBlock;
Expand All @@ -74,7 +66,6 @@ private ProgramData(final boolean good, final List<Integer> intBlock, final List
/**
* Metadata related to a varint parsed from teal program data.
*/
@Deprecated
public static class VarintResult {
final public int value;
final public int length;
Expand All @@ -90,7 +81,6 @@ private VarintResult() {
}
}

@Deprecated
protected static class IntConstBlock {
public final int size;
public final List<Integer> results;
Expand All @@ -101,7 +91,6 @@ protected static class IntConstBlock {
}
}

@Deprecated
protected static class ByteConstBlock {
public final int size;
public final List<byte[]> results;
Expand All @@ -124,7 +113,6 @@ protected static class ByteConstBlock {
* @param value being serialized
* @return byte array holding the serialized bits
*/
@Deprecated
public static byte[] putUVarint(int value) {
assert value >= 0 : "putUVarint expects non-negative values.";
List<Byte> buffer = new ArrayList<>();
Expand All @@ -146,7 +134,6 @@ public static byte[] putUVarint(int value) {
* @param bufferOffset position in the buffer to start reading from
* @return pair of values in an array: value, read size
*/
@Deprecated
public static VarintResult getUVarint(byte [] buffer, int bufferOffset) {
int x = 0;
int s = 0;
Expand All @@ -164,92 +151,29 @@ public static VarintResult getUVarint(byte [] buffer, int bufferOffset) {
return new VarintResult();
}

private static boolean isAsciiPrintable(final byte symbol) {
char symbolChar = (char) (symbol & 0xFF);
// linebreak existence check in program byte
boolean isBreakLine = symbolChar == '\n';
// printable ascii between range 32 (space) and 126 (tilde ~)
boolean isStdPrintable = symbolChar >= ' ' && symbolChar <= '~';
return isBreakLine || isStdPrintable;
}

private static boolean isAsciiPrintable(final byte[] program) {
for (byte b : program) {
if (!isAsciiPrintable(b))
return false;
}
return true;
}

/**
* Performs heuristic program validation:
* check if passed in bytes are Algorand address or is B64 encoded, rather than Teal bytes
* @param program
*/
public static void sanityCheckProgram(final byte[] program) {
if (program == null || program.length == 0)
throw new IllegalArgumentException("empty program");
// in any case, if a slice of "program-bytes" is full of ASCII printable,
// then the slice of bytes can't be Teal program bytes.
// need to check what possible kind of bytes are passed in.
if (isAsciiPrintable(program)) {
// maybe the bytes passed in are representing an Algorand address
boolean isAddress = false;
try {
new Address(new String(program));
isAddress = true;
} catch (NoSuchAlgorithmException | IllegalArgumentException e) {
// if exception is IllegalArgException, it means bytes are not Algorand address
if (e instanceof NoSuchAlgorithmException)
throw new IllegalArgumentException("cannot check if program bytes are Algorand address" + e);
}
if (isAddress)
throw new IllegalArgumentException("requesting program bytes, get Algorand address");

// or maybe these bytes are some B64 encoded bytes representation
if (Base64.isBase64(program))
throw new IllegalArgumentException("program should not be b64 encoded");

// can't further analyze, but it is more than just B64 encoding at this point
throw new IllegalArgumentException(
"program bytes are all ASCII printable characters, not looking like Teal byte code"
);
}
}

@Deprecated
private static LangSpec langSpec;
@Deprecated
private static Operation[] opcodes;

/**
* Performs basic program validation: instruction count and program cost
* @deprecated Validation relies on metadata (`langspec.json`) that
* does not accurately represent opcode behavior across program versions.
* The behavior of `checkProgram` relies on `langspec.json`. Thus, this method is being deprecated.
*
* @param program
* @param args
* @return
* @throws IOException
*/
@Deprecated
public static boolean checkProgram(byte[] program, List<byte[]> args) throws IOException {
return readProgram(program, args).good;
}

/**
* Performs basic program validation: instruction count and program cost
* @deprecated Validation relies on metadata (`langspec.json`) that
* does not accurately represent opcode behavior across program versions.
* The behavior of `readProgram` relies on `langspec.json`. Thus, this method is being deprecated.
*
* @param program Program to validate
* @param args Program arguments to validate
* @return boolean
* @throws IOException
*/
@Deprecated
public static ProgramData readProgram(byte[] program, List<byte[]> args) throws IOException {
List<Integer> ints = new ArrayList<>();
List<byte[]> bytes = new ArrayList<>();
Expand Down Expand Up @@ -342,7 +266,6 @@ public static ProgramData readProgram(byte[] program, List<byte[]> args) throws
* @return int
* @throws IOException
*/
@Deprecated
public static int getLogicSigVersion() throws IOException {
if (langSpec == null) {
loadLangSpec();
Expand All @@ -355,15 +278,13 @@ public static int getLogicSigVersion() throws IOException {
* @return int
* @throws IOException
*/
@Deprecated
public static int getEvalMaxVersion() throws IOException {
if (langSpec == null) {
loadLangSpec();
}
return langSpec.EvalMaxVersion;
}

@Deprecated
private static void loadLangSpec() throws IOException {
if (langSpec != null) {
return;
Expand All @@ -385,7 +306,6 @@ private static void loadLangSpec() throws IOException {
reader.close();
}

@Deprecated
protected static IntConstBlock readIntConstBlock(byte[] program, int pc) {
ArrayList<Integer> results = new ArrayList<>();

Expand Down Expand Up @@ -414,7 +334,6 @@ protected static IntConstBlock readIntConstBlock(byte[] program, int pc) {
return new IntConstBlock(size, results);
}

@Deprecated
protected static ByteConstBlock readByteConstBlock(byte[] program, int pc) {
ArrayList<byte[]> results = new ArrayList<>();
int size = 1;
Expand Down Expand Up @@ -448,7 +367,6 @@ protected static ByteConstBlock readByteConstBlock(byte[] program, int pc) {
return new ByteConstBlock(size, results);
}

@Deprecated
protected static IntConstBlock readPushIntOp(byte[] program, int pc) {
int size = 1;
VarintResult result = getUVarint(program, pc + size);
Expand All @@ -462,7 +380,6 @@ protected static IntConstBlock readPushIntOp(byte[] program, int pc) {
return new IntConstBlock(size, Collections.singletonList(result.value));
}

@Deprecated
protected static ByteConstBlock readPushByteOp(byte[] program, int pc) {
int size = 1;
VarintResult result = getUVarint(program, pc + size);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.algorand.algosdk.unit;

import com.algorand.algosdk.logic.Logic;
import com.algorand.algosdk.crypto.LogicsigSignature;
import com.algorand.algosdk.util.Encoder;

import io.cucumber.java.en.Given;
Expand All @@ -21,7 +21,7 @@ public void takeB64encodedBytes(String b64encodedBytes) {
@When("I start heuristic sanity check over the bytes")
public void heuristicCheckOverBytes() {
try {
Logic.sanityCheckProgram(seeminglyProgram);
new LogicsigSignature(seeminglyProgram);
} catch (Exception e) {
actualErrMsg = e.getMessage();
}
Expand Down

0 comments on commit 0edecf8

Please sign in to comment.