Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/v3 #60

Open
wants to merge 86 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
1b0b5c7
feat(V3): prepare tests
lemoustachiste Apr 12, 2022
4be4c47
test(All): fix failures
lemoustachiste Apr 12, 2022
9add513
test(CertificateFragment): passing test v2
lemoustachiste Apr 12, 2022
87138c6
fix(CertificateFragment): remove lone closing script tag
lemoustachiste Apr 12, 2022
1627373
refactor(Blockcert): implement abstract method getDisplayHtml to enca…
lemoustachiste Apr 12, 2022
90cf866
test(Parser): test v2 parser
lemoustachiste Apr 13, 2022
1f1388a
test(BlockCertParser): add v1.2 test and ensure version output
lemoustachiste Apr 19, 2022
8cf677a
fix(BlockCertParser): ensure v1.1 gets parsed
lemoustachiste Apr 19, 2022
f088a22
refactor(BlockCertAdapter): encapsulate responsibility of setting doc…
lemoustachiste Apr 19, 2022
717db25
refactor(BlockCertAdapter): dynamically choose adapter class
lemoustachiste Apr 19, 2022
25b0783
refactor(BlockCertAdapter): rely on @context property to figure out b…
lemoustachiste Apr 20, 2022
ef5be17
refactor(BlockCertAdapter): extract to method to better document beha…
lemoustachiste Apr 20, 2022
d5c737b
style(BlockCertAdapter): remove trailing prints
lemoustachiste Apr 20, 2022
90c7a64
test(BlockCertParser): re-instate tests mistakenly erased by commit 8…
lemoustachiste Apr 20, 2022
cf13c79
feat(V3): implement BlockCertV30 class
lemoustachiste Apr 20, 2022
6ee0d10
feat(v3): implement displayHtml method
lemoustachiste Apr 20, 2022
6fe3cf1
refactor(Tests): create v3 fixtures directory
lemoustachiste Apr 20, 2022
8ac7865
feat(v3): support image display
lemoustachiste Apr 20, 2022
670801d
feat(v3): support pdf display
lemoustachiste Apr 20, 2022
9503e0b
refactor(Tests): centralize readFileAsString method
lemoustachiste Apr 21, 2022
31cc564
refactor(Tests): abstract get content method
lemoustachiste Apr 21, 2022
c3ea90a
feat(V3): retrieve issuer id according to shape of data
lemoustachiste Apr 21, 2022
a408f72
feat(V3): expose cert id
lemoustachiste Apr 22, 2022
942ef84
refactor(Test): abstract fileToBlockCert method
lemoustachiste Apr 22, 2022
6026717
feat(V3): implement getName
lemoustachiste Apr 22, 2022
5b30c05
feat(V3): implement getUrl
lemoustachiste Apr 22, 2022
0c89dc4
feat(V3): implement getDescription
lemoustachiste Apr 22, 2022
451d934
feat(V3): protect against empty values
lemoustachiste Apr 22, 2022
4f93a5f
feat(V3): implement getIssueDate
lemoustachiste Apr 22, 2022
e6c8661
feat(V3): implement getMetadata
lemoustachiste Apr 22, 2022
fed2962
refactor(V3): cast to expected type early
lemoustachiste Apr 22, 2022
2166d7e
test(V3): test getDisplayHtml
lemoustachiste Apr 22, 2022
056f88c
feat(V3): implement getExpirationDate
lemoustachiste Apr 22, 2022
83a5836
feat(V3): prevent NullPointerException in case undefined
lemoustachiste Apr 22, 2022
9b247ff
fix(Metadata): prevent information tab breakage if metadata is malformed
lemoustachiste Apr 26, 2022
346169c
feat(V3): implement getRecipientPublicKey
lemoustachiste Apr 26, 2022
7a2a2e1
feat(did): add isDid helper
lemoustachiste Apr 27, 2022
16fe090
feat(did): introduce DID models
lemoustachiste Apr 27, 2022
d1d312e
feat(did): retrieve data from did document
lemoustachiste Apr 27, 2022
af2cd65
feat(did): resolve DID before looking up issuer profile
lemoustachiste Apr 28, 2022
4541cdb
refactor(Test): abstract file to class method
lemoustachiste Apr 28, 2022
c5a7dc2
refactor(Test): make value a constant
lemoustachiste Apr 28, 2022
340123d
refactor(Test): extract getResourceAsStream method
lemoustachiste Apr 29, 2022
dd5414a
feat(did): update Certificate db with issuer id after DID retrieval
lemoustachiste Apr 29, 2022
65997be
feat(V3): update verifier.js
lemoustachiste May 5, 2022
08a2202
Merge branch 'master' of https://github.com/blockchain-certificates/w…
lemoustachiste Jun 23, 2022
0891660
feat(MultiSign): update verifier js code
lemoustachiste Jun 23, 2022
1365a85
feat(MultiSign): update verifier consumption
lemoustachiste Jun 23, 2022
255a250
feat(MultiSign): remove chain string from verification process as irr…
lemoustachiste Jun 23, 2022
3779595
feat(MultiSign): display substeps from verification suite
lemoustachiste Jun 23, 2022
03df504
refactor(MultiSign): centralize repetitive function
lemoustachiste Jun 23, 2022
15f9ec9
feat(MultiSign): maintain verification pipeline update
lemoustachiste Jun 23, 2022
1a2b9f7
refactor(MultiSign): remove unused property
lemoustachiste Jun 24, 2022
f128330
feat(MultiSign): maintain showing chain name at end of successful ver…
lemoustachiste Jun 24, 2022
779a46d
feat(MultiSign): display prooftype per group
lemoustachiste Jun 27, 2022
f759d26
feat(MultiSign): hide group title if group empty
lemoustachiste Jun 27, 2022
13ff49c
feat(MultiSign): align title
lemoustachiste Jun 27, 2022
5393e7a
feat(MultiSign): nicer padding of elements
lemoustachiste Jun 28, 2022
9f7e917
feat(MultiSign): do not display signature suite type when only one si…
lemoustachiste Jun 28, 2022
8c332b0
feat(MultiSign): fix height of placeholder status bar
lemoustachiste Jun 29, 2022
30191ba
refactor(MultiSign): remove proxy method and abstract title style method
lemoustachiste Jun 29, 2022
37e96e4
refactor(MultiSign): abstract start process method
lemoustachiste Jun 29, 2022
eb6f3a2
style(MultiSign): re-organize private/public methods
lemoustachiste Jun 29, 2022
5965ed4
fix(MultiSign): show step icon when last subitem is success
lemoustachiste Jun 29, 2022
b0e2e34
feat(MultiSign): retrieve parentStep from callback API of CVJS
lemoustachiste Jun 29, 2022
37e9a40
feat(MultiSign): animate status bar
lemoustachiste Jun 29, 2022
16275e1
refactor(MultiSign): extract error process
lemoustachiste Jun 29, 2022
43d11cd
refactor(MultiSign): centralize height change logic
lemoustachiste Jun 29, 2022
97476db
feat(MultiSign): position verification items to overlap status bar
lemoustachiste Jun 30, 2022
96d1817
feat(MultiSign): fix status bar final height
lemoustachiste Jun 30, 2022
df6ad6f
feat(MultiSign): push down verification success icon
lemoustachiste Jun 30, 2022
5bc73a3
fix(MultiSign): maintain placeholder bar height when error
lemoustachiste Jun 30, 2022
db92bad
refactor(MultiSign): rely on current applicable value rather than pot…
lemoustachiste Jun 30, 2022
1cd1b46
style(MultiSign): remove superfluous comments and unused method
lemoustachiste Jun 30, 2022
1043734
feat(MultiSign): prevent showing 'null' if chainName is not defined
lemoustachiste Jun 30, 2022
a90c208
Merge pull request #62 from blockchain-certificates/feat/multisign
lemoustachiste Jun 30, 2022
68f6769
Merge branch 'master' of https://github.com/blockchain-certificates/w…
lemoustachiste Feb 27, 2023
193a960
Merge branch 'master' of https://github.com/blockchain-certificates/w…
lemoustachiste Sep 27, 2024
24510f8
chore(Build): bump gradle version
lemoustachiste Sep 27, 2024
01c9811
refactor(UI): access id comparison updated after version change
lemoustachiste Sep 30, 2024
0d78437
fix(Deps): update version to avoid duplicate class conflict
lemoustachiste Sep 30, 2024
44b8715
feat(Release): bump version
lemoustachiste Sep 30, 2024
3186581
feat(Verifier): bump to v6.14.1
lemoustachiste Oct 2, 2024
2ad9a37
feat(V3): handle v3.1
lemoustachiste Oct 2, 2024
681cd71
chore(PlayStore): bump version, target sdk
lemoustachiste Oct 3, 2024
659fead
fix(V3): prevent failure when claim is not a json object
lemoustachiste Oct 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 17 additions & 9 deletions LearningMachine/app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
apply plugin: 'com.android.application'

android {
compileSdkVersion 33
buildToolsVersion '30.0.3'
namespace "com.learningmachine.android.app"
compileSdkVersion 34
dexOptions {
javaMaxHeapSize "4g"
}
defaultConfig {
applicationId "com.learningmachine.android.app"
minSdkVersion 19
targetSdkVersion 33
versionName "3.1.11"
minSdkVersion 31
targetSdkVersion 34
versionName "3.2.0"
def buildNumber = System.getenv("BUILD_NUMBER")
if (buildNumber != null) {
versionCode buildNumber.toInteger()
versionNameSuffix "-" + buildNumber
} else {
versionCode 58
versionCode 61
}

testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
Expand All @@ -28,6 +28,10 @@ android {
targetCompatibility JavaVersion.VERSION_1_8
}

testOptions {
unitTests.returnDefaultValues = true
}

signingConfigs {
LearningMachineRelease {
keyAlias System.getenv("SIGNING_KEY_ALIAS")
Expand Down Expand Up @@ -102,8 +106,10 @@ android {
}
}

dataBinding {
enabled = true
buildFeatures {
dataBinding true
viewBinding true
buildConfig true
}

lintOptions {
Expand Down Expand Up @@ -140,7 +146,7 @@ final JODATIME_VERSION = '2.8.1'

// These must be forced until everyone gets on the same page
// ?: 18.0, Robolectric: 20.0
final GUAVA_VERSION = '25.1-android'
final GUAVA_VERSION = '27.0.1-android'
// RxLifecycle: 3.0.1, Robolectric: 2.0.1
final FINDBUGS_VERSION = '3.0.1'

Expand Down Expand Up @@ -203,6 +209,8 @@ dependencies {

testImplementation "junit:junit:$JUNIT_VERSION"
testImplementation "org.hamcrest:hamcrest:2.2"
testImplementation "net.javacrumbs.json-unit:json-unit:2.33.0"
testImplementation "org.json:json:20140107"
testImplementation "org.mockito:mockito-core:$MOCKITO_VERSION"
testImplementation "org.robolectric:robolectric:$ROBOLECTRIC_VERSION"
testImplementation "org.robolectric:shadows-multidex:$ROBOLECTRIC_VERSION"
Expand Down
3 changes: 1 addition & 2 deletions LearningMachine/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.learningmachine.android.app">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
Expand Down
371 changes: 293 additions & 78 deletions LearningMachine/app/src/main/assets/www/verifier.js

Large diffs are not rendered by default.

9 changes: 6 additions & 3 deletions LearningMachine/app/src/main/assets/www/verify.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,15 @@
}

loadJSON("./certificate.json", async function(certificateContentsString) {
var certificate = new Verifier.Certificate(certificateContentsString, { locale: 'auto' });
var certificate = new Verifier.Certificate(certificateContentsString, {
locale: 'auto',
didResolverUrl: 'https://dev.uniresolver.io/1.0/identifiers'
});
await certificate.init();
Android.notifyChainName(certificate.chain.name);
var verificationSteps = certificate.verificationSteps;
Android.notifyVerificationSteps(JSON.stringify(verificationSteps));
certificate.verify(statusCallback);
await certificate.verify(statusCallback);
Android.notifyChainName(certificate.signers[0].chain.name);
});
</script>
</body>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ public class LMConstants {
public static final boolean SHOULD_PERFORM_OWNERSHIP_CHECK = false;
public static final String PLAY_STORE_URL = "market://details?id=com.learningmachine.android.app";
public static final String PLAY_STORE_EXTERNAL_URL = "https://play.google.com/store/apps/details?id=com.learningmachine.android.app";

public static final String DID_RESOLVER_URL = "https://dev.uniresolver.io/1.0/identifiers";
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.learningmachine.android.app.data.webservice.CertificateService;
import com.learningmachine.android.app.data.webservice.response.IssuerResponse;
import com.learningmachine.android.app.util.FileUtils;
import com.learningmachine.android.app.util.StringUtils;

import java.io.File;
import java.io.FileInputStream;
Expand All @@ -38,17 +39,19 @@ public class CertificateManager {

private final Context mContext;
private final CertificateStore mCertificateStore;
private final IssuerStore mIssuerStore;
private final CertificateService mCertificateService;
private final BitcoinManager mBitcoinManager;
private final IssuerManager mIssuerManager;

public CertificateManager(Context context, CertificateStore certificateStore,
IssuerStore issuerStore, CertificateService certificateService,
BitcoinManager bitcoinManager, IssuerManager issuerManager) {
public CertificateManager(
Context context,
CertificateStore certificateStore,
CertificateService certificateService,
BitcoinManager bitcoinManager,
IssuerManager issuerManager
) {
mContext = context;
mCertificateStore = certificateStore;
mIssuerStore = issuerStore;
mCertificateService = certificateService;
mBitcoinManager = bitcoinManager;
mIssuerManager = issuerManager;
Expand Down Expand Up @@ -149,11 +152,8 @@ private Observable<String> handleCertificateResponse(ResponseBody responseBody)
String certUid = blockCert.getCertUid();
FileUtils.saveCertificate(mContext, buffer, certUid);

return mIssuerManager.fetchIssuer(blockCert.getIssuerId()).map(issuer -> {
String recipientPublicKey = blockCert.getRecipientPublicKey();
mIssuerStore.saveIssuerResponse(issuer, recipientPublicKey);
return certUid;
});
saveIssuer(blockCert);
return Observable.just(certUid);
} catch (JsonSyntaxException e) {
Timber.w(e, "Certificate failed to parse");
return Observable.error(e);
Expand Down Expand Up @@ -210,15 +210,21 @@ private Observable<String> handleCertificateInputStream(InputStream certInputStr
String certUid = blockCert.getCertUid();
FileUtils.renameCertificateFile(mContext, tempFilename, certUid);

return mIssuerManager.fetchIssuer(blockCert.getIssuerId()).map(issuer -> {
String recipientPublicKey = blockCert.getRecipientPublicKey();
mIssuerStore.saveIssuerResponse(issuer, recipientPublicKey);
return certUid;
});
saveIssuer(blockCert);
return Observable.just(certUid);
}

private void saveIssuer(BlockCert blockCert) {
mIssuerManager.fetchAndSaveIssuerOf(blockCert)
.subscribe(issuerUuid -> updateBlockCertIssuerUuid(blockCert.getCertUid(), issuerUuid));
}

private void saveBlockCert(BlockCert blockCert) {
Timber.i("Saving certificate " + blockCert.getCertName());
mCertificateStore.saveBlockchainCertificate(blockCert);
}

private void updateBlockCertIssuerUuid (String blockCertUuid, String issuerUuid) {
mCertificateStore.updateIssuerUuid(blockCertUuid, issuerUuid);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import android.content.Context;

import com.learningmachine.android.app.LMConstants;
import com.learningmachine.android.app.data.cert.BlockCert;
import com.learningmachine.android.app.data.error.IssuerAnalyticsException;
import com.learningmachine.android.app.data.model.IssuerRecord;
import com.learningmachine.android.app.data.store.IssuerStore;
Expand Down Expand Up @@ -52,7 +54,20 @@ public Observable<List<IssuerRecord>> getIssuers() {
return Observable.just(mIssuerStore.loadIssuers());
}

public Observable<String> fetchAndSaveIssuerOf (BlockCert blockCert) {
return fetchIssuer(blockCert.getIssuerId()).<String>flatMap(issuer -> {
String recipientPublicKey = blockCert.getRecipientPublicKey();
return Observable.just(saveIssuer(issuer, recipientPublicKey));
});
}

public Observable<IssuerResponse> fetchIssuer(String url) {
if (StringUtils.isDid(url)) {
final String didUri = url;
final String didResolveUrl = LMConstants.DID_RESOLVER_URL + "/" + didUri;
return mIssuerService.getIssuerDID(didResolveUrl)
.flatMap(didDocument -> mIssuerService.getIssuer(didDocument.getIssuerProfileUrl()));
}
return mIssuerService.getIssuer(url);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ public interface BlockCert {
String getMetadata();
String getReceiptHash();
String getExpirationDate();
abstract String getDisplayHtml();
abstract String version();

/**
* @return The portion of the certificate whose hash needs to be compared against the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,21 @@
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.learningmachine.android.app.data.cert.v11.BlockCertV11;
import com.learningmachine.android.app.data.cert.v12.BlockCertV12;
import com.learningmachine.android.app.data.cert.v20.BlockCertV20;
import com.learningmachine.android.app.data.cert.v30.BlockCertV30;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.regex.*;

import timber.log.Timber;

public class BlockCertAdapter implements JsonSerializer<BlockCert>, JsonDeserializer<BlockCert> {
@Override
Expand All @@ -28,47 +34,95 @@ public JsonElement serialize(BlockCert src, Type typeOfSrc, JsonSerializationCon
public BlockCert deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
BlockCert blockCert = null;
JsonObject jsonObject = json.getAsJsonObject();
if (isV20(jsonObject)) {
blockCert = context.deserialize(json, BlockCertV20.class);
jsonObject.remove("signature");
blockCert.setDocumentNode(jsonObject);
} else if (isV12(jsonObject)) {
blockCert = context.deserialize(json, BlockCertV12.class);
blockCert.setDocumentNode(jsonObject.getAsJsonObject("document"));
} else if (isV11(jsonObject)) {
blockCert = context.deserialize(json, BlockCertV11.class);
blockCert = context.deserialize(json, getAdapterClass(jsonObject));
if (blockCert != null) {
blockCert.setDocumentNode(jsonObject);
}
return blockCert;
}

private boolean isV11(JsonObject jsonObject) {
if (jsonObject.get("certificate") != null
&& jsonObject.get("assertion") != null
&& jsonObject.get("verify") != null
&& jsonObject.get("recipient") != null
&& jsonObject.get("signature") != null
&& jsonObject.get("extension") != null) {
return true;
private Class<?> getAdapterClass (JsonObject jsonObject) {
final String version = getBlockcertsVersion(jsonObject);
switch (version) {
case "v1.1":
return BlockCertV11.class;

case "v1":
case "v1.2":
return BlockCertV12.class;

case "v2":
case "v2.0":
case "v2.1":
return BlockCertV20.class;

case "v3":
case "v3.0":
case "v3.1":
return BlockCertV30.class;

default:
Timber.e(String.format("Unrecognized blockcerts version: %s", version));
return null;
}
return false;
}

private boolean isV12(JsonObject jsonObject) {
if (jsonObject.get("@context") != null
&& jsonObject.get("type") != null
&& jsonObject.get("document") != null
&& jsonObject.get("receipt") != null) {
return true;
private String getBlockcertsVersion (JsonObject jsonObject) {
if (jsonObject.get("@context") == null && isV11(jsonObject)) {
return "v1.1";
}
return false;
ArrayList contextArray = new ArrayList<String>();
final JsonElement context = jsonObject.get("@context");
if (context.isJsonArray()) {
final JsonArray contextObjectAsArray = jsonObject.getAsJsonArray("@context");
getStringsFromContextArray(contextObjectAsArray, contextArray);
} else {
contextArray.add(context.toString());
}

final String blockcertsContextUrl = filterBlockcertsContext(contextArray);
if (blockcertsContextUrl != "") {
final String version = getVersionNumber(blockcertsContextUrl);
if (version.startsWith("v")) {
return version;
}
return "v" + version;
}
return "invalid blockcerts version";
}

private void getStringsFromContextArray (JsonArray array, ArrayList targetArray) {
for (int i = 0; i < array.size(); i++) {
if (!array.get(i).isJsonObject()) {
targetArray.add(array.get(i).toString());
}
}
}

private String filterBlockcertsContext(ArrayList<String> contextArray) {
Object[] result = contextArray.stream().filter(s -> s.contains("blockcerts")).toArray();
if (result.length != 0) {
return result[0].toString();
}
return "";
}

private boolean isV20(JsonObject jsonObject) {
if (jsonObject.get("type") != null
&& jsonObject.get("badge") != null
private String getVersionNumber (String blockcertsContextUrl) {
Pattern versionRegexPattern = Pattern.compile("/(v?\\d{1}.?\\d?\\b)");
Matcher matcher = versionRegexPattern.matcher(blockcertsContextUrl);
if (matcher.find()) {
return matcher.group(1);
}
return "";
}

private boolean isV11(JsonObject jsonObject) {
if (jsonObject.get("certificate") != null
&& jsonObject.get("assertion") != null
&& jsonObject.get("verify") != null
&& jsonObject.get("recipient") != null
&& jsonObject.get("signature") != null
&& jsonObject.get("recipient") != null) {
&& jsonObject.get("@context") == null) {
return true;
}
return false;
Expand Down
Loading