Skip to content

Commit

Permalink
Merge pull request #311 from TokenScript/OH_openssl_eas_base64url
Browse files Browse the repository at this point in the history
feat: 🎸 added option to read OpenSSL key, EAS base64->base64url
  • Loading branch information
oleggrib authored Aug 23, 2023
2 parents f692191 + ca06e14 commit 65a7fef
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 11 deletions.
10 changes: 10 additions & 0 deletions src/main/javascript/crypto/src/asn1/shemas/AttestationFramework.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ export class SubjectPublicKeyInfo {
@AsnProp({ type: AsnPropTypes.Any }) public null? = false;
}

export class PrivateKeyDataOpenSSL {
@AsnProp({ type: AsnPropTypes.Integer }) public one: number;
// @AsnProp({ type: AsnPropTypes.Any }) public algDescr2: Uint8Array;
@AsnProp({ type: AsnPropTypes.OctetString }) public privateKey: Uint8Array;
@AsnProp({ type: AsnPropTypes.ObjectIdentifier, context: 0 })
public algorithm: string;
@AsnProp({ type: AsnPropTypes.BitString, context: 1 })
public publicKey: Uint8Array;
}

export class PrivateKeyData {
@AsnProp({ type: AsnPropTypes.Integer }) public one: number;
// @AsnProp({ type: AsnPropTypes.Any }) public algDescr2: Uint8Array;
Expand Down
8 changes: 4 additions & 4 deletions src/main/javascript/crypto/src/eas/EasTicketAttestation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {BigNumber, ethers, Signer} from "ethers";
import {AttestationCrypto} from "../libs/AttestationCrypto";
import {AsnParser, AsnProp, AsnPropTypes, AsnSerializer} from "@peculiar/asn1-schema";
import {defaultAbiCoder, joinSignature} from "ethers/lib/utils";
import {hexStringToUint8, uint8tohex} from "../libs/utils";
import {base64UrltoBase64, base64toBase64Url, hexStringToUint8, uint8tohex} from "../libs/utils";
import {OffchainAttestationParams} from "@ethereum-attestation-service/eas-sdk/dist/offchain/offchain";
import {Attestable} from "../libs/Attestable";
import {AttestableObject} from "../libs/AttestableObject";
Expand Down Expand Up @@ -202,7 +202,7 @@ export class EasTicketAttestation extends AttestableObject implements Attestable


getEncoded(){
return zipAndEncodeToBase64(this.getEasJson());
return base64toBase64Url(zipAndEncodeToBase64(this.getEasJson()));
}

// TODO: Return ID based on decoded data
Expand Down Expand Up @@ -332,10 +332,10 @@ export class EasTicketAttestation extends AttestableObject implements Attestable
}

loadFromEncoded(
base64: string,
base64url: string,
keys?: KeysArray,
commitmentSecret?: string){
const decoded = decodeBase64ZippedBase64(base64);
const decoded = decodeBase64ZippedBase64(base64UrltoBase64(base64url));

this.loadEasAttestation(decoded.sig as SignedOffchainAttestation, keys, commitmentSecret)
}
Expand Down
135 changes: 135 additions & 0 deletions src/main/javascript/crypto/src/keyPair.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/**
* @jest-environment jsdom
*/

import {
bnToUint8,
hexStringToUint8,
stringToArray,
uint8arrayToBase64,
uint8tohex,
testsLogger, base64ToUint8array, hexStringToBase64
} from './libs/utils';
import {readFileSync} from "fs";
import {KeyPair} from "./libs/KeyPair";
import {Authenticator} from "./Authenticator";
import {Asn1Der} from "./libs/DerUtility";
import {DEBUGLEVEL} from "./config";


const querystring = require('querystring');

const PREFIX_PATH = '../../../../build/test-results/';

let useAttestRes: string,
sessionKey: KeyPair,
session2Key: KeyPair,
userKey: KeyPair,
userPubKey: KeyPair,
attestorPubKey: KeyPair,
attestorKey: KeyPair,
safeconnectKey: KeyPair,

senderKey: KeyPair,
senderPubKey: KeyPair,

sessionSignature: Uint8Array,
useAttestationJson: string,
attestationRequestJson: string,
requestAttestAndUsage: string,
magicLink: string,

magicLinkPublicPEM: string,
magicLinkPrivatePEM: string,

useRequestAttestationJson: string;
let sessionMessage = "message";
let email = "test@test.ts";
let type = "mail";
let WEB_DOMAIN = "http://wwww.hotelbogota.com";
let ATTESTOR_DOMAIN = "http://wwww.attestation.id";

describe("Utils tests", () => {
test('uint8tohex test', () => {
expect(uint8tohex(new Uint8Array([1,2]))).toBe("0102")
})
});

describe("Read keys and files", () => {

attestationRequestJson = readFileSync(PREFIX_PATH + 'attestation-request.json', 'utf8');
attestationRequestJson = attestationRequestJson.split(/\r?\n/).join('');

const userPrivPEM = readFileSync(PREFIX_PATH + 'user-priv.pem', 'utf8');
userKey = KeyPair.privateFromPEM(userPrivPEM);

const userPubPEM = readFileSync(PREFIX_PATH + 'user-pub.pem', 'utf8');
userPubKey = KeyPair.publicFromBase64orPEM(userPubPEM);

const senderPubPEM = readFileSync(PREFIX_PATH + 'sender-pub.pem', 'utf8');
senderPubKey = KeyPair.publicFromBase64orPEM(senderPubPEM);

const attestorPubPEM = readFileSync(PREFIX_PATH + 'attestor-pub.pem', 'utf8');
attestorPubKey = KeyPair.publicFromBase64orPEM(attestorPubPEM);

const attestorPrivPEM = readFileSync(PREFIX_PATH + 'attestor-priv.pem', 'utf8');
attestorKey = KeyPair.privateFromPEM(attestorPrivPEM);

const sessionPrivPEM = readFileSync(PREFIX_PATH + 'session-priv.pem', 'utf8');
sessionKey = KeyPair.privateFromPEM(sessionPrivPEM);

const senderPrivPEM = readFileSync(PREFIX_PATH + 'sender-priv.pem', 'utf8');
senderKey = KeyPair.privateFromPEM(senderPrivPEM);

const session2PrivPEM = readFileSync(PREFIX_PATH + 'session-priv2.pem', 'utf8');
session2Key = KeyPair.privateFromPEM(session2PrivPEM);

const safeconnectIssuerPubKeyPem = readFileSync(PREFIX_PATH + 'key-ec.txt', 'utf8');
safeconnectKey = KeyPair.publicFromBase64orPEM(safeconnectIssuerPubKeyPem);

useAttestationJson = readFileSync(PREFIX_PATH + 'use-attestation.json', 'utf8');

magicLink = readFileSync(PREFIX_PATH + 'mah@mah.com.url', 'utf8');

useRequestAttestationJson = readFileSync(PREFIX_PATH + 'use-and-request-attestation.json', 'utf8');

magicLink = readFileSync(PREFIX_PATH + 'mah_v2@mah.com.url', 'utf8');
magicLinkPrivatePEM = readFileSync('../../../../src/test/data/namedEcPrivKey.pem', 'utf8');
magicLinkPublicPEM = readFileSync('../../../../src/test/data/namedEcPubKey.pem', 'utf8');

test('Read keys test ok', () => {
expect(userPubKey.getPublicKeyAsHexStr()).toBe(userKey.getPublicKeyAsHexStr());
})
});

describe("read public key", () => {
// Standard PKCS stored key
const pubKeyPem1 = readFileSync(PREFIX_PATH + 'ticket-issuer-key.pem', 'utf8');
let addr1 = "0x94085A072E5481D64D6E2165268801B87A362B64";
// Pure base64 dump
const keyFile2 = 'MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEOt9mWLpQVOxiOvswFK4GGI0oOZ2GqS2Q6ec0AWIeuVoCuTD+atppPvjMgNLg9qQzJxsDW3zLxnOPFWO/Decnag==';
let addr2 = "0x17C0B3B51A75F1A001F255A7CAD4FA45529CAC20";


test('read 2 keys', async () => {
let key1 = Authenticator.decodePublicKey(pubKeyPem1);
expect(key1.getAddress().toLocaleLowerCase()).toBe(addr1.toLocaleLowerCase());

let key2 = Authenticator.decodePublicKey(keyFile2);
expect(key2.getAddress().toLocaleLowerCase()).toBe(addr2.toLocaleLowerCase());
})
})

describe("read private key", () => {

let opensslKey = "MHQCAQEEIF4H530+T2FFQjMI0hkaMzR5DfotHzXbhAcq+OxjcM5WoAcGBSuBBAAKoUQDQgAEYOCWsqPOp9lirOB9xIO5zIepnAoIgPfWwRG06XonF8+BdHrPMqe1DzsEwsuIDpVxB9fEpZVbVPkPurFYDk8KAQ=="
// Standard PKCS stored key

test('read key', async () => {
let key = KeyPair.privateFromPEM(opensslKey)
expect(key.getAddress().toLowerCase()).toBe("0xCE88748AEDF95313D96559AB39254F332DFE8F9C".toLowerCase())
})


})

25 changes: 20 additions & 5 deletions src/main/javascript/crypto/src/libs/KeyPair.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {AsnParser} from "@peculiar/asn1-schema";
import {
PrivateKeyData,
PrivateKeyInfo, PublicKeyInfoValue,
SubjectPublicKeyInfo
SubjectPublicKeyInfo, PrivateKeyDataOpenSSL
} from "../asn1/shemas/AttestationFramework";
import {ethers} from "ethers";
import {Signature} from "../asn1/shemas/Signature";
Expand Down Expand Up @@ -206,11 +206,26 @@ export class KeyPair {

static privateFromPEM(pem: string): KeyPair {
const receiverPrivUint8 = base64ToUint8array(pem);
let privateKeyObj: PrivateKeyInfo = AsnParser.parse(receiverPrivUint8, PrivateKeyInfo);
return KeyPair.privateFromKeyInfo(privateKeyObj);
}
try {
let privateKeyObj: PrivateKeyInfo = AsnParser.parse(receiverPrivUint8, PrivateKeyInfo);
return KeyPair.privateFromKeyInfo(privateKeyObj);
} catch(e){
// try to decode OpenSSL format
}
let privateKeyObj: PrivateKeyDataOpenSSL = AsnParser.parse(receiverPrivUint8, PrivateKeyDataOpenSSL);

let me = new this();

if (privateKeyObj.algorithm === "1.3.132.0.10") {
me.algorithm = "secp256k1"
} else {
throw new Error(`Unknown algorithm "${privateKeyObj.algorithm}"`)
}

me.privKey = new Uint8Array(privateKeyObj.privateKey);
return me;

}

// Generate a private key
static async generateKeyAsync(): Promise<KeyPair> {
Expand Down Expand Up @@ -298,7 +313,7 @@ export class KeyPair {

getAddress(): string {
var pubPoint = this.getPublicKeyAsHexStr();
pubPoint = pubPoint.substr(2);
pubPoint = pubPoint.substring(2);
let hash = sha3.keccak256(hexStringToArray(pubPoint));
return "0x" + hash.substr(-40).toUpperCase();
}
Expand Down
10 changes: 8 additions & 2 deletions src/main/javascript/crypto/src/libs/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,14 @@ export function uint8arrayToBase64( bytes: Uint8Array ): string {

export function base64toBase64Url(base64: string): string {
return base64.split('/').join('_')
.split('+').join('-');
// .split('=').join('.');
.split('+').join('-')
.split('=').join('.');
}

export function base64UrltoBase64(base64Url: string): string {
return base64Url.split('_').join('/')
.split('-').join('+')
.split('.').join('=');
}

export function pemOrBase64Orbase64urlToString(base64str: string): string {
Expand Down

1 comment on commit 65a7fef

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage report for src/main/javascript/crypto/

❌ An unexpected error occurred. For more details, check console

Error: The process '/opt/hostedtoolcache/node/16.20.2/x64/bin/npm' failed with exit code 1
St.
Category Percentage Covered / Total
🟡 Statements 76.27% 2455/3219
🔴 Branches 48.8% 346/709
🟢 Functions 82.35% 434/527
🟡 Lines 76.48% 2413/3155

Test suite run failed

Failed tests: 3/70. Failed suites: 1/3.
  ● UnpredictableNumberToolTest › validate legacy Java UN - mac

    assert.strictEqual(received, expected)

    Expected value to strictly be equal to:
      true
    Received:
      false

    Message:
      expected false to be true

      116 |     const unt: IUnpredictableNumberTool = UnpredictableNumberToolTest.createUnt("mac");
      117 |     let randomness = new Uint8Array([117, -106, -9, 48, 71, 18, -58, 36, -121, 69, 93, 120, -100, 100, -108, 104, -5, 67, 73, -36, -121, 79, -128, -128, -59, -119, -2, -86, -126, -36, 74, 117]);
    > 118 |     expect(unt.validateUnpredictableNumber("ABJ34us29mc=", randomness, BigInt(1977060911693))).true;
          |                                                                                               ^
      119 |   }
      120 |
      121 |   @test 'validate Java UN - mac'() {

      at UnpredictableNumberToolTest.validate legacy Java UN - mac (src/libs/UnpredictableNumberTool.test.ts:118:95)
      at Object.validate legacy Java UN - mac (node_modules/@testdeck/core/index.ts:180:27)

  ● UnpredictableNumberToolTest › validate Java UN - mac

    assert.strictEqual(received, expected)

    Expected value to strictly be equal to:
      true
    Received:
      false

    Message:
      expected false to be true

      122 |     const unt: IUnpredictableNumberTool = UnpredictableNumberToolTest.createUnt("mac");
      123 |     let randomness = new Uint8Array([28, -66, -9, -85, 78, -90, -64, -121, -111, 14, 93, -46, 65, -27, 64, 43, 75, -104, -121, -64, -67, -16, 4, -96, 66, -93, 99, 69, -89, -97, 39, -67]);
    > 124 |     expect(unt.validateUnpredictableNumber("llzLl_elSTv_64uII9FZGg==", randomness, BigInt(33198034831702))).true;
          |                                                                                                            ^
      125 |   }
      126 |
      127 |   @test 'validate Java UN - sig'() {

      at UnpredictableNumberToolTest.validate Java UN - mac (src/libs/UnpredictableNumberTool.test.ts:124:108)
      at Object.validate Java UN - mac (node_modules/@testdeck/core/index.ts:180:27)

  ● UnpredictableNumberToolTest › validate Java UN context - mac

    assert.strictEqual(received, expected)

    Expected value to strictly be equal to:
      true
    Received:
      false

    Message:
      expected false to be true

      134 |     const unt: IUnpredictableNumberTool = UnpredictableNumberToolTest.createUnt("mac");
      135 |     let randomness = new Uint8Array([28, -66, -9, -85, 78, -90, -64, -121, -111, 14, 93, -46, 65, -27, 64, 43, 75, -104, -121, -64, -67, -16, 4, -96, 66, -93, 99, 69, -89, -97, 39, -67]);
    > 136 |     expect(unt.validateUnpredictableNumber("mMfnOhRXj5iNz6_L-n3cOw==", randomness, BigInt(33198026955780), new Uint8Array([42]))).true;
          |                                                                                                                                  ^
      137 |   }
      138 |
      139 |   @test 'validate Java UN context - sig'() {

      at UnpredictableNumberToolTest.validate Java UN context - mac (src/libs/UnpredictableNumberTool.test.ts:136:130)
      at Object.validate Java UN context - mac (node_modules/@testdeck/core/index.ts:180:27)

Report generated by 🧪jest coverage report action from 65a7fef

Please sign in to comment.