-
Notifications
You must be signed in to change notification settings - Fork 36
jellyfish-crypto: aes256
#331
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
fuxingloh
merged 11 commits into
main
from
feature/mnemonic-wallet/passphrase-encrypt-seed
Jun 11, 2021
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
555d36d
add an aes256 lib, required for scrypt later
3f85f54
remove duplicated test
d74cf26
minor jsdoc update, add ip ref
a7da375
remove direct native crypto pkg usage, use browserify libs
0e18502
allow custom iv for aes enc
e3fe183
remove invalid npm dev dep
8df8329
change iv arg from buffer to () => buffer
3c12bc1
few requested changes
b7f64a7
update comment
a515433
enhance test
f63f539
repeat test
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| import randomBytes from 'randombytes' | ||
| import { AES256 } from '../src' | ||
|
|
||
| describe('Aes256', () => { | ||
| const raw = 'e9873d79c6d87dc0fb6a5778633389f4e93213303da61f20bd67fc233aa33262' | ||
| const privateKey = Buffer.from(raw, 'hex') | ||
| const passphrase = Buffer.from('password', 'ascii') | ||
|
|
||
| let encrypted: Buffer | ||
|
|
||
| it('encrypt', () => { | ||
| encrypted = AES256.encrypt(passphrase, privateKey) | ||
| expect(encrypted.length).toStrictEqual(48) // [16 bytes salt, 32 bytes cipher] | ||
| }) | ||
|
|
||
| it('encrypt - should be able to overwrite cipher iv for better security measure', () => { | ||
| const iv = (): Buffer => Buffer.from('0102030405060708090a0b0c0d0e0f10', 'hex') | ||
| encrypted = AES256.encrypt(passphrase, privateKey, iv) | ||
| expect(encrypted.length).toStrictEqual(48) // [16 bytes salt, 32 bytes cipher] | ||
| }) | ||
|
|
||
| it('encrypt - should reject non 16 bytes long iv', () => { | ||
| const iv = (): Buffer => Buffer.from('0102030405060708090a0b0c0d0e0f', 'hex') | ||
| expect(() => AES256.encrypt(passphrase, privateKey, iv)).toThrow('Initialization vector must be 16 bytes long') | ||
| }) | ||
|
|
||
| it('decrypt - with valid passphrase', () => { | ||
| const decrypted = AES256.decrypt(passphrase, encrypted) | ||
| expect(decrypted.toString('hex')).toStrictEqual(raw) | ||
| }) | ||
|
|
||
| it('decrypt - with invalid passphrase', () => { | ||
| const invalid = AES256.decrypt(passphrase.slice(1), encrypted) | ||
| expect(invalid.length).toStrictEqual(32) | ||
| expect(invalid.toString('hex')).not.toStrictEqual(raw) | ||
| }) | ||
|
|
||
| it('decrypt - data too short, insufficient length to include salt', () => { | ||
| const invalidData = Buffer.alloc(16) | ||
| expect(() => { | ||
| AES256.decrypt(passphrase, invalidData) | ||
| }).toThrow('Provided "encrypted" must decrypt to a non-empty string or buffer') | ||
| }) | ||
| }) | ||
|
|
||
| it('Repeat 1000 times with random data - should be consistent', () => { | ||
| for (let i = 0; i < 1000; i++) { | ||
| const sampleData = randomBytes(1000) | ||
| const passphrase = randomBytes(60) | ||
|
|
||
| const encrypted = AES256.encrypt(passphrase, sampleData) | ||
| const decrypted = AES256.decrypt(passphrase, encrypted) | ||
|
|
||
| const encryptedAgain = AES256.encrypt(passphrase, decrypted) | ||
| const decryptedAgain = AES256.decrypt(passphrase, encryptedAgain) | ||
|
|
||
| // encrypted value are salted, it will be never the same | ||
| expect(encryptedAgain.toString('hex')).not.toStrictEqual(encrypted.toString('hex')) | ||
| // decrypted value are raw, recoverable using passphrase | ||
| expect(decryptedAgain.toString('hex')).toStrictEqual(decrypted.toString('hex')) | ||
| expect(decryptedAgain.toString('hex')).toStrictEqual(sampleData.toString('hex')) | ||
| } | ||
| }) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| import randomBytes from 'randombytes' | ||
| import aes from 'browserify-aes' | ||
| import { SHA256 } from './hash' | ||
|
|
||
| const CIPHER_ALGORITHM = 'aes-256-ctr' | ||
|
|
||
| /** | ||
| * Encrypt a clear-text message using AES-256 plus a random Initialization Vector. | ||
ivan-zynesis marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| * @see https://github.com/JamesMGreene/node-aes256 | ||
| * | ||
| * @param {Buffer} key A passphrase of any length to used to generate a symmetric session key. | ||
| * @param {Buffer} data The clear-text message or buffer to be encrypted. | ||
| * @param {(lengthOfBytes: number) => Buffer} rng Initialization vector generator, default using `crypto` or browserify `random-bytes` package | ||
| * @returns {Buffer} | ||
| */ | ||
| function encrypt (key: Buffer, data: Buffer, rng?: (lengthOfBytes: number) => Buffer): Buffer { | ||
| const sha256 = SHA256(key) | ||
| const initVector = rng === undefined ? randomBytes(16) : rng(16) | ||
thedoublejay marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| if (initVector.length !== 16) { | ||
| throw new Error('Initialization vector must be 16 bytes long') | ||
| } | ||
| const cipher = aes.createCipheriv(CIPHER_ALGORITHM, sha256, initVector) | ||
| const ciphertext = cipher.update(data) | ||
| return Buffer.concat([initVector, ciphertext, cipher.final()]) | ||
| } | ||
|
|
||
| /** | ||
| * Decrypt an encrypted message back to clear-text using AES-256 plus a random Initialization Vector. | ||
| * @see https://github.com/JamesMGreene/node-aes256 | ||
| * | ||
| * @param {Buffer} key A passphrase of any length to used to generate a symmetric session key. | ||
| * @param {Buffer} encrypted The encrypted message to be decrypted. | ||
| * @returns {Buffer} The original plain-text message or buffer. | ||
| */ | ||
| function decrypt (key: Buffer, encrypted: Buffer): Buffer { | ||
| if (encrypted.length < 17) { | ||
| throw new Error('Provided "encrypted" must decrypt to a non-empty string or buffer') | ||
| } | ||
|
|
||
| const sha256 = SHA256(key) | ||
| const initVector = encrypted.slice(0, 16) | ||
| const decipher = aes.createDecipheriv(CIPHER_ALGORITHM, sha256, initVector) | ||
|
|
||
| const ciphertext = encrypted.slice(16) | ||
| const deciphered = decipher.update(ciphertext) | ||
| const decipherFinal = decipher.final() | ||
| return Buffer.concat([deciphered, decipherFinal]) | ||
| } | ||
|
|
||
| export const AES256 = { | ||
| encrypt, | ||
| decrypt | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| /** | ||
| * No DefinitelyTyped declarations found, declaring our own here. | ||
| * For ./aes256 package to work without crypto. | ||
| * @warning this is bare minimum, not fully typed as native crypto package. | ||
| */ | ||
|
|
||
| declare module 'browserify-aes' { | ||
| interface Cipher { | ||
| update: (data: Buffer) => Buffer | ||
| final: () => Buffer | ||
| } | ||
| interface Decipher extends Cipher {} | ||
|
|
||
| export function createCipheriv (algorithm: string, password: Buffer, iv: Buffer): Cipher | ||
| export function createDecipheriv (algorithm: string, password: Buffer, iv: Buffer): Decipher | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| export * from './aes256' | ||
| export * from './bech32' | ||
| export * from './bs58' | ||
| export * from './der' | ||
|
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.