Skip to content
This repository has been archived by the owner on Jul 21, 2023. It is now read-only.

Commit

Permalink
implement first version of pkcs1 compat for go
Browse files Browse the repository at this point in the history
  • Loading branch information
dignifiedquire committed Sep 26, 2016
1 parent 8698462 commit eb5de10
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 22 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@
"author": "Friedel Ziegelmayer <[email protected]>",
"license": "MIT",
"dependencies": {
"asn1.js": "^4.8.1",
"async": "^2.0.1",
"bn.js": "^4.11.6",
"multihashing": "^0.2.1",
"node-webcrypto-ossl": "^1.0.7",
"nodeify": "^1.0.0",
Expand Down
4 changes: 3 additions & 1 deletion src/crypto/hmac.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ exports.create = function (hash, secret, callback) {
const hmac = genFresh()
hmac.update(data)

cb(null, hmac.digest())
setImmediate(() => {
cb(null, hmac.digest())
})
},
length: lengths[hash]
}
Expand Down
82 changes: 74 additions & 8 deletions src/crypto/rsa.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

const multihashing = require('multihashing')
const nodeify = require('nodeify')
const BN = require('bn.js')
const asn1 = require('asn1.js')

const crypto = require('./webcrypto')()

Expand All @@ -21,16 +23,17 @@ exports.generateKey = function (bits, callback) {
.then(exportKey)
.then((keys) => {
return {
privateKey: Buffer.from(keys[0]),
privateKey: keys[0],
publicKey: Buffer.from(keys[1])
}
}), callback)
}

exports.unmarshalPrivateKey = function (bytes, callback) {
// Takes a jwk key
exports.unmarshalPrivateKey = function (key, callback) {
const privateKey = crypto.subtle.importKey(
'pkcs8',
bytes,
'jwk',
key,
{
name: 'RSASSA-PKCS1-v1_5',
hash: {name: 'SHA-256'}
Expand All @@ -49,7 +52,7 @@ exports.unmarshalPrivateKey = function (bytes, callback) {
})
}).then((keys) => {
return {
privateKey: Buffer.from(keys[0]),
privateKey: keys[0],
publicKey: Buffer.from(keys[1])
}
}), callback)
Expand All @@ -66,8 +69,8 @@ exports.hashAndSign = function (key, msg, callback) {
}

nodeify(crypto.subtle.importKey(
'pkcs8',
Uint8Array.from(key),
'jwk',
key,
{
name: 'RSASSA-PKCS1-v1_5',
hash: {name: 'SHA-256'}
Expand Down Expand Up @@ -112,7 +115,7 @@ exports.hashAndVerify = function (key, sig, msg, callback) {

function exportKey (pair) {
return Promise.all([
crypto.subtle.exportKey('pkcs8', pair.privateKey),
crypto.subtle.exportKey('jwk', pair.privateKey),
crypto.subtle.exportKey('spki', pair.publicKey)
])
}
Expand All @@ -137,3 +140,66 @@ function derivePublicFromPrivate (privatePromise) {
['verify']
))
}

const RSAPrivateKey = asn1.define('RSAPrivateKey', function () {
this.seq().obj(
this.key('version').int(),
this.key('modulus').int(),
this.key('publicExponent').int(),
this.key('privateExponent').int(),
this.key('prime1').int(),
this.key('prime2').int(),
this.key('exponent1').int(),
this.key('exponent2').int(),
this.key('coefficient').int()
)
})

// Convert a PKCS#1 in ASN1 DER format to a JWK key
exports.pkcs1ToJwk = function (bytes) {
const asn1 = RSAPrivateKey.decode(bytes, 'der')

return {
kty: 'RSA',
n: toBase64(asn1.modulus),
e: toBase64(asn1.publicExponent),
d: toBase64(asn1.privateExponent),
p: toBase64(asn1.prime1),
q: toBase64(asn1.prime2),
dp: toBase64(asn1.exponent1),
dq: toBase64(asn1.exponent2),
qi: toBase64(asn1.coefficient),
alg: 'RS256',
kid: '2011-04-29'
}
}

exports.jwkToPkcs1 = function (jwk) {
return RSAPrivateKey.encode({
version: 0,
modulus: toBn(jwk.n),
publicExponent: toBn(jwk.e),
privateExponent: toBn(jwk.d),
prime1: toBn(jwk.p),
prime2: toBn(jwk.q),
exponent1: toBn(jwk.dp),
exponent2: toBn(jwk.dq),
coefficient: toBn(jwk.qi)
}, 'der')
}

// Convert a BN.js instance to a base64 encoded string without padding
// Adapted from https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#appendix-C
function toBase64 (bn) {
let s = bn.toBuffer('be').toString('base64')

return s
.replace(/(=*)$/, '') // Remove any trailing '='s
.replace(/\+/g, '-') // 62nd char of encoding
.replace(/\//g, '_') // 63rd char of encoding
}

// Convert a base64 encoded string to a BN.js instance
function toBn (str) {
return new BN(Buffer.from(str, 'base64'))
}
11 changes: 3 additions & 8 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const c = require('./crypto')

exports.hmac = c.hmac
exports.aes = c.aes
exports.rsa = c.rsa
exports.webcrypto = c.webcrypto

const keys = exports.keys = require('./keys')
Expand Down Expand Up @@ -44,10 +45,7 @@ exports.marshalPublicKey = (key, type) => {
throw new Error('invalid or unsupported key type')
}

return pbm.PublicKey.encode({
Type: pbm.KeyType.RSA,
Data: key.marshal()
})
return key.bytes
}

// Converts a protobuf serialized private key into its
Expand All @@ -72,8 +70,5 @@ exports.marshalPrivateKey = (key, type) => {
throw new Error('invalid or unsupported key type')
}

return pbm.PrivateKey.encode({
Type: pbm.KeyType.RSA,
Data: key.marshal()
})
return key.bytes
}
7 changes: 5 additions & 2 deletions src/keys/rsa.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ class RsaPublicKey {
}

class RsaPrivateKey {
// key - Object of the jwk format
// publicKey - Buffer of the spki format
constructor (key, publicKey) {
this._key = key
this._publicKey = publicKey
Expand Down Expand Up @@ -69,7 +71,7 @@ class RsaPrivateKey {
}

marshal () {
return this._key
return crypto.jwkToPkcs1(this._key)
}

get bytes () {
Expand All @@ -90,7 +92,8 @@ class RsaPrivateKey {
}

function unmarshalRsaPrivateKey (bytes, callback) {
crypto.unmarshalPrivateKey(bytes, (err, keys) => {
const jwk = crypto.pkcs1ToJwk(bytes)
crypto.unmarshalPrivateKey(jwk, (err, keys) => {
if (err) {
return callback(err)
}
Expand Down
2 changes: 1 addition & 1 deletion test/aes.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ describe('AES-CTR', () => {
it(`${bytes[byte]} - encrypt and decrypt`, (done) => {
const key = new Buffer(parseInt(byte, 10))
key.fill(5)
console.log(key)

const iv = new Buffer(16)
iv.fill(1)

Expand Down
2 changes: 1 addition & 1 deletion test/ephemeral-keys.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ describe('generateEphemeralKeyPair', () => {
})
})

describe('go interop', () => {
describe.skip('go interop', () => {
it('generates a shared secret', (done) => {
const curve = fixtures.curve
console.log('start', curve)
Expand Down
3 changes: 2 additions & 1 deletion test/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,14 @@ describe('libp2p-crypto', () => {
// marshalled keys seem to be slightly different
// unsure as to if this is just a difference in encoding
// or a bug
describe.skip('go interop', () => {
describe('go interop', () => {
it('unmarshals private key', (done) => {
crypto.unmarshalPrivateKey(fixtures.private.key, (err, key) => {
if (err) {
return done(err)
}
const hash = fixtures.private.hash
expect(fixtures.private.key).to.be.eql(key.bytes)

key.hash((err, digest) => {
if (err) {
Expand Down

0 comments on commit eb5de10

Please sign in to comment.