// From email with Scott Fluhrer: // To generate the SEED for a child LMS tree, we will adapt algorithm A by using the // I value of the parent LMS tree the q value to be the LMS index of the child, and the i value to be 65534. // Algorithm A: // x_q[i] = H(I || u32str(q) || u16str(i) || u8str(0xff) || SEED). // UPDATE: Generate child I from algorithm A using the I value of the parent LMS tree, the q value // to be the LMS index of the child, and the i value to be 65535; the I value will be the first 128 bits of the hash. public Hss(int layers, LmsType[] lmsTypes, LmotsType[] lmotsTypes, EntropyProviderTypes entropyType = EntropyProviderTypes.Random, BitString seed = null, BitString rootI = null) { _entropyType = entropyType; _entropyProvider = _entropyFactory.GetEntropyProvider(entropyType); _entropyProvider.AddEntropy(seed); SEED = _entropyProvider.GetEntropy(256); // for now will only be 256 bits (m = 256) _entropyProvider.AddEntropy(rootI); RootI = _entropyProvider.GetEntropy(128); _sha256 = new NativeFastSha2_256(); _lms = new Lms[layers]; _lmsTypes = lmsTypes; _lmotsTypes = lmotsTypes; _lms[0] = new Lms(lmsTypes[0], lmotsTypes[0], entropyType, SEED, rootI); var parentSeed = SEED; var parentI = RootI; for (int i = 1; i < layers; i++) { var childSeed = _sha256.HashMessage(parentI .ConcatenateBits(new BitString(0, 32)) .ConcatenateBits(new BitString(65534, 16)) .ConcatenateBits(new BitString("ff", 8)) .ConcatenateBits(parentSeed)).Digest; var I = _sha256.HashMessage(parentI .ConcatenateBits(new BitString(0, 32)) .ConcatenateBits(new BitString(65535, 16)) .ConcatenateBits(new BitString("ff", 8)) .ConcatenateBits(parentSeed)).Digest.MSBSubstring(0, 128); _lms[i] = new Lms(lmsTypes[i], lmotsTypes[i], entropyType, childSeed, I); parentSeed = childSeed; parentI = I; } }
/// <summary> /// Updates the key pair one step. Should be called my MCT in order to safely advance key. /// </summary> /// <param name="keyPair"></param> /// <returns></returns> public async Task <HssKeyPair> UpdateKeyPairOneStepAsync(HssKeyPair keyPair) { var keyPairCopy = keyPair.GetDeepCopy(); int d = _lms.Length; while (keyPairCopy.PrivateKey.PrivateKeys[d - 1].Q == (keyPairCopy.PrivateKey.PrivateKeys[d - 1].OTS_PRIV.Length / (_lms[d - 1].GetLmotsN() * _lms[d - 1].GetLmotsP() + 24)) - 1) { d--; if (d == 0) { keyPairCopy.Expired = true; return(keyPairCopy); } } var lowD = d; if (d < _lms.Length) { while (d < _lms.Length) { var newSeed = _sha256.HashMessage(_lms[d - 1].GetI() .ConcatenateBits(new BitString(keyPairCopy.PrivateKey.PrivateKeys[d - 1].Q + 1, 32)) .ConcatenateBits(new BitString(65534, 16)) .ConcatenateBits(new BitString("ff", 8)) .ConcatenateBits(_lms[d - 1].GetSeed())).Digest; var newI = _sha256.HashMessage(_lms[d - 1].GetI() .ConcatenateBits(new BitString(keyPairCopy.PrivateKey.PrivateKeys[d - 1].Q + 1, 32)) .ConcatenateBits(new BitString(65535, 16)) .ConcatenateBits(new BitString("ff", 8)) .ConcatenateBits(_lms[d - 1].GetSeed())).Digest; newI = newI.MSBSubstring(0, 128); _lms[d] = new Lms(_lmsTypes[d], _lmotsTypes[d], _entropyType, newSeed, newI); var newkeyPairCopy = await _lms[d].GenerateLmsKeyPairAsync(); keyPairCopy.PrivateKey.PrivateKeys[d] = newkeyPairCopy.PrivateKey; keyPairCopy.PrivateKey.PublicKeys[d] = newkeyPairCopy.PublicKey; keyPairCopy.PrivateKey.PrivateKeys[d - 1].Q = (keyPairCopy.PrivateKey.PrivateKeys[d - 1].Q + 1) % (1 << _lms[d - 1].GetH()); keyPairCopy.PrivateKey.Signatures[d - 1] = _lms[d - 1].GenerateLmsSignature( keyPairCopy.PrivateKey.PublicKeys[d], keyPairCopy.PrivateKey.PrivateKeys[d - 1]); if (keyPairCopy.PrivateKey.Signatures[d - 1] == null) { keyPairCopy.Expired = true; return(keyPairCopy); } d++; } } else { // Update Q value keyPairCopy.PrivateKey.PrivateKeys[d - 1].Q = (keyPairCopy.PrivateKey.PrivateKeys[d - 1].Q + 1) % (1 << _lms[d - 1].GetH()); } return(keyPairCopy); }
/// <summary> /// Used in signature generation to sign after advancing the key some number of times. /// This function is unsafe to use if the keyPair has been advanced before. /// </summary> /// <param name="keyPair"></param> /// <param name="times"></param> /// <returns></returns> private async Task <HssKeyPair> UpdateKeyPairAsync(HssKeyPair keyPair, int times = 1) { if (times == 0) { return(keyPair); } keyPair = keyPair.GetDeepCopy(); var divisor = 1; for (int i = 1; i < _lms.Length; i++) { divisor *= (1 << _lms[i].GetH()); } // If update would cause the key to expire, then expire key if (((1 << _lms[0].GetH()) - keyPair.PrivateKey.PrivateKeys[0].Q) * divisor <= times) { keyPair.Expired = true; return(keyPair); } for (int d = 0; d < _lms.Length; d++) { // Update divisor for next step if (d != 0) { divisor /= (1 << _lms[d].GetH()); } var qStep = times / divisor; // If tree update is needed if (qStep + keyPair.PrivateKey.PrivateKeys[d].Q >= (1 << _lms[d].GetH())) { // If update would cause the key to expire, then expire key if (d == 0) { keyPair.Expired = true; return(keyPair); } var newSeed = _sha256.HashMessage(_lms[d - 1].GetI() .ConcatenateBits(new BitString(keyPair.PrivateKey.PrivateKeys[d - 1].Q, 32)) .ConcatenateBits(new BitString(65534, 16)) .ConcatenateBits(new BitString("ff", 8)) .ConcatenateBits(_lms[d - 1].GetSeed())).Digest; var newI = _sha256.HashMessage(_lms[d - 1].GetI() .ConcatenateBits(new BitString(keyPair.PrivateKey.PrivateKeys[d - 1].Q, 32)) .ConcatenateBits(new BitString(65535, 16)) .ConcatenateBits(new BitString("ff", 8)) .ConcatenateBits(_lms[d - 1].GetSeed())).Digest; newI = newI.MSBSubstring(0, 128); _lms[d] = new Lms(_lmsTypes[d], _lmotsTypes[d], _entropyType, newSeed, newI); var newKeyPair = await _lms[d].GenerateLmsKeyPairAsync(); keyPair.PrivateKey.PrivateKeys[d] = newKeyPair.PrivateKey; keyPair.PrivateKey.PublicKeys[d] = newKeyPair.PublicKey; keyPair.PrivateKey.Signatures[d - 1] = _lms[d - 1].GenerateLmsSignature( keyPair.PrivateKey.PublicKeys[d], keyPair.PrivateKey.PrivateKeys[d - 1]); } // Update Q value keyPair.PrivateKey.PrivateKeys[d].Q = (keyPair.PrivateKey.PrivateKeys[d].Q + qStep) % (1 << _lms[d].GetH()); } return(keyPair); }