diff --git a/src/main/java/com/algorand/algosdk/crypto/LogicsigSignature.java b/src/main/java/com/algorand/algosdk/crypto/LogicsigSignature.java index 313ac99bb..73f7d66c4 100644 --- a/src/main/java/com/algorand/algosdk/crypto/LogicsigSignature.java +++ b/src/main/java/com/algorand/algosdk/crypto/LogicsigSignature.java @@ -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. @@ -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, @@ -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; @@ -118,7 +173,7 @@ public boolean verify(Address singleSigner) throws NoSuchAlgorithmException { return false; } - Logic.sanityCheckProgram(this.logic); + sanityCheckProgram(this.logic); PublicKey pk; try { diff --git a/src/main/java/com/algorand/algosdk/logic/Logic.java b/src/main/java/com/algorand/algosdk/logic/Logic.java index 8b123077b..a6825f314 100644 --- a/src/main/java/com/algorand/algosdk/logic/Logic.java +++ b/src/main/java/com/algorand/algosdk/logic/Logic.java @@ -2,14 +2,11 @@ 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; @@ -17,7 +14,11 @@ /** * 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 @@ -164,59 +165,6 @@ 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 diff --git a/src/test/java/com/algorand/algosdk/unit/ProgramSanityCheck.java b/src/test/java/com/algorand/algosdk/unit/ProgramSanityCheck.java index 7429b91c7..6e9ffde03 100644 --- a/src/test/java/com/algorand/algosdk/unit/ProgramSanityCheck.java +++ b/src/test/java/com/algorand/algosdk/unit/ProgramSanityCheck.java @@ -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; @@ -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(); }