Skip to content
This repository was archived by the owner on May 13, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<groupId>com.navnorth.learningregistry</groupId>
<artifactId>LRJavaLib</artifactId>
<packaging>jar</packaging>
<version>0.1.2</version>
<version>0.1.3-BKS</version>
<name>LRJavaLib</name>
<url>http://github.com/navnorth/LRJavaLib</url>

Expand Down
52 changes: 19 additions & 33 deletions src/com/navnorth/learningregistry/LREnvelope.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@

import com.navnorth.learningregistry.util.MapUtil;

import java.util.Map;
import java.util.Map;
import java.util.HashMap;
import java.util.LinkedHashMap;

/**
* Envelope for data to export to a learning registry node
Expand All @@ -34,9 +34,8 @@
*/
public abstract class LREnvelope
{
private static final String docVersion = "0.23.0";
private static final String docVersion = "0.49.0";
private static final String docType = "resource_data";
private static final String active = "true";

private static final String docTypeField = "doc_type";
private static final String docVersionField = "doc_version";
Expand All @@ -57,6 +56,7 @@ public abstract class LREnvelope
private static final String payloadSchemaLocatorField = "payload_schema_locator";
private static final String keysField = "keys";
private static final String resourceDataField = "resource_data";
private static final String replacesField = "replaces";

private static final String keyLocationField = "key_location";
private static final String signingMethodField = "signing_method";
Expand All @@ -65,10 +65,14 @@ public abstract class LREnvelope

private static final String submitterTTLField = "submitter_TTL";

private static final String[] excludedFields = {"digital_signature", "publishing_node", "update_timestamp", "node_timestamp", "create_timestamp", "doc_ID", "_id", "_rev"};

protected String resourceLocator;
protected String resourceDataType;
protected Object resourceData;

protected String[] replaces;

protected String payloadPlacement;
protected String payloadSchemaLocator;
protected String[] payloadSchema;
Expand Down Expand Up @@ -99,31 +103,12 @@ public abstract class LREnvelope
*/
protected Map<String, Object> getSignableData()
{
Map<String, Object> doc = new HashMap<String, Object>();

MapUtil.put(doc, docTypeField, docType);
MapUtil.put(doc, docVersionField, docVersion);
MapUtil.put(doc, activeField, active);
MapUtil.put(doc, resourceDataTypeField, resourceDataType);
Map<String, Object> docId = new HashMap<String, Object>();
MapUtil.put(docId, submitterTypeField, submitterType);
MapUtil.put(docId, submitterField, submitter);
MapUtil.put(docId, curatorField, curator);
MapUtil.put(docId, ownerField, owner);
MapUtil.put(docId, signerField, signer);
MapUtil.put(doc, identityField, docId);
MapUtil.put(doc, submitterTTLField, submitterTTL);
Map<String, Object> docTOS = new HashMap<String, Object>();
MapUtil.put(docTOS, submissionTOSField, submissionTOS);
MapUtil.put(docTOS, submissionAttributionField, submissionAttribution);
MapUtil.put(doc, TOSField, docTOS);
MapUtil.put(doc, resourceLocatorField, resourceLocator);
MapUtil.put(doc, payloadPlacementField, payloadPlacement);
MapUtil.put(doc, payloadSchemaField, payloadSchema);
MapUtil.put(doc, payloadSchemaLocatorField, payloadSchemaLocator);
MapUtil.put(doc, keysField, tags);
MapUtil.put(doc, resourceDataField, getResourceData());

final Map<String, Object> doc = getSendableData();

// remove node-specific data
for (int i = 0; i < excludedFields.length; i++) {
doc.remove(excludedFields[i]);
}
return doc;
}

Expand All @@ -134,7 +119,7 @@ protected Map<String, Object> getSignableData()
*/
protected Map<String, Object> getSendableData()
{
Map<String, Object> doc = new HashMap<String, Object>();
Map<String, Object> doc = new LinkedHashMap<String, Object>();

MapUtil.put(doc, docTypeField, docType);
MapUtil.put(doc, docVersionField, docVersion);
Expand All @@ -158,15 +143,16 @@ protected Map<String, Object> getSendableData()
MapUtil.put(doc, payloadSchemaLocatorField, payloadSchemaLocator);
MapUtil.put(doc, keysField, tags);
MapUtil.put(doc, resourceDataField, getResourceData());
MapUtil.put(doc, replacesField, replaces);

if (signed)
{
Map<String, Object> sig = new HashMap<String, Object>();
String[] keys = {publicKeyLocation};
MapUtil.put(sig, "key_location", keys);
MapUtil.put(sig, "signing_method", signingMethod);
MapUtil.put(sig, "signature", clearSignedMessage);
MapUtil.put(doc, "digital_signature", sig);
MapUtil.put(sig, keyLocationField, keys);
MapUtil.put(sig, signingMethodField, signingMethod);
MapUtil.put(sig, signatureField, clearSignedMessage);
MapUtil.put(doc, digitalSignatureField, sig);
}

return doc;
Expand Down
105 changes: 64 additions & 41 deletions src/com/navnorth/learningregistry/LRJSONDocument.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
public class LRJSONDocument extends LREnvelope
{
/**
* Create a new simple documet with specified details
* Create a new simple document with specified details
*
* @param resourceData value for "resource_data"
* @param resourceDataType value for "resource_data_type"
Expand All @@ -57,32 +57,13 @@ public LRJSONDocument(JSONObject resourceData, String resourceDataType, String r
String payloadPlacement, String payloadSchemaLocator, String[] payloadSchema,
String submitter, String submitterType, String submissionTOS, String submissionAttribution, String signer) throws LRException
{
try
{
this.resourceData = new ObjectMapper().readValue(resourceData.toString(), HashMap.class);
}
catch (IOException e)
{
throw new LRException(LRException.INVALID_JSON);
}

this.resourceDataType = StringUtil.nullifyBadInput(resourceDataType);
this.resourceLocator = StringUtil.nullifyBadInput(resourceLocator);
this.curator = StringUtil.nullifyBadInput(curator);
this.owner = StringUtil.nullifyBadInput(owner);
this.tags = StringUtil.removeDuplicates(tags);
this.payloadPlacement = StringUtil.nullifyBadInput(payloadPlacement);
this.payloadSchemaLocator = StringUtil.nullifyBadInput(payloadSchemaLocator);
this.payloadSchema = StringUtil.nullifyBadInput(payloadSchema);
this.submissionTOS = StringUtil.nullifyBadInput(submissionTOS);
this.submissionAttribution = StringUtil.nullifyBadInput(submissionAttribution);
this.submitterType = StringUtil.nullifyBadInput(submitterType);
this.submitter = StringUtil.nullifyBadInput(submitter);
this.signer = StringUtil.nullifyBadInput(signer);
initProperties(this, resourceData, resourceDataType, resourceLocator, curator, owner, tags, payloadPlacement,
payloadSchemaLocator, payloadSchema, submitter, submitterType, submissionTOS,
submissionAttribution, signer, null);
}

/**
* Create a new simple documet with specified details
* Create a new simple document with specified details
*
* @param resourceData value for "resource_data"
* @param resourceDataType value for "resource_data_type"
Expand All @@ -106,30 +87,72 @@ public LRJSONDocument(String resourceData, String resourceDataType, String resou
try
{
JSONObject resourceJSON = new JSONObject(resourceData);
this.resourceData = new ObjectMapper().readValue(resourceJSON.toString(), HashMap.class);
initProperties(this, resourceJSON, resourceDataType, resourceLocator, curator, owner, tags, payloadPlacement,
payloadSchemaLocator, payloadSchema, submitter, submitterType, submissionTOS,
submissionAttribution, signer, null);
}
catch (IOException e)
catch (JSONException e)
{
throw new LRException(LRException.INVALID_JSON);
}
catch (JSONException e)
}

/**
* Create a new simple document with specified details
*
* @param resourceData value for "resource_data"
* @param resourceDataType value for "resource_data_type"
* @param resourceLocator value for "resource_locator"
* @param curator value for "curator"
* @param owner value for "owner"
* @param tags value for "keys"
* @param payloadPlacement value for "payload_placement"
* @param payloadSchemaLocator value for "payload_schema_locator"
* @param payloadSchema value for "payload_schema"
* @param submitter value for "submitter"
* @param submitterType value for "submitter_type"
* @param submissionTOS value for "submission_TOS"
* @param submissionAttribution value for "submission_attribution"
* @param signer value for "signer"
* @param replaces array of document IDs to be replaced by this document
*/
public LRJSONDocument(JSONObject resourceData, String resourceDataType, String resourceLocator, String curator, String owner, String[] tags,
String payloadPlacement, String payloadSchemaLocator, String[] payloadSchema,
String submitter, String submitterType, String submissionTOS, String submissionAttribution, String signer,
String[] replaces) throws LRException
{
initProperties(this, resourceData, resourceDataType, resourceLocator, curator, owner, tags, payloadPlacement,
payloadSchemaLocator, payloadSchema, submitter, submitterType, submissionTOS,
submissionAttribution, signer, replaces);
}

protected void initProperties(LRJSONDocument document, JSONObject resourceData, String resourceDataType, String resourceLocator, String curator, String owner, String[] tags,
String payloadPlacement, String payloadSchemaLocator, String[] payloadSchema,
String submitter, String submitterType, String submissionTOS, String submissionAttribution, String signer,
String[] replaces) throws LRException
{
try
{
document.resourceData = new ObjectMapper().readValue(resourceData.toString(), HashMap.class);
document.resourceDataType = StringUtil.nullifyBadInput(resourceDataType);
document.resourceLocator = StringUtil.nullifyBadInput(resourceLocator);
document.curator = StringUtil.nullifyBadInput(curator);
document.owner = StringUtil.nullifyBadInput(owner);
document.tags = StringUtil.removeDuplicates(tags);
document.payloadPlacement = StringUtil.nullifyBadInput(payloadPlacement);
document.payloadSchemaLocator = StringUtil.nullifyBadInput(payloadSchemaLocator);
document.payloadSchema = StringUtil.nullifyBadInput(payloadSchema);
document.submissionTOS = StringUtil.nullifyBadInput(submissionTOS);
document.submissionAttribution = StringUtil.nullifyBadInput(submissionAttribution);
document.submitterType = StringUtil.nullifyBadInput(submitterType);
document.submitter = StringUtil.nullifyBadInput(submitter);
document.signer = StringUtil.nullifyBadInput(signer);
document.replaces = StringUtil.removeDuplicates(replaces);
}
catch (IOException e)
{
throw new LRException(LRException.INVALID_JSON);
}

this.resourceDataType = StringUtil.nullifyBadInput(resourceDataType);
this.resourceLocator = StringUtil.nullifyBadInput(resourceLocator);
this.curator = StringUtil.nullifyBadInput(curator);
this.owner = StringUtil.nullifyBadInput(owner);
this.tags = StringUtil.removeDuplicates(tags);
this.payloadPlacement = StringUtil.nullifyBadInput(payloadPlacement);
this.payloadSchemaLocator = StringUtil.nullifyBadInput(payloadSchemaLocator);
this.payloadSchema = StringUtil.nullifyBadInput(payloadSchema);
this.submissionTOS = StringUtil.nullifyBadInput(submissionTOS);
this.submissionAttribution = StringUtil.nullifyBadInput(submissionAttribution);
this.submitterType = StringUtil.nullifyBadInput(submitterType);
this.submitter = StringUtil.nullifyBadInput(submitter);
this.signer = StringUtil.nullifyBadInput(signer);
}

public Object getResourceData()
Expand Down
71 changes: 64 additions & 7 deletions src/com/navnorth/learningregistry/LRSigner.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@

import com.navnorth.learningregistry.util.StringUtil;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import java.io.InputStream;
Expand All @@ -25,11 +28,7 @@
import java.io.File;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.Security;
import java.security.SignatureException;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.bcpg.ArmoredOutputStream;
Expand Down Expand Up @@ -57,6 +56,7 @@ public class LRSigner
{
private static final String pgpRegex = "(?s).*-----BEGIN PGP PRIVATE KEY BLOCK-----.*-----END PGP PRIVATE KEY BLOCK-----.*";
private static final String signingMethod = "LR-PGP.1.0";
private static final String nullLiteral = "null";

private String publicKeyLocation;
private String privateKey;
Expand Down Expand Up @@ -122,6 +122,61 @@ public LRActivity sign(LRActivity envelope) throws LRException
return envelope;
}

/**
* Normalizes document as LRSignature Python module does
* - nulls converted to string literal "null"
* - booleans converted to string literals "true" or "false"
* - numeric values in lists are dropped
* - nested maps/JSON documents are normalized
* @param doc Document to normalize
*/
private Map<String, Object> normalizeMap(Map<String, Object> doc) {

final Map<String, Object> result = new LinkedHashMap<String, Object>();

for (String key : doc.keySet()) {
Object value = doc.get(key);

if (value == null) {
result.put(key, nullLiteral);
} else if (value instanceof Boolean) {
result.put(key, ((Boolean) value).toString());
} else if (value instanceof List<?>) {
result.put(key, normalizeList((List<Object>) value));
} else if (value instanceof Map<?, ?>) {
result.put(key, normalizeMap((Map<String, Object>) value));
} else {
result.put(key, value);
}
}

return result;
}

/**
* Helper for map normalization; inspects list and returns
* a replacement list that has been normalized.
* @param list
* @return Normalized list for encoding/hashing/signing
*/
private List<Object> normalizeList(List<Object> list) {
List<Object> result = new ArrayList<Object>();
for (Object o : list) {
if (o == null) {
result.add(nullLiteral);
} else if (o instanceof Boolean) {
result.add(((Boolean) o).toString());
} else if (o instanceof List<?>) {
result.add(normalizeList((List<Object>) o));
} else if (o instanceof Map<?, ?>) {
result.add(normalizeMap((Map<String, Object>) o));
} else if (!(o instanceof Number)) {
result.add(o);
}
}
return result;
}


/**
* Bencodes document
Expand All @@ -130,18 +185,20 @@ public LRActivity sign(LRActivity envelope) throws LRException
* @return Bencoded string of the provided document
* @throws LRException BENCODE_FAILED if document cannot be bencoded
*/
private String bencode(Map<String, Object> doc) throws LRException
private String bencode(final Map<String, Object> doc) throws LRException
{
String text = "";
String encodedString = "";

// Bencode the provided document
// normalize the document
final Map<String, Object> normalizedDoc = normalizeMap(doc);

// Bencode the normalized document
try
{
ByteArrayOutputStream s = new ByteArrayOutputStream();
BencodingOutputStream bencoder = new BencodingOutputStream(s);
bencoder.writeMap(doc);
bencoder.writeMap(normalizedDoc);
bencoder.flush();
encodedString = s.toString();
s.close();
Expand Down