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

Commit eb5de10

Browse files
implement first version of pkcs1 compat for go
1 parent 8698462 commit eb5de10

File tree

8 files changed

+91
-22
lines changed

8 files changed

+91
-22
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@
3434
"author": "Friedel Ziegelmayer <[email protected]>",
3535
"license": "MIT",
3636
"dependencies": {
37+
"asn1.js": "^4.8.1",
3738
"async": "^2.0.1",
39+
"bn.js": "^4.11.6",
3840
"multihashing": "^0.2.1",
3941
"node-webcrypto-ossl": "^1.0.7",
4042
"nodeify": "^1.0.0",

src/crypto/hmac.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ exports.create = function (hash, secret, callback) {
1010
const hmac = genFresh()
1111
hmac.update(data)
1212

13-
cb(null, hmac.digest())
13+
setImmediate(() => {
14+
cb(null, hmac.digest())
15+
})
1416
},
1517
length: lengths[hash]
1618
}

src/crypto/rsa.js

Lines changed: 74 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
const multihashing = require('multihashing')
44
const nodeify = require('nodeify')
5+
const BN = require('bn.js')
6+
const asn1 = require('asn1.js')
57

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

@@ -21,16 +23,17 @@ exports.generateKey = function (bits, callback) {
2123
.then(exportKey)
2224
.then((keys) => {
2325
return {
24-
privateKey: Buffer.from(keys[0]),
26+
privateKey: keys[0],
2527
publicKey: Buffer.from(keys[1])
2628
}
2729
}), callback)
2830
}
2931

30-
exports.unmarshalPrivateKey = function (bytes, callback) {
32+
// Takes a jwk key
33+
exports.unmarshalPrivateKey = function (key, callback) {
3134
const privateKey = crypto.subtle.importKey(
32-
'pkcs8',
33-
bytes,
35+
'jwk',
36+
key,
3437
{
3538
name: 'RSASSA-PKCS1-v1_5',
3639
hash: {name: 'SHA-256'}
@@ -49,7 +52,7 @@ exports.unmarshalPrivateKey = function (bytes, callback) {
4952
})
5053
}).then((keys) => {
5154
return {
52-
privateKey: Buffer.from(keys[0]),
55+
privateKey: keys[0],
5356
publicKey: Buffer.from(keys[1])
5457
}
5558
}), callback)
@@ -66,8 +69,8 @@ exports.hashAndSign = function (key, msg, callback) {
6669
}
6770

6871
nodeify(crypto.subtle.importKey(
69-
'pkcs8',
70-
Uint8Array.from(key),
72+
'jwk',
73+
key,
7174
{
7275
name: 'RSASSA-PKCS1-v1_5',
7376
hash: {name: 'SHA-256'}
@@ -112,7 +115,7 @@ exports.hashAndVerify = function (key, sig, msg, callback) {
112115

113116
function exportKey (pair) {
114117
return Promise.all([
115-
crypto.subtle.exportKey('pkcs8', pair.privateKey),
118+
crypto.subtle.exportKey('jwk', pair.privateKey),
116119
crypto.subtle.exportKey('spki', pair.publicKey)
117120
])
118121
}
@@ -137,3 +140,66 @@ function derivePublicFromPrivate (privatePromise) {
137140
['verify']
138141
))
139142
}
143+
144+
const RSAPrivateKey = asn1.define('RSAPrivateKey', function () {
145+
this.seq().obj(
146+
this.key('version').int(),
147+
this.key('modulus').int(),
148+
this.key('publicExponent').int(),
149+
this.key('privateExponent').int(),
150+
this.key('prime1').int(),
151+
this.key('prime2').int(),
152+
this.key('exponent1').int(),
153+
this.key('exponent2').int(),
154+
this.key('coefficient').int()
155+
)
156+
})
157+
158+
// Convert a PKCS#1 in ASN1 DER format to a JWK key
159+
exports.pkcs1ToJwk = function (bytes) {
160+
const asn1 = RSAPrivateKey.decode(bytes, 'der')
161+
162+
return {
163+
kty: 'RSA',
164+
n: toBase64(asn1.modulus),
165+
e: toBase64(asn1.publicExponent),
166+
d: toBase64(asn1.privateExponent),
167+
p: toBase64(asn1.prime1),
168+
q: toBase64(asn1.prime2),
169+
dp: toBase64(asn1.exponent1),
170+
dq: toBase64(asn1.exponent2),
171+
qi: toBase64(asn1.coefficient),
172+
alg: 'RS256',
173+
kid: '2011-04-29'
174+
}
175+
}
176+
177+
exports.jwkToPkcs1 = function (jwk) {
178+
return RSAPrivateKey.encode({
179+
version: 0,
180+
modulus: toBn(jwk.n),
181+
publicExponent: toBn(jwk.e),
182+
privateExponent: toBn(jwk.d),
183+
prime1: toBn(jwk.p),
184+
prime2: toBn(jwk.q),
185+
exponent1: toBn(jwk.dp),
186+
exponent2: toBn(jwk.dq),
187+
coefficient: toBn(jwk.qi)
188+
}, 'der')
189+
}
190+
191+
// Convert a BN.js instance to a base64 encoded string without padding
192+
// Adapted from https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#appendix-C
193+
function toBase64 (bn) {
194+
let s = bn.toBuffer('be').toString('base64')
195+
196+
return s
197+
.replace(/(=*)$/, '') // Remove any trailing '='s
198+
.replace(/\+/g, '-') // 62nd char of encoding
199+
.replace(/\//g, '_') // 63rd char of encoding
200+
}
201+
202+
// Convert a base64 encoded string to a BN.js instance
203+
function toBn (str) {
204+
return new BN(Buffer.from(str, 'base64'))
205+
}

src/index.js

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const c = require('./crypto')
66

77
exports.hmac = c.hmac
88
exports.aes = c.aes
9+
exports.rsa = c.rsa
910
exports.webcrypto = c.webcrypto
1011

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

47-
return pbm.PublicKey.encode({
48-
Type: pbm.KeyType.RSA,
49-
Data: key.marshal()
50-
})
48+
return key.bytes
5149
}
5250

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

75-
return pbm.PrivateKey.encode({
76-
Type: pbm.KeyType.RSA,
77-
Data: key.marshal()
78-
})
73+
return key.bytes
7974
}

src/keys/rsa.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ class RsaPublicKey {
4242
}
4343

4444
class RsaPrivateKey {
45+
// key - Object of the jwk format
46+
// publicKey - Buffer of the spki format
4547
constructor (key, publicKey) {
4648
this._key = key
4749
this._publicKey = publicKey
@@ -69,7 +71,7 @@ class RsaPrivateKey {
6971
}
7072

7173
marshal () {
72-
return this._key
74+
return crypto.jwkToPkcs1(this._key)
7375
}
7476

7577
get bytes () {
@@ -90,7 +92,8 @@ class RsaPrivateKey {
9092
}
9193

9294
function unmarshalRsaPrivateKey (bytes, callback) {
93-
crypto.unmarshalPrivateKey(bytes, (err, keys) => {
95+
const jwk = crypto.pkcs1ToJwk(bytes)
96+
crypto.unmarshalPrivateKey(jwk, (err, keys) => {
9497
if (err) {
9598
return callback(err)
9699
}

test/aes.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ describe('AES-CTR', () => {
1515
it(`${bytes[byte]} - encrypt and decrypt`, (done) => {
1616
const key = new Buffer(parseInt(byte, 10))
1717
key.fill(5)
18-
console.log(key)
18+
1919
const iv = new Buffer(16)
2020
iv.fill(1)
2121

test/ephemeral-keys.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ describe('generateEphemeralKeyPair', () => {
4141
})
4242
})
4343

44-
describe('go interop', () => {
44+
describe.skip('go interop', () => {
4545
it('generates a shared secret', (done) => {
4646
const curve = fixtures.curve
4747
console.log('start', curve)

test/index.spec.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,14 @@ describe('libp2p-crypto', () => {
3838
// marshalled keys seem to be slightly different
3939
// unsure as to if this is just a difference in encoding
4040
// or a bug
41-
describe.skip('go interop', () => {
41+
describe('go interop', () => {
4242
it('unmarshals private key', (done) => {
4343
crypto.unmarshalPrivateKey(fixtures.private.key, (err, key) => {
4444
if (err) {
4545
return done(err)
4646
}
4747
const hash = fixtures.private.hash
48+
expect(fixtures.private.key).to.be.eql(key.bytes)
4849

4950
key.hash((err, digest) => {
5051
if (err) {

0 commit comments

Comments
 (0)