Skip to content

Commit

Permalink
Implement unpackFriendshipCertificate
Browse files Browse the repository at this point in the history
Signed-off-by: Jack Works <[email protected]>
  • Loading branch information
Jack-Works committed May 14, 2019
1 parent 626be8b commit 73c915f
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 50 deletions.
17 changes: 5 additions & 12 deletions src/protocols/friendship-discovery/friendship-cert.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
/**
* Server guide:
* - On receive `signed`:
* 1. If `signed.cryptoKey` is used before, drop it.
* 2. Set `signed.timestamp` to current time.
* - On receive `packed`:
* 1. If `packed.cryptoKey` is used before, drop it.
* 2. Set `packed.timestamp` to current time.
* 3. Drop this object after a period of time. e.g. 31days
*/
/**
* Verify Steps:
* For each `signed` ({@link FriendshipCertificateSignedV1}):
* 1. Derive an AES key by `signed.cryptoKey` and your own key, let it be `aes`
* 2. Decrypt `signed.payload`, if failed, drop it; else, let it be `cert`
* 3. Manual or automatically verify friendship of `cert.myId` on network `cert.network`
*/
/**
* @remarks
* ! This object should be encrypted with a NEW RANDOM crypto key !
Expand All @@ -31,15 +24,15 @@ export interface FriendshipCertificateV1 {
*/
myId: string
}
export interface FriendshipCertificateSignedV1 {
export interface FriendshipCertificatePackedV1 {
version: 1
/**
* ! A NEW RANDOM crypto key !
* ! If a server receive a cert with a cryptoKey previously received, reject it !
*/
cryptoKey: JsonWebKey
/**
* This is encrypted {@link FriendshipCertificate} by {@link FriendshipCertificateSigned.cryptoKey}
* This is encrypted {@link FriendshipCertificate} by {@link FriendshipCertificatePacked.cryptoKey}
*/
payload: string
/**
Expand Down
75 changes: 75 additions & 0 deletions src/protocols/friendship-discovery/friendship-pack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { FriendshipCertificateV1, FriendshipCertificatePackedV1 } from './friendship-cert'
import { encryptWithAES, decryptWithAES } from '../../crypto/crypto-alpha-40'
import { encodeArrayBuffer, decodeText } from '../../utils/type-transform/EncodeDecode'
import { toECDH } from '../../utils/type-transform/CryptoUtils'
/**
* Pack steps for {@link FriendshipCertificateV1}
* @remakrs
* Pack Steps:
* 1. let `cert` = New {@link FriendshipCertificateV1}
* 2. let `key` = A new random CryptoKey. algorithm = 'ECDH', curve: 'K-256'
* 3. Derive an AES key by key of `cert target` and `key`, let it be `aes`
* 4. Encrypt `cert` with `aes`, let it be `payload`, store the `iv`
* 5. let `packed` = @{link FriendshipCertificatePackedV1} (version: `1`, payload: `payload`, cryptoKey: `key`, iv: `iv`)
* 6. Send `packed` to server
*/
export async function packFriendshipCertificate(
cert: FriendshipCertificateV1,
targetKey: CryptoKey,
): Promise<FriendshipCertificatePackedV1> {
const key = await crypto.subtle.generateKey({ name: 'ECDH', namedCurve: 'K-256' }, true, ['deriveKey'])
const aes = await crypto.subtle.deriveKey(
{ name: 'ECDH', public: targetKey },
key.privateKey,
{ name: 'AES-GCM', length: 256 },
false,
['encrypt', 'decrypt'],
)
const { content: payload, iv } = await encryptWithAES({
aesKey: aes,
content: JSON.stringify(cert),
})
return {
cryptoKey: await crypto.subtle.exportKey('jwk', key.publicKey),
iv: encodeArrayBuffer(iv),
payload: encodeArrayBuffer(payload),
timestamp: Date.now(),
version: 1,
}
}
/**
* Verify Steps:
* For each `packed` ({@link FriendshipCertificatePackedV1}):
* 1. Derive an AES key by `packed.cryptoKey` and your own key, let it be `aes`
* 2. Decrypt `packed.payload`, if failed, drop it; else, let it be `cert`
* 3. Manual or automatically verify friendship of `cert.myId` on network `cert.network`
*/
export async function unpackFriendshipCertificate(
packed: FriendshipCertificatePackedV1,
privateKey: CryptoKey,
): Promise<null | FriendshipCertificateV1> {
const ownPrivateKey = privateKey.usages.find(x => x === 'deriveKey') ? privateKey : await toECDH(privateKey)
const packedCryptoKey = await crypto.subtle.importKey(
'jwk',
packed.cryptoKey,
{ name: 'ECDH', namedCurve: 'K-256' },
false,
['deriveKey'],
)
const aes = await crypto.subtle.deriveKey(
{ name: 'ECDH', public: packedCryptoKey },
ownPrivateKey,
{ name: 'AES-GCM', length: 256 },
false,
['encrypt', 'decrypt'],
)
try {
const certBuffer = await decryptWithAES({ aesKey: aes, encrypted: packed.payload, iv: packed.iv })
const certString = decodeText(certBuffer)
const cert: FriendshipCertificateV1 = JSON.parse(certString)
if (!cert.myId || !cert.network) throw new TypeError('Not a valid cert.')
return cert
} catch {
return null
}
}
38 changes: 0 additions & 38 deletions src/protocols/friendship-discovery/friendship-sign.ts

This file was deleted.

0 comments on commit 73c915f

Please sign in to comment.