-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from caru-ini/feat/sign-in
ログイン機能の実装
- Loading branch information
Showing
16 changed files
with
365 additions
and
51 deletions.
There are no files selected for viewing
This file contains 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 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 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 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 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,20 +1,28 @@ | ||
import type { EntityId } from 'api/@types/brandedId'; | ||
import assert from 'assert'; | ||
import crypto from 'crypto'; | ||
import { g, N } from './srp/constants'; | ||
import { calculatePrivateKey, toBuffer } from './srp/util'; | ||
import { g, N, Nbytes } from './srp/constants'; | ||
import { calculatePrivateKey, getPoolName, toBufferWithLength } from './srp/util'; | ||
|
||
export const genVerifier = (params: { | ||
poolId: EntityId['userPool']; | ||
username: string; | ||
password: string; | ||
salt: string; | ||
}): string => { | ||
// extract pool name from poolId (poolId format: userPoolId_poolName) | ||
const poolName = getPoolName(params.poolId); | ||
const privateKey = calculatePrivateKey(poolName, params.username, params.password, params.salt); | ||
// verifier = g ^ privateKey % N | ||
const verifier = toBufferWithLength(g.modPow(privateKey, N), Nbytes).toString('hex'); | ||
return verifier; | ||
}; | ||
|
||
export const genCredentials = (params: { | ||
poolId: EntityId['userPool']; | ||
username: string; | ||
password: string; | ||
}): { salt: string; verifier: string } => { | ||
const salt = crypto.randomBytes(16).toString('hex'); | ||
// extract pool name from poolId (poolId format: userPoolId_poolName) | ||
const poolName = params.poolId.split('_')[1]; | ||
assert(poolName, 'Invalid poolId'); | ||
const privateKey = calculatePrivateKey(poolName, params.username, params.password, salt); | ||
// verifier = g^privateKey % N | ||
const verifier = toBuffer(g.modPow(privateKey, N)).toString('hex'); | ||
const verifier = genVerifier({ ...params, salt }); | ||
return { salt, verifier }; | ||
}; |
This file contains 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,51 @@ | ||
import assert from 'assert'; | ||
import { calculateScramblingParameter } from 'domain/user/service/srp/calcSessionKey'; | ||
import { calculateSignature } from 'domain/user/service/srp/calcSignature'; | ||
import { N, Nbytes, g, multiplierParam } from 'domain/user/service/srp/constants'; | ||
import { | ||
calculatePrivateKey, | ||
fromBuffer, | ||
getPoolName, | ||
padHex, | ||
toBufferWithLength, | ||
} from 'domain/user/service/srp/util'; | ||
import { BigInteger } from 'jsbn'; | ||
import { DEFAULT_USER_POOL_ID } from 'service/envValues'; | ||
|
||
export const calcClientSignature = (params: { | ||
A: string; | ||
a: BigInteger; | ||
B: string; | ||
username: string; | ||
password: string; | ||
salt: string; | ||
timestamp: string; | ||
secretBlock: string; | ||
}): string => { | ||
const poolname = getPoolName(DEFAULT_USER_POOL_ID); | ||
const Bint = new BigInteger(padHex(params.B), 16); | ||
|
||
assert(Bint.compareTo(BigInteger.ZERO) > 0, 'B should be greater than 0'); | ||
assert(Bint.compareTo(N) < 0, 'A should be less than N'); | ||
|
||
const privateKey = calculatePrivateKey(poolname, params.username, params.password, params.salt); | ||
|
||
const scramblingParameter = calculateScramblingParameter(params.A, params.B); | ||
|
||
const S = toBufferWithLength( | ||
// S = (B - k * (g ^ x)) ^ (a + u * x) | ||
Bint.subtract(multiplierParam.multiply(g.modPow(privateKey, N))) | ||
.modPow(params.a.add(fromBuffer(scramblingParameter).multiply(privateKey)), N) | ||
.mod(N), | ||
Nbytes, | ||
); | ||
|
||
return calculateSignature({ | ||
poolname, | ||
username: params.username, | ||
secretBlock: params.secretBlock, | ||
timestamp: params.timestamp, | ||
scramblingParameter, | ||
key: S, | ||
}); | ||
}; |
This file contains 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,39 @@ | ||
import assert from 'assert'; | ||
import crypto from 'crypto'; | ||
import { BigInteger } from 'jsbn'; | ||
import { N, Nbytes } from './constants'; | ||
import { fromBuffer, padHex, toBufferWithLength } from './util'; | ||
|
||
export const calculateScramblingParameter = (A: string, B: string): Buffer => { | ||
// H(A | B) | ||
const ABuffer = Buffer.from(padHex(A), 'hex'); | ||
const BBuffer = Buffer.from(padHex(B), 'hex'); | ||
const hash = crypto.createHash('sha256').update(ABuffer).update(BBuffer).digest(); | ||
return hash; | ||
}; | ||
|
||
export const calculateSessionKey = (params: { | ||
A: string; | ||
B: string; | ||
b: string; | ||
v: string; | ||
}): Buffer => { | ||
const Aint = new BigInteger(padHex(params.A), 16); | ||
const Bint = new BigInteger(padHex(params.B), 16); | ||
const bInt = new BigInteger(padHex(params.b), 16); | ||
const vInt = new BigInteger(params.v, 16); | ||
|
||
assert(Aint.compareTo(BigInteger.ZERO) > 0, 'A should be greater than 0'); | ||
assert(Aint.compareTo(N) < 0, 'A should be less than N'); | ||
assert(Bint.compareTo(BigInteger.ZERO) > 0, 'B should be greater than 0'); | ||
assert(Bint.compareTo(N) < 0, 'A should be less than N'); | ||
|
||
const scramblingParameter = calculateScramblingParameter(params.A, params.B); | ||
|
||
// u = H(A,B) % N | ||
const u = vInt.modPow(fromBuffer(scramblingParameter), N); | ||
// S = (B - k * g^x) ^ (a + u * x) % N | ||
const S = Aint.multiply(u).modPow(bInt, N).mod(N); | ||
|
||
return toBufferWithLength(S, Nbytes); | ||
}; |
This file contains 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,30 @@ | ||
import crypto from 'crypto'; | ||
import { infoBits } from './constants'; | ||
import { padBufferToHex } from './util'; | ||
|
||
export const calculateSignature = (params: { | ||
poolname: string; | ||
username: string; | ||
secretBlock: string; | ||
timestamp: string; | ||
scramblingParameter: Buffer; | ||
key: Buffer; | ||
}): string => { | ||
const secretBlockBuffer = Buffer.from(params.secretBlock, 'base64'); | ||
|
||
const prk = crypto | ||
.createHmac('sha256', Buffer.from(padBufferToHex(params.scramblingParameter), 'hex')) | ||
.update(Buffer.from(padBufferToHex(params.key), 'hex')) | ||
.digest(); | ||
|
||
const hmac = crypto.createHmac('sha256', prk).update(infoBits).digest(); | ||
const hkdf = hmac.subarray(0, 16); | ||
|
||
return crypto | ||
.createHmac('sha256', hkdf) | ||
.update(params.poolname) | ||
.update(params.username) | ||
.update(secretBlockBuffer) | ||
.update(params.timestamp) | ||
.digest('base64'); | ||
}; |
This file contains 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,18 @@ | ||
import crypto from 'crypto'; | ||
import { BigInteger } from 'jsbn'; | ||
import { N, g, multiplierParam } from './constants'; | ||
import { fromBuffer, toBuffer } from './util'; | ||
|
||
export const calculateSrpB = ( | ||
v: string, | ||
): { | ||
b: string; | ||
B: string; | ||
} => { | ||
const b = crypto.randomBytes(32); | ||
const bInt = fromBuffer(b); | ||
const vInt = new BigInteger(v, 16); | ||
// kv + g^b | ||
const B = toBuffer(multiplierParam.multiply(vInt).add(g.modPow(bInt, N)).mod(N)); | ||
return { b: b.toString('hex'), B: B.toString('hex') }; | ||
}; |
This file contains 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,9 +1,18 @@ | ||
import { BigInteger } from 'jsbn'; | ||
import { getHash } from './util'; | ||
|
||
const Nhex = `FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF`; | ||
export const N = new BigInteger(Nhex, 16); | ||
export const Nbits = N.bitLength(); | ||
export const Nbytes = Nbits / 8; | ||
export const g = new BigInteger('2'); | ||
export const infoBits = 'Caldera Derived Key\x01'; | ||
|
||
export const HASH_TYPE = 'sha256'; | ||
const getMultiplierParam = (): BigInteger => { | ||
const Nhex = `00${N.toString(16)}`; | ||
const ghex = `0${g.toString(16)}`; | ||
const hash = getHash(Buffer.from(Nhex + ghex, 'hex'), 32); | ||
return new BigInteger(hash, 16); | ||
}; | ||
|
||
export const multiplierParam = getMultiplierParam(); |
This file contains 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
Oops, something went wrong.